Compare commits

...

252 Commits

Author SHA1 Message Date
snipe 78d1256b74 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2025-12-12 08:47:16 +00:00
snipe a3f9aad418 Bumped version 2025-12-12 08:46:25 +00:00
snipe dba8cb83bf Merge remote-tracking branch 'origin/develop' 2025-12-12 07:49:53 +00:00
snipe 1954c607cd #18339 - removed heroku support 2025-12-12 07:49:01 +00:00
snipe 744124f407 Merge remote-tracking branch 'origin/develop' 2025-12-12 07:14:51 +00:00
snipe 3c14921a8c #18340 - jfc swift 2025-12-12 07:14:36 +00:00
snipe b595fe7488 Merge remote-tracking branch 'origin/develop' 2025-12-12 06:41:18 +00:00
snipe b0b194cef7 Fixed missing comma 2025-12-12 06:41:06 +00:00
snipe eb0a3a27d3 Merge remote-tracking branch 'origin/develop' 2025-12-12 05:03:09 +00:00
snipe 72fbcd72e0 Fix for fullscreen with dark/light 2025-12-12 05:02:56 +00:00
snipe 09e660a38c Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/mix-manifest.json
2025-12-12 04:26:35 +00:00
snipe add1810fcc Added more options and validation to suppliers “new” modal 2025-12-12 04:18:42 +00:00
snipe eead2ce93e Adds cursor pointer to checkboxes and radios and their labels 2025-12-12 03:51:20 +00:00
snipe 5e60d96614 Simpler footer link color 2025-12-11 20:15:57 +00:00
snipe 85c721da99 Better alert color on dark 2025-12-11 20:14:33 +00:00
snipe f3f09dd9a5 Upgraded jquery to 3.7.1 2025-12-11 20:12:23 +00:00
snipe 29ad804ca8 Merge pull request #18332 from Godmartinz/add-category-to-account-accept-table
Fixed #18316 - Adds #18316 category to pending acceptance index table
2025-12-11 19:12:15 +00:00
Godfrey M a8c77d6e26 update accessor to laravel 11.x standards 2025-12-11 11:07:36 -08:00
snipe b949380db8 Fixed highlighted button in license toolbar 2025-12-11 18:38:40 +00:00
Godfrey M b7f6137a63 add accessor for category name on checkout acceptance model 2025-12-11 10:08:00 -08:00
snipe 181cd7f0dc Merge remote-tracking branch 'origin/develop' 2025-12-11 18:03:12 +00:00
snipe 10692dc587 Removed back button on history importer, themed upload button 2025-12-11 17:49:56 +00:00
snipe 8d0793e004 Merge remote-tracking branch 'origin/develop' 2025-12-11 17:45:23 +00:00
snipe 02da163ee0 Fixed modal header color in light mode 2025-12-11 17:45:14 +00:00
snipe 3199e94b3c Made icons fixed width via fa-fw 2025-12-11 16:49:02 +00:00
snipe ac2a1503e2 Merge remote-tracking branch 'origin/develop' 2025-12-11 16:45:48 +00:00
snipe ea10167607 Merge pull request #18333 from Godmartinz/fix-checkin-notification
Fixes [FD-52267] Expected Checkin Notification Shows Overdue instead of Reminder
2025-12-11 16:44:45 +00:00
snipe e617b913cd Merge remote-tracking branch 'origin/develop' 2025-12-11 16:44:01 +00:00
snipe 8f6208a3c9 Removed text-blue in bootstrap-tables 2025-12-11 16:42:56 +00:00
snipe 39c71481c9 Added breaks 2025-12-11 16:34:55 +00:00
snipe a38e49290e Added external link indicator to help text 2025-12-11 16:31:12 +00:00
snipe f974427964 Merge remote-tracking branch 'origin/develop' 2025-12-11 16:05:41 +00:00
snipe 1f311c8657 One more nullsafe 2025-12-11 16:05:31 +00:00
snipe c0406734bc Merge remote-tracking branch 'origin/develop' 2025-12-11 16:01:15 +00:00
snipe 66e80628f6 Account for no created_by value 2025-12-11 16:01:06 +00:00
Godfrey M 620c43fd6d fixes expected checkin Notification 2025-12-10 11:33:08 -08:00
Godfrey M dfb9d5622a adds category to account accept index table 2025-12-10 10:43:59 -08:00
snipe af0aa7da4e Merge remote-tracking branch 'origin/develop' 2025-12-10 10:27:49 +00:00
snipe 75ddb50738 Use theme color for logo uploads 2025-12-10 10:27:40 +00:00
snipe 600238dd9b Merge remote-tracking branch 'origin/develop' 2025-12-10 09:38:51 +00:00
snipe 5a88e98ad9 Merge pull request #18330 from grokability/bigger-double-scrollbar-issue
Fixed #18317 - Longer double scrollbar issue
2025-12-10 09:38:37 +00:00
snipe 84a0544621 Removed <div class="table-responsive"> 2025-12-10 09:33:08 +00:00
snipe 8a1c7ee448 Remove <div class="table table-responsive"> 2025-12-10 09:09:12 +00:00
snipe 2fb29dad0a Merge remote-tracking branch 'origin/develop' 2025-12-10 00:09:17 +00:00
snipe 7d160abdaf Merge pull request #18323 from grokability/#18317-removed-responsive-table-divs
Fixed #18317 - Double scrollbars on some screens
2025-12-10 00:08:55 +00:00
snipe 6c5d2c6716 Merge pull request #18328 from grokability/#17197-download-importer-files
Added #17197: Ability to download uploaded import CSVs
2025-12-10 00:07:01 +00:00
snipe f3feff7988 Disable delete button if not owner of super admin 2025-12-09 23:59:22 +00:00
snipe 7d24f50cdc Removed extra whitespace 2025-12-09 23:49:21 +00:00
snipe 7c7375ed43 Undo temp delete commented out 2025-12-09 23:48:34 +00:00
snipe e2e4adca4e Generic message if the user tries to delete a file they don’t have access to 2025-12-09 23:46:53 +00:00
snipe a350b9bc3d Handle redirect if the user does not have permission to view results 2025-12-09 23:46:33 +00:00
snipe 7854543122 Added import-download controller 2025-12-09 23:46:13 +00:00
snipe 8b5636c0ab Override progress bar text color 2025-12-09 23:45:58 +00:00
snipe 9f948fd2ba Link to download and user 2025-12-09 23:45:49 +00:00
snipe 60fb67461a Added downnload route 2025-12-09 23:45:38 +00:00
snipe 5c896fc965 Merge pull request #18314 from Godmartinz/adjust-fonts-on-labelwriter-211xxx
Fixes [FD-52064] LabelWriter label font choice for fields, adds scaling to all labels.
2025-12-09 22:08:35 +00:00
snipe c779988771 Fixed gallery cards in dark mode 2025-12-09 16:53:02 +00:00
snipe e6eb15d053 Removed the duplicate table-responsive in BS tables 2025-12-09 16:52:51 +00:00
snipe 05b957df19 Merge pull request #18322 from grokability/bulk-checkin-delete-UI-fixes-dark-mode
Bulk checkin delete UI fixes dark mode
2025-12-09 15:38:25 +00:00
snipe 96da8a5fab Updated route 2025-12-09 15:28:22 +00:00
snipe 62bf61402e Updated strings 2025-12-09 15:24:16 +00:00
snipe 227be798f6 Visual tweaks to bulk 2025-12-09 15:24:10 +00:00
snipe 53f304d137 Updated view with tooltip on disabled 2025-12-09 15:23:59 +00:00
snipe 137d362369 Added breadcrumbs 2025-12-09 15:21:50 +00:00
snipe 5b2cf54f50 Fixed accessory buttons on mobile 2025-12-09 14:06:53 +00:00
snipe b4bc785f7c Merge remote-tracking branch 'origin/develop' 2025-12-09 13:13:59 +00:00
snipe 98a8e4c2ec Merge pull request #18319 from uberbrady/fix_component_edit
Fixed [RB-4066], #18308 - Fix error on saving component after create
2025-12-09 13:13:45 +00:00
Brady Wetherington bed6b04c3d Unset the 'sum_unconstrained_assets' attribute before saving 2025-12-09 13:00:41 +00:00
snipe babb3ffb9c Merge remote-tracking branch 'origin/develop' 2025-12-08 20:24:31 +00:00
snipe 15c96f753c Revert hasMany “fix” 2025-12-08 20:22:55 +00:00
snipe 354bdeffbf Merge remote-tracking branch 'origin/develop' 2025-12-08 20:19:54 +00:00
snipe 512af90d31 Re-removed non-asset models from kits
These still do not work as expected.
2025-12-08 20:18:39 +00:00
snipe ed837b7527 Merge remote-tracking branch 'origin/develop' 2025-12-08 19:33:07 +00:00
snipe fa5dd99f00 Only try to print status name if it exists 2025-12-08 19:32:57 +00:00
Godfrey M a19282710b fix up 11354 label, and readd 1d barcode to 2112283 2025-12-08 10:44:21 -08:00
Godfrey M 2f3cfb0a4e fix labelwriter fonts and add scaling 2025-12-08 10:20:53 -08:00
snipe af4db94d17 Merge pull request #18306 from fdiaz3000/change-import-assetmodel
Change title_class to ucfirst in Object Import Command to allow AssetModel
2025-12-08 17:05:23 +00:00
snipe bcbf27acca Merge remote-tracking branch 'origin/develop' 2025-12-08 16:51:44 +00:00
snipe 80b037c5a5 Fixed label url 2025-12-08 16:51:36 +00:00
snipe 20bacfeecf Merge remote-tracking branch 'origin/develop' 2025-12-07 21:40:19 +00:00
snipe 8a128ae8c2 Fixed RB-4062 - double-braces 2025-12-07 21:40:02 +00:00
snipe beacfbb082 Merge remote-tracking branch 'origin/develop' 2025-12-07 15:02:17 +00:00
snipe df0d565ae5 Set audit button back to btn-primary 2025-12-07 15:02:07 +00:00
snipe 9ee755c112 More consistent bulk action labels 2025-12-07 14:58:08 +00:00
snipe 130aca2943 Merge remote-tracking branch 'origin/develop' 2025-12-07 14:53:10 +00:00
snipe 5ea76ecb66 Fixed checkmark class on model buk edit 2025-12-07 14:52:49 +00:00
snipe b8ff3ef41a Merge remote-tracking branch 'origin/develop' 2025-12-07 14:49:44 +00:00
snipe 3e8156be54 Merge pull request #18307 from grokability/bulk-model-edit-dark-light-fix
Fixed unreadable table on dark mode for asset model bulk edit, added breadcrumbs
2025-12-07 14:49:31 +00:00
snipe 47e192b530 Fixed breadcrumb title 2025-12-07 14:43:47 +00:00
snipe b33f222fc0 Smaller bulk delete form 2025-12-07 14:39:00 +00:00
snipe 20eab1f403 Fixed updated route names 2025-12-07 14:37:22 +00:00
snipe a283fdb75a Removed back button 2025-12-07 14:37:10 +00:00
snipe a29a115846 Nicer layout on asset bulk delete form 2025-12-07 14:36:59 +00:00
snipe 05ff9183fb Fixed weird top border 2025-12-07 14:31:47 +00:00
snipe 793d299c1d Fixed breadcrumbs 2025-12-07 14:31:01 +00:00
snipe 7d5f862f34 Use striped mode 2025-12-07 14:13:17 +00:00
snipe b0ab900a0f Fixed new dark mode for bulk model edit 2025-12-07 14:10:09 +00:00
Felix 0ea5012ba2 fix(import-command): 18305 change title_class to ucfirst 2025-12-06 15:00:43 -05:00
snipe 7ecb96d45a Merge remote-tracking branch 'origin/develop' 2025-12-06 01:08:46 +00:00
snipe 5f0d7fde39 Better selected color 2025-12-06 01:08:31 +00:00
snipe fe3c301ca2 Prod assets 2025-12-06 00:59:55 +00:00
snipe 3adf8847b0 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-12-06 00:59:47 +00:00
snipe 9ae68f0000 More color tweaks 2025-12-06 00:58:32 +00:00
snipe 36415a1f7a Updated buttons 2025-12-05 23:43:56 +00:00
snipe 39d1aa932c Moved clear button 2025-12-05 21:35:47 +00:00
snipe 82b37c3b58 Removed back button 2025-12-05 21:25:17 +00:00
snipe c73c2b003a Merge remote-tracking branch 'origin/develop' 2025-12-05 20:52:48 +00:00
snipe 65b39d3a30 Use the themed buttons 2025-12-05 20:51:47 +00:00
snipe 6a59119c58 Merge pull request #18303 from grokability/update-users-api-with-disallowed-fields-list
Update users api with disallowed fields list
2025-12-05 20:32:55 +00:00
snipe 8b4387ec32 Fixed weird indenting 2025-12-05 19:52:31 +00:00
snipe dc82f8f077 Updated tests 2025-12-05 19:27:05 +00:00
snipe 07e1f67e13 Disallow fill for sensitive fields 2025-12-05 19:27:01 +00:00
snipe 412f4c65c8 Fixed dark mode footer links 2025-12-05 18:35:18 +00:00
snipe a6d9c1f882 Merge remote-tracking branch 'origin/develop' 2025-12-05 18:07:13 +00:00
snipe bb5c142f52 Nicer footer color for light/dark 2025-12-05 18:06:48 +00:00
snipe a5e1528c0d Merge remote-tracking branch 'origin/develop' 2025-12-05 17:46:30 +00:00
snipe 904c20e879 Removed blue text override 2025-12-05 17:46:20 +00:00
snipe 612daa6824 Merge pull request #18302 from grokability/preflight-quickstart-cleanup
Preflight quickstart cleanup
2025-12-05 17:20:29 +00:00
snipe 02b6de2385 Added tests 2025-12-05 17:03:10 +00:00
snipe da5db1920e Use generic email address domains 2025-12-05 17:03:02 +00:00
snipe d20545741e Nicer views 2025-12-05 17:02:51 +00:00
snipe 03b42d2c6c Nicer styles 2025-12-05 17:02:19 +00:00
snipe e9dbeebbc4 Updated some strings 2025-12-05 17:02:09 +00:00
snipe bb53fa245b Removed email domain from required setup fields 2025-12-05 17:01:36 +00:00
snipe bc796498a3 Moved setup controller methods out of settings controller 2025-12-05 17:01:24 +00:00
snipe c25266054b Merge remote-tracking branch 'origin/develop' 2025-12-05 10:55:59 +00:00
snipe 0204414196 Handle /setup link colors via middleware 2025-12-05 10:55:49 +00:00
snipe c6b2017494 Merge pull request #18297 from Godmartinz/tze_24mm_E_2d-adjustment-and-scaling
Fixes [FD-50838] adjust `Tze_24mm_E` 2D barcode 20% bigger, scales fields, center label more
2025-12-05 08:57:20 +00:00
snipe 84fd48602e Merge pull request #18298 from Godmartinz/add-TZe_241
Adds [FD-52267] TZe_241 based on TZe_18mm sizes
2025-12-05 08:56:55 +00:00
Godfrey M c8e8eb58aa adds TZe_241 based on TZe_18mm sizes 2025-12-04 12:41:57 -08:00
Godfrey M 0b087ca77d adjust 2d barcode 20% bigger, scale fields, center label more 2025-12-04 09:55:47 -08:00
snipe 444083ec5d Merge remote-tracking branch 'origin/develop' 2025-12-04 17:54:31 +00:00
snipe bf01a11fec Fixed label colors in dark/light 2025-12-04 17:54:19 +00:00
snipe 8f232421d2 Merge remote-tracking branch 'origin/develop' 2025-12-04 17:37:58 +00:00
snipe dbc688ad6e Set '#ffffff' as default 2025-12-04 17:37:44 +00:00
snipe 6217a721ac Merge remote-tracking branch 'origin/develop' 2025-12-04 15:11:38 +00:00
snipe c2ba937ac6 Merge pull request #18295 from grokability/#18288-allow-reference-editing-if-edit-profile-is-disabled
Fixed #18288: Allow users to edit display preferences even if profile editing is not enabled
2025-12-04 15:11:11 +00:00
snipe d860786221 Re-add button 2025-12-04 13:21:54 +00:00
snipe 621ce1777f Fixed #18288: Allow users to change preferences even if profile editing is not permitted 2025-12-04 13:21:04 +00:00
snipe 4f610ac1af Added tag color to importer 2025-12-04 11:07:59 +00:00
snipe 7341cd1712 Merge pull request #18294 from grokability/#18278-import-company-in-locations
Fixed #18278: Import companies into locations on initial import
2025-12-04 11:07:18 +00:00
snipe bf112b7b4b Fixed #18278: Import companies into locations on initial import 2025-12-04 10:59:45 +00:00
snipe ad9e0cc39a Merge remote-tracking branch 'origin/develop' 2025-12-04 10:50:09 +00:00
snipe 1439681113 Alert message link text 2025-12-04 10:34:58 +00:00
snipe 3b750541c9 Prod assets 2025-12-04 10:19:12 +00:00
snipe 79765201ac Merge remote-tracking branch 'origin/develop' 2025-12-04 10:18:37 +00:00
snipe 0086b9d848 mix 2025-12-04 10:17:17 +00:00
snipe c8ddb44783 Fixed button color 2025-12-04 10:16:44 +00:00
snipe d4829a4bac Fixed #18286 - :user in declined email 2025-12-04 10:16:35 +00:00
snipe 486f0c0035 Re-running assets for prod 2025-12-04 08:46:53 +00:00
snipe dc3a695ab0 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/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-black-dark.css
#	public/css/dist/skins/skin-black-dark.css.map
#	public/css/dist/skins/skin-black-dark.min.css
#	public/css/dist/skins/skin-black.css
#	public/css/dist/skins/skin-black.css.map
#	public/css/dist/skins/skin-black.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.css.map
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-blue.css
#	public/css/dist/skins/skin-blue.css.map
#	public/css/dist/skins/skin-blue.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/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.css.map
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-green.css
#	public/css/dist/skins/skin-green.css.map
#	public/css/dist/skins/skin-green.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.css.map
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-orange.css
#	public/css/dist/skins/skin-orange.css.map
#	public/css/dist/skins/skin-orange.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.css.map
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-purple.css
#	public/css/dist/skins/skin-purple.css.map
#	public/css/dist/skins/skin-purple.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.css.map
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-red.css
#	public/css/dist/skins/skin-red.css.map
#	public/css/dist/skins/skin-red.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.css.map
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/css/dist/skins/skin-yellow.css
#	public/css/dist/skins/skin-yellow.css.map
#	public/css/dist/skins/skin-yellow.min.css
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/mix-manifest.json
2025-12-04 08:45:19 +00:00
snipe 3feee682b6 Fixed import view with light/dark 2025-12-03 21:38:57 +00:00
snipe 4ea5cb9538 Merge pull request #18284 from Godmartinz/fix-alert-threshold-bug
Fixes Low Inventory Alerts not sending when threshold is `null`
2025-12-03 21:32:29 +00:00
Godfrey M f6461a755a revert again 2025-12-03 13:22:46 -08:00
Godfrey M 39cf5ce66e revert some changes 2025-12-03 13:22:03 -08:00
Godfrey M c68d9892b5 fix threshold bug, include minimum to available 2025-12-03 13:20:01 -08:00
snipe 33b20b6268 Fixed sidemenu hover 2025-12-03 19:27:44 +00:00
snipe 280df20a0b Better default link colors for skin migration 2025-12-03 18:18:24 +00:00
snipe 7027cd80d4 Updated modal for light/dark 2025-12-03 18:18:14 +00:00
snipe bfbcfe7bae Updated text string 2025-12-03 17:36:48 +00:00
snipe e1f64b6d2b Modal title color fix 2025-12-03 17:36:39 +00:00
snipe 6944c438dd Inline tooltip improvements 2025-12-03 16:04:23 +00:00
snipe 49b7ff1192 Fixed email field color 2025-12-03 15:58:50 +00:00
snipe 662cdbaa0e Datepicker color fixes 2025-12-03 15:55:04 +00:00
snipe e912eb5ef8 Merge pull request #18279 from uberbrady/new_allowlist_restore_cleaner
Loosen regex allowlist for setting character sets
2025-12-03 13:31:01 +00:00
Brady Wetherington 5ab68d83a5 Loosen regex allowlist for setting character sets 2025-12-03 13:06:02 +00:00
snipe f7c432f7fd Bumped hash 2025-12-03 10:09:23 +00:00
snipe 592ccb6ebe Merge pull request #18244 from Godmartinz/receive-all-expired-licenses-on-report
adds #17422 [FD-49345] `--expired-licenses` command parameter to `snipeit:expiring-alerts`
2025-12-03 09:49:49 +00:00
snipe d22d70dd92 Merge pull request #18257 from iryadifarhan/fix/manager-view-not-displaying-subordinates-eulas-properly
Fixes Manager View not displaying subordinates EULAs properly in View Assets page
2025-12-03 09:49:35 +00:00
snipe 7ec5606ce4 Merge pull request #18247 from uberbrady/multi_create_fixes
Fixed #18160 - Multi-create fixes
2025-12-03 09:49:14 +00:00
snipe 476bf95edf Merge pull request #18272 from spencerrlongg/add-null-checks
Adds Null Checks
2025-12-03 09:48:43 +00:00
snipe cdf036ed7b Merge pull request #18274 from spencerrlongg/check-that-email-exists-on-recipient
Check That Email Exists on Recipient in Checkout Acceptance
2025-12-02 19:35:28 +00:00
snipe 639a3b9295 Merge pull request #18273 from Godmartinz/fix-null-assignee-bug-in-checkoutable
Refactor `assignee` in Checkoutable to accept null
2025-12-02 19:31:22 +00:00
spencerrlongg e4f8c3bef7 add null check and check for email 2025-12-02 13:17:13 -06:00
Godfrey M 462945022c allow null for assignee in checkoutable 2025-12-02 11:05:07 -08:00
spencerrlongg aa57687df0 add null checks to license 2025-12-02 12:13:38 -06:00
snipe 3237a3b9de One more button fix 2025-12-02 16:44:31 +00:00
snipe ff30e109cc Small button fixes 2025-12-02 16:42:32 +00:00
snipe 2d291f843a Fixed create new on table buttons 2025-12-02 16:36:54 +00:00
snipe 7219fc1c3c Added style to border on colorpicker 2025-12-02 16:22:53 +00:00
snipe ed6bfa7810 Disable branding colopickers on demos 2025-12-02 16:16:33 +00:00
snipe ad15090c34 Nicer search highlight box 2025-12-02 15:58:34 +00:00
snipe 9d34bf4a19 Fixed table header colors 2025-12-02 15:54:18 +00:00
snipe 7c9b1a52af Swapped link colors 2025-12-02 15:39:32 +00:00
snipe dd297dca31 Merge pull request #18249 from grokability/proper-dark-toggle
Experiment: simpler light/dark toggle
2025-12-02 14:37:52 +00:00
snipe 1409d01078 Tweaked color 2025-12-02 14:19:53 +00:00
snipe c9a03cf9b7 Final color tweaks 2025-12-02 14:03:08 +00:00
snipe 6a99132e76 More tweaks 2025-12-02 13:29:12 +00:00
snipe 2e269d2e63 Removed old skin less files 2025-11-29 10:42:10 +00:00
snipe 7820636c9f Nicer defaults 2025-11-29 10:41:58 +00:00
snipe db1b35ccf6 Fixed style 2025-11-28 19:20:41 +00:00
snipe fadfe0a782 Removed old skin references 2025-11-28 19:17:52 +00:00
snipe 255a2ecdd9 Sigh 2025-11-28 19:09:38 +00:00
snipe 97ffe33fc8 Check that a setting record exists 2025-11-28 19:07:46 +00:00
snipe 56d97a1f59 Updated map 2025-11-28 19:05:25 +00:00
snipe 28d5d24617 Migration to handle skins 2025-11-28 19:05:19 +00:00
snipe d97f6903d6 Save settings controller 2025-11-28 19:05:03 +00:00
snipe 3bf84d96d9 Update language 2025-11-28 19:04:49 +00:00
snipe 8df643a2ab Removed user skin option 2025-11-28 19:04:40 +00:00
snipe 2d001c4fa1 Added colorpickers for link colors 2025-11-28 19:04:20 +00:00
snipe cbd6b57445 Removed skin from user profile update 2025-11-28 19:04:00 +00:00
snipe dac684c08a Update demo settings 2025-11-28 19:03:48 +00:00
snipe 772c29791a Use css variable 2025-11-28 17:48:08 +00:00
snipe 89a232ae14 Merge pull request #18266 from Valinwolf/develop
Added endpoint & use_path_style_endpoint configs for public/private S3
2025-11-28 17:39:23 +00:00
snipe 4e4b8ddb77 And more updates 2025-11-28 17:33:30 +00:00
Patrick Thomas 6eaefa0bdd Added endpoint & use_path_style_endpoint configs for public/private S3 2025-11-28 17:29:02 +00:00
snipe 20a75bbbb7 More styles 2025-11-28 15:54:24 +00:00
snipe 5cc261dd3c Smaller LDAP screen 2025-11-28 15:50:37 +00:00
snipe 6d958b6f65 Added fa-fw to arrow class 2025-11-28 15:50:25 +00:00
snipe 8ddac4d7c7 More select2 styling :( 2025-11-28 15:16:22 +00:00
snipe a321ad9dbe Handle select2 stuff 2025-11-28 14:13:19 +00:00
snipe 4dff66253c Added contrast-color to dynamically pick white/black for topnav 2025-11-27 16:15:35 +00:00
snipe 9a1e9f90bc Better preview for header color, updated text 2025-11-27 15:56:06 +00:00
snipe c54724919c Show header color preview 2025-11-27 15:29:24 +00:00
snipe 139d1cdcf8 Added a few more classes 2025-11-27 14:48:28 +00:00
snipe 490c50a182 Added fa-fw to action buttons 2025-11-27 14:48:16 +00:00
snipe af1e496eab Added correct box class 2025-11-27 13:30:11 +00:00
snipe efea043549 Added dark/light text 2025-11-27 13:29:59 +00:00
snipe d4ee91f013 Removed a few classes 2025-11-27 13:29:50 +00:00
iryadifarhan d4561581ad Apply fix around view-assets to pass request parameter and profile controller to address request parameter 2025-11-27 13:42:47 +07:00
snipe a17f167952 Fix button overrides 2025-11-26 15:41:38 +00:00
snipe 5beb068cde More updates for dark and light switches 2025-11-26 15:35:43 +00:00
snipe a272bdc796 Merge pull request #18251 from uberbrady/improve_component_asset_counts
Optimize queries for Components listing
2025-11-26 14:40:14 +00:00
snipe 30a43089a0 More variablization 2025-11-26 13:50:39 +00:00
Brady Wetherington 416b32cbc8 Optimize queries for Components listing 2025-11-26 12:36:44 +00:00
snipe d203cece0e Removed indicidual skins 2025-11-26 04:08:31 +00:00
snipe 9f6f0f04c7 Few more tweaks 2025-11-26 04:06:20 +00:00
snipe a974c6d4cd Moved icon-med 2025-11-26 02:56:05 +00:00
snipe 34612acdcf Experiment: light/dark simplifcation 2025-11-26 02:49:40 +00:00
snipe 9e23117f9c Merge remote-tracking branch 'origin/develop' 2025-11-26 00:35:26 +00:00
snipe b3996f1970 In the sea, @uberbrady! (fixed missing semicolon) 2025-11-26 00:35:16 +00:00
snipe e143017432 Tinkering with CSS/JS dark more 2025-11-26 00:34:46 +00:00
Brady Wetherington c6c0a14ee0 Whoops, used PHP's equal signs instead of MySQL's :/ 2025-11-25 20:23:15 +00:00
Brady Wetherington 9b8768dbdd Tighten everything up, remove logging, fix last bits of functionality 2025-11-25 19:34:10 +00:00
snipe a3bfcc962d Merge remote-tracking branch 'origin/develop' 2025-11-25 18:50:23 +00:00
snipe ca4ed605a8 Merge pull request #18246 from Godmartinz/resize-label-fields-conditionally-L6009_A
Fixes [FD-52064] Avery `L6009_A` & `L4736_A` label field overflow with scaling
2025-11-25 18:37:06 +00:00
Godfrey M d3e6d7442f typo bonanza 2025-11-25 10:30:56 -08:00
Godfrey M b558bc5334 change variable" 2025-11-25 10:30:06 -08:00
Godfrey M 204d7b5be6 added scaling to L4736_A 2025-11-25 10:23:08 -08:00
Godfrey M 7dccfec332 adds notes 2025-11-25 10:12:27 -08:00
snipe 0b694bfd0b Merge remote-tracking branch 'origin/develop' 2025-11-25 18:02:23 +00:00
snipe dfb59d8a55 Added link to rudder2snipe repo 2025-11-25 18:01:29 +00:00
Brady Wetherington 3cd191210c Merge branch 'develop' into multi_create_fixes 2025-11-25 13:58:56 +00:00
snipe 56a44ad421 Merge remote-tracking branch 'origin/develop' 2025-11-25 13:38:54 +00:00
snipe a12ee3c0da Merge pull request #18245 from uberbrady/redirect_upgrader
Add new 'git remote' management to change Snipe-IT URLs
2025-11-25 13:38:31 +00:00
Brady Wetherington a657c479be Add new 'git remote' management to change Snipe-IT URL's 2025-11-25 13:17:56 +00:00
Godfrey M bb7dabc73b adds expired-licenses command parameter 2025-11-24 11:41:50 -08:00
Godfrey M ab82c5fd88 resizes label field box to size if needed 2025-11-24 11:04:23 -08:00
snipe 78cfb19f69 Merge remote-tracking branch 'origin/develop' 2025-11-24 17:40:26 +00:00
snipe f2334082ee Added tag color to location query 2025-11-24 17:40:13 +00:00
Brady Wetherington 70d79c1890 Merge branch 'develop' into multi_create_fixes 2025-11-11 18:20:49 +00:00
Brady Wetherington fb1fde26ce Use a FormRequest to better handle multiple-asset-creation by GUI 2025-11-11 18:00:22 +00:00
262 changed files with 3844 additions and 14691 deletions
+4
View File
@@ -137,6 +137,8 @@ PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
@@ -147,6 +149,8 @@ PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
+4
View File
@@ -144,6 +144,8 @@ PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
@@ -154,6 +156,8 @@ PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
+4
View File
@@ -143,6 +143,8 @@ PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
@@ -153,6 +155,8 @@ PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
+1
View File
@@ -83,6 +83,7 @@ Since the release of the JSON REST API, several third-party developers have been
- [jamf2snipe](https://github.com/grokability/jamf2snipe) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
- [jamf-snipe-rename](https://macblog.org/jamf-snipe-rename/) - Python script to rename computers in Jamf from Snipe-IT
- [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
- [Rudder2Snipe](https://github.com/norbertoaquino/rudder2snipe) by [@norbertoaquino](https://github.com/norbertoaquino) - Rudder.io integration for Snipe-IT
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-IT.
+1 -1
View File
@@ -56,7 +56,7 @@ class ObjectImportCommand extends Command
$this->progressIndicator = new ProgressIndicator($this->output);
$filename = $this->argument('filename');
$class = title_case($this->option('item-type'));
$class = ucfirst($this->option('item-type'));
$classString = "App\\Importer\\{$class}Importer";
$importer = new $classString($filename);
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
+5 -2
View File
@@ -49,14 +49,15 @@ class ResetDemoSettings extends Command
$settings->logo = 'snipe-logo.png';
$settings->alert_email = 'service@snipe-it.io';
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
$settings->header_color = null;
$settings->header_color = '#3c8dbc';
$settings->link_dark_color = '#86cbf2';
$settings->link_light_color = '#084d73;';
$settings->label2_2d_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 2;
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 0;
$settings->label2_1d_type = 'C128';
$settings->skin = '';
$settings->email_domain = 'snipeitapp.com';
$settings->email_format = 'filastname';
$settings->username_format = 'filastname';
@@ -80,6 +81,8 @@ class ResetDemoSettings extends Command
if ($user = User::where('username', '=', 'admin')->first()) {
$user->locale = 'en-US';
$user->enable_confetti = 1;
$user->enable_sounds = 1;
$user->save();
}
+1 -1
View File
@@ -52,7 +52,7 @@ class SQLStreamer {
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
"<^/\*!40101 SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, //using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
"<^/\*![0-9]{5} SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, //using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
"<^/\*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' \*/;$>" => false, //same, now handle zero-values
];
@@ -14,11 +14,11 @@ use Illuminate\Support\Facades\Mail;
class SendExpirationAlerts extends Command
{
/**
* The console command name.
*
* The name and signature of the console command.
*
* @var string
*/
protected $name = 'snipeit:expiring-alerts';
protected $signature = 'snipeit:expiring-alerts {--expired-licenses}';
/**
* The console command description.
@@ -85,7 +85,7 @@ class SendExpirationAlerts extends Command
}
// Expiring licenses
$licenses = License::query()->ExpiringLicenses($alert_interval)
$licenses = License::query()->ExpiringLicenses($alert_interval, $this->option('expired-licenses'))
->with('manufacturer','category')
->orderBy('expiration_date', 'ASC')
->orderBy('termination_date', 'ASC')
+5 -5
View File
@@ -784,7 +784,7 @@ class Helper
foreach ($consumables as $consumable) {
$avail = $consumable->numRemaining();
if ($avail < ($consumable->min_amt) + $alert_threshold) {
if ($avail <= ($consumable->min_amt) + $alert_threshold) {
if ($consumable->qty > 0) {
$percent = number_format((($avail / $consumable->qty) * 100), 0);
} else {
@@ -803,7 +803,7 @@ class Helper
foreach ($accessories as $accessory) {
$avail = $accessory->qty - $accessory->checkouts_count;
if ($avail < ($accessory->min_amt) + $alert_threshold) {
if ($avail <= ($accessory->min_amt) + $alert_threshold) {
if ($accessory->qty > 0) {
$percent = number_format((($avail / $accessory->qty) * 100), 0);
} else {
@@ -822,7 +822,7 @@ class Helper
foreach ($components as $component) {
$avail = $component->numRemaining();
if ($avail < ($component->min_amt) + $alert_threshold) {
if ($avail <= ($component->min_amt) + $alert_threshold) {
if ($component->qty > 0) {
$percent = number_format((($avail / $component->qty) * 100), 0);
} else {
@@ -845,7 +845,7 @@ class Helper
$total_owned = $asset->where('model_id', '=', $asset_model->id)->count();
$avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count();
if ($avail < ($asset_model->min_amt) + $alert_threshold) {
if ($avail <= ($asset_model->min_amt) + $alert_threshold) {
if ($avail > 0) {
$percent = number_format((($avail / $total_owned) * 100), 0);
} else {
@@ -863,7 +863,7 @@ class Helper
foreach ($licenses as $license){
$avail = $license->remaincount();
if ($avail < ($license->min_amt) + $alert_threshold) {
if ($avail <= ($license->min_amt) + $alert_threshold) {
if ($avail > 0) {
$percent = number_format((($avail / $license->min_amt) * 100), 0);
} else {
@@ -216,7 +216,7 @@ class AcceptanceController extends Controller
try {
$recipient = User::find($acceptance->alert_on_response_id);
if ($recipient) {
if ($recipient?->email) {
Log::debug('Attempting to send email acceptance.');
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
$acceptance,
@@ -58,8 +58,8 @@ class ComponentsController extends Controller
];
$components = Component::select('components.*')
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer', 'uncontrainedAssets')
->withSum('uncontrainedAssets', 'components_assets.assigned_qty');
->with('company', 'location', 'category', 'supplier', 'adminuser', 'manufacturer')
->withSum('uncontrainedAssets as sum_unconstrained_assets', 'components_assets.assigned_qty');
$filter = [];
@@ -112,7 +112,8 @@ class ComponentsController extends Controller
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $components->count()) ? $components->count() : app('api_offset_value');
$components_count = $components->count();
$offset = ($request->input('offset') > $components_count) ? $components_count : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@@ -143,7 +144,7 @@ class ComponentsController extends Controller
break;
}
$total = $components->count();
$total = $components_count;
$components = $components->skip($offset)->take($limit)->get();
return (new ComponentsTransformer)->transformComponents($components, $total);
+30 -3
View File
@@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\JsonEncodingException;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use League\Csv\Reader;
use Onnov\DetectEncoding\EncodingDetector;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
@@ -149,7 +150,9 @@ class ImportController extends Controller
}
$date = date('Y-m-d-his');
$fixed_filename = str_slug($file->getClientOriginalName());
$fixed_filename = Str::of($file->getClientOriginalName())->basename('.csv').'.csv';
try {
$file->move($path, $date.'-'.$fixed_filename);
} catch (FileException $exception) {
@@ -211,36 +214,47 @@ class ImportController extends Controller
$redirectTo = 'hardware.index';
switch ($request->get('import-type')) {
case 'asset':
$model_perms = 'App\Models\Asset';
$redirectTo = 'hardware.index';
break;
case 'assetModel':
$model_perms = 'App\Models\AssetModel';
$redirectTo = 'models.index';
break;
case 'accessory':
$model_perms = 'App\Models\Accessory';
$redirectTo = 'accessories.index';
break;
case 'consumable':
$model_perms = 'App\Models\Consumable';
$redirectTo = 'consumables.index';
break;
case 'component':
$model_perms = 'App\Models\Component';
$redirectTo = 'components.index';
break;
case 'license':
$model_perms = 'App\Models\License';
$redirectTo = 'licenses.index';
break;
case 'user':
$model_perms = 'App\Models\User';
$redirectTo = 'users.index';
break;
case 'location':
$model_perms = 'App\Models\Location';
$redirectTo = 'locations.index';
break;
case 'supplier':
$model_perms = 'App\Models\Supplier';
$redirectTo = 'suppliers.index';
break;
case 'manufacturer':
$model_perms = 'App\Models\Manufacturer';
$redirectTo = 'manufacturers.index';
break;
case 'category':
$model_perms = 'App\Models\Category';
$redirectTo = 'categories.index';
break;
}
@@ -251,7 +265,11 @@ class ImportController extends Controller
//Flash message before the redirect
Session::flash('success', trans('admin/hardware/message.import.success'));
return response()->json(Helper::formatStandardApiResponse('success', null, ['redirect_url' => route($redirectTo)]));
if (auth()->user()->can('view', $model_perms)) {
return response()->json(Helper::formatStandardApiResponse('success', null, ['redirect_url' => route($redirectTo)]));
}
return response()->json(Helper::formatStandardApiResponse('success', null, ['redirect_url' => route('imports.index')]));
}
/**
@@ -261,9 +279,16 @@ class ImportController extends Controller
*/
public function destroy($import_id) : JsonResponse
{
$this->authorize('create', Asset::class);
$this->authorize('import');
if ($import = Import::find($import_id)) {
if ((auth()->user()->id != $import->created_by) && (!auth()->user()->isSuperUser())) {
return response()->json(Helper::formatStandardApiResponse('warning', null, trans('admin/hardware/message.import.file_not_deleted_warning')));
}
try {
// Try to delete the file
Storage::delete('imports/'.$import->file_path);
@@ -280,4 +305,6 @@ class ImportController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('warning', null, trans('admin/hardware/message.import.file_not_deleted_warning')));
}
}
@@ -242,6 +242,7 @@ class LocationsController extends Controller
'locations.currency',
'locations.company_id',
'locations.notes',
'locations.tag_color',
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
@@ -78,10 +78,16 @@ class ManufacturersController extends Controller
$manufacturers->onlyTrashed();
}
if ($request->input('status') == 'deleted') {
$manufacturers->onlyTrashed();
}
if ($request->filled('search')) {
$manufacturers = $manufacturers->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$manufacturers->where('name', '=', $request->input('name'));
}
+11 -3
View File
@@ -14,6 +14,7 @@ use Laravel\Passport\TokenRepository;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Support\Facades\Gate;
use App\Models\CustomField;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@@ -179,10 +180,17 @@ class ProfileController extends Controller
*@since [v8.1.16]
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
*/
public function eulas(ProfileTransformer $transformer)
public function eulas(ProfileTransformer $transformer, Request $request)
{
// Only return this user's EULAs
$eulas = auth()->user()->eulas;
if($request->filled('user_id') && $request->input('user_id') != 0) {
// Return selected user's EULAs
$eulas = User::find($request->input('user_id'))->eulas;
}
else {
// Only return this user's EULAs
$eulas = auth()->user()->eulas;
}
return response()->json(
$transformer->transformFiles($eulas, $eulas->count())
);
+78 -72
View File
@@ -522,93 +522,99 @@ class UsersController extends Controller
{
$this->authorize('update', User::class);
$this->authorize('update', $user);
$this->authorize('update', $user);
/**
* This is a janky hack to prevent people from changing admin demo user data on the public demo.
* The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
* Thanks, jerks. You are why we can't have nice things. - snipe
*
*/
/**
* This is a janky hack to prevent people from changing admin demo user data on the public demo.
* The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
* Thanks, jerks. You are why we can't have nice things. - snipe
*
*/
if ((($user->id == 1) || ($user->id == 2)) && (config('app.lock_passwords'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.'));
return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.'));
}
// Pull out sensitive fields that require extra permission
$user->fill($request->except(['password', 'username', 'email', 'activated', 'permissions', 'activation_code', 'remember_token', 'two_factor_secret', 'two_factor_enrolled', 'two_factor_optin']));
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
$user->fill($request->all());
if ($request->filled('company_id')) {
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
if ($request->filled('username')) {
$user->username = $request->input('username');
}
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
if ($request->filled('email')) {
$user->email = $request->input('email');
}
// check for permissions related fields and pull them out if the current user cannot edit them
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
if ($request->filled('username')) {
$user->username = $request->input('username');
}
if ($request->filled('display_name')) {
$user->display_name = $request->input('display_name');
}
if ($request->filled('email')) {
$user->email = $request->input('email');
}
if ($request->filled('activated')) {
$user->activated = $request->input('activated');
}
if ($request->filled('activated')) {
$user->activated = $request->input('activated');
}
// We need to use has() instead of filled()
// here because we need to overwrite permissions
// if someone needs to null them out
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
}
// Strip out the individual superuser permission if the API user isn't a superadmin
if (!auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
// We need to use has() instead of filled()
// here because we need to overwrite permissions
// if someone needs to null them out
if ($request->filled('display_name')) {
$user->display_name = $request->input('display_name');
}
if ($request->filled('company_id')) {
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
}
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
// Strip out the individual superuser permission if the API user isn't a superadmin
if (!auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
}
$user->permissions = $permissions_array;
}
if ($request->has('location_id')) {
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
}
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
if ($user->save()) {
// Check if the request has groups passed and has a value, AND that the user us a superuser
if (($request->has('groups')) && (auth()->user()->isSuperUser())) {
$validator = Validator::make($request->only('groups'), [
'groups.*' => 'integer|exists:permission_groups,id',
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
$user->permissions = $permissions_array;
// Sync the groups since the user is a superuser and the groups pass validation
$user->groups()->sync($request->input('groups'));
}
if($request->has('location_id')) {
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
}
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
if ($user->save()) {
// Check if the request has groups passed and has a value, AND that the user us a superuser
if (($request->has('groups')) && (auth()->user()->isSuperUser())) {
$validator = Validator::make($request->only('groups'), [
'groups.*' => 'integer|exists:permission_groups,id',
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
// Sync the groups since the user is a superuser and the groups pass validation
$user->groups()->sync($request->input('groups'));
}
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
}
/**
+117 -102
View File
@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\CreateMultipleAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
@@ -98,7 +99,7 @@ class AssetsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
*/
public function store(ImageUploadRequest $request) : RedirectResponse
public function store(CreateMultipleAssetRequest $request): RedirectResponse
{
$this->authorize(Asset::class);
@@ -135,122 +136,136 @@ class AssetsController extends Controller
$successes = [];
$failures = [];
for ($a = 1, $aMax = count($asset_tags); $a <= $aMax; $a++) {
$asset = new Asset();
try {
DB::beginTransaction();
for ($a = 1, $aMax = count($asset_tags); $a <= $aMax; $a++) {
$asset = new Asset();
$asset->model()->associate($model);
$asset->name = $request->input('name');
$asset->model()->associate($model);
$asset->name = $request->input('name');
// Check for a corresponding serial
if (($serials) && (array_key_exists($a, $serials))) {
$asset->serial = $serials[$a];
}
if (($asset_tags) && (array_key_exists($a, $asset_tags))) {
$asset->asset_tag = $asset_tags[$a];
}
$asset->company_id = $companyId;
$asset->model_id = $request->input('model_id');
$asset->order_number = $request->input('order_number');
$asset->notes = $request->input('notes');
$asset->created_by = auth()->id();
$asset->status_id = request('status_id');
$asset->warranty_months = request('warranty_months', null);
$asset->purchase_cost = request('purchase_cost');
$asset->purchase_date = request('purchase_date', null);
$asset->asset_eol_date = request('asset_eol_date', null);
$asset->assigned_to = request('assigned_to', null);
$asset->supplier_id = request('supplier_id', null);
$asset->requestable = request('requestable', 0);
$asset->rtd_location_id = request('rtd_location_id', null);
$asset->byod = request('byod', 0);
if (! empty($settings->audit_interval)) {
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
}
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
if (!request('assigned_user') && !request('assigned_asset') && !request('assigned_location')) {
$asset->location_id = $request->input('rtd_location_id', null);
}
if ($request->has('use_cloned_image')) {
$cloned_model_img = Asset::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'assets/'.$new_image_name;
Storage::disk('public')->copy('assets/'.$cloned_model_img->image, $new_image);
$asset->image = $new_image_name;
// Check for a corresponding serial
if (($serials) && (array_key_exists($a, $serials))) {
$asset->serial = $serials[$a];
}
} else {
$asset = $request->handleImages($asset);
}
if (($asset_tags) && (array_key_exists($a, $asset_tags))) {
$asset->asset_tag = $asset_tags[$a];
}
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
$asset->company_id = $companyId;
$asset->model_id = $request->input('model_id');
$asset->order_number = $request->input('order_number');
$asset->notes = $request->input('notes');
$asset->created_by = auth()->id();
$asset->status_id = request('status_id');
$asset->warranty_months = request('warranty_months', null);
$asset->purchase_cost = request('purchase_cost');
$asset->purchase_date = request('purchase_date', null);
$asset->asset_eol_date = request('asset_eol_date', null);
$asset->assigned_to = request('assigned_to', null);
$asset->supplier_id = request('supplier_id', null);
$asset->requestable = request('requestable', 0);
$asset->rtd_location_id = request('rtd_location_id', null);
$asset->byod = request('byod', 0);
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
if (!empty($settings->audit_interval)) {
$asset->next_audit_date = Carbon::now()->addMonths((int)$settings->audit_interval)->toDateString();
}
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
if (!request('assigned_user') && !request('assigned_asset') && !request('assigned_location')) {
$asset->location_id = $request->input('rtd_location_id', null);
}
if ($request->has('use_cloned_image')) {
$cloned_model_img = Asset::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-' . date('U') . '-' . $cloned_model_img->image;
$new_image = 'assets/' . $new_image_name;
Storage::disk('public')->copy('assets/' . $cloned_model_img->image, $new_image);
$asset->image = $new_image_name;
}
} else {
$asset = $request->handleImages($asset);
}
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
} else {
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
}
}
} else {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
} else {
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
$asset->{$field->db_column} = $request->input($field->db_column);
}
}
} else {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
} else {
$asset->{$field->db_column} = $request->input($field->db_column);
}
}
// Validate the asset before saving
// Note - it can be tempting to instead want to call saveOrFail(), to automatically throw when an object
// is invalid (and can't save). But this won't work, because Custom Fields _overrides_ the save() method
// to inject the Custom Field Rules into the $rules property right before invoking the _real_ save.
// so, instead, we have to catch failures on the 'else' clause and throw there.
if ($asset->isValid() && $asset->save()) {
$target = null;
$location = null;
if ($userId = request('assigned_user')) {
$target = User::find($userId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
}
$location = $target->location_id;
} elseif ($assetId = request('assigned_asset')) {
$target = Asset::find($assetId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
}
$location = $target->location_id;
} elseif ($locationId = request('assigned_location')) {
$target = Location::find($locationId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
}
$location = $target->id;
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
}
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
} else {
$asset->throwValidationException(); // we have to do this for the reason listed above - can't use saveOrFail()
$failures[] = join(",", $asset->getErrors()->all()); //TODO - this can probably go away soon
}
}
// Validate the asset before saving
if ($asset->isValid() && $asset->save()) {
$target = null;
$location = null;
if ($userId = request('assigned_user')) {
$target = User::find($userId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
}
$location = $target->location_id;
} elseif ($assetId = request('assigned_asset')) {
$target = Asset::find($assetId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
}
$location = $target->location_id;
} elseif ($locationId = request('assigned_location')) {
$target = Location::find($locationId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
}
$location = $target->id;
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
}
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
} else {
$failures[] = join(",", $asset->getErrors()->all());
}
} catch (\Throwable $e) {
\Log::debug("Caught exception in multi-create - rolling back: " . $e->getMessage());
DB::rollBack();
throw $e;
}
DB::commit();
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
+21 -12
View File
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\ProfileTransformer;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CurrentInventory;
@@ -34,7 +35,7 @@ class ProfileController extends Controller
*/
public function getIndex() : View
{
$this->authorize('self.profile');
$user = auth()->user();
return view('account/profile', compact('user'));
}
@@ -47,20 +48,25 @@ class ProfileController extends Controller
*/
public function postIndex(ImageUploadRequest $request) : RedirectResponse
{
$this->authorize('self.profile');
$user = auth()->user();
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->website = $request->input('website');
$user->gravatar = $request->input('gravatar');
$user->skin = $request->input('skin');
$user->phone = $request->input('phone');
if ((Gate::allows('self.profile')) && (! config('app.lock_passwords'))) {
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->website = $request->input('website');
$user->gravatar = $request->input('gravatar');
$user->phone = $request->input('phone');
}
$user->enable_sounds = $request->input('enable_sounds', false);
$user->enable_confetti = $request->input('enable_confetti', false);
$user->link_light_color = $request->input('link_light_color', '#296282');
$user->link_dark_color = $request->input('link_dark_color', '#296282');
$user->nav_link_color = $request->input('nav_link_color', '#FFFFFF');
$user->locale = $request->input('locale');
if (! config('app.lock_passwords')) {
$user->locale = $request->input('locale');
}
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled == '1') && (! config('app.lock_passwords')))) {
$user->two_factor_optin = $request->input('two_factor_optin', '0');
@@ -249,7 +255,10 @@ class ProfileController extends Controller
$logentry = Actionlog::where('filename', $filename)->first();
// Make sure the user has permission to view this file
if (auth()->id() != $logentry->target_id) {
// Also allow if the user (manager) able to view both users and assets
$allowed_to_view_users_assets = Gate::allows('view', User::class) && Gate::allows('view', Asset::class);
if (auth()->id() != $logentry->target_id && !$allowed_to_view_users_assets) {
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
}
+14 -236
View File
@@ -6,38 +6,31 @@ use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SettingsSamlRequest;
use App\Http\Requests\SetupUserRequest;
use App\Http\Requests\StoreLabelSettings;
use App\Http\Requests\StoreLdapSettings;
use App\Http\Requests\StoreLocalizationSettings;
use App\Http\Requests\StoreNotificationSettings;
use App\Http\Requests\StoreLabelSettings;
use App\Http\Requests\StoreSecuritySettings;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Group;
use App\Models\Labels\Label as LabelModel;
use App\Models\Setting;
use App\Models\Asset;
use App\Models\User;
use App\Notifications\FirstAdminNotification;
use App\Notifications\MailTest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\JsonResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\Str;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use \Illuminate\Contracts\View\View;
/**
* This controller handles all actions related to Settings for
@@ -47,224 +40,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
*/
class SettingsController extends Controller
{
/**
* Checks to see whether or not the database has a migrations table
* and a user, otherwise display the setup view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*
* @return \Illuminate\Contracts\View\View | \Illuminate\Http\Response
*/
public function getSetupIndex() : View
{
$start_settings['php_version_min'] = false;
if (version_compare(PHP_VERSION, config('app.min_php'), '<')) {
return response('<center><h1>This software requires PHP version '.config('app.min_php').' or greater. This server is running '.PHP_VERSION.'. </h1><h2>Please upgrade PHP on this server and try again. </h2></center>', 500);
}
try {
$conn = DB::select('select 2 + 2');
$start_settings['db_conn'] = true;
$start_settings['db_name'] = DB::connection()->getDatabaseName();
$start_settings['db_error'] = null;
} catch (\PDOException $e) {
$start_settings['db_conn'] = false;
$start_settings['db_name'] = config('database.connections.mysql.database');
$start_settings['db_error'] = $e->getMessage();
}
$start_settings['url_config'] = trim(config('app.url'), '/'). '/setup';
$start_settings['real_url'] = request()->url();
$start_settings['url_valid'] = $start_settings['url_config'] === $start_settings['real_url'];
$start_settings['php_version_min'] = true;
// Curl the .env file to make sure it's not accessible via a browser
$start_settings['env_exposed'] = $this->dotEnvFileIsExposed();
if (App::Environment('production') && (true == config('app.debug'))) {
$start_settings['debug_exposed'] = true;
} else {
$start_settings['debug_exposed'] = false;
}
$environment = app()->environment();
if ('production' != $environment) {
$start_settings['env'] = $environment;
$start_settings['prod'] = false;
} else {
$start_settings['env'] = $environment;
$start_settings['prod'] = true;
}
$start_settings['owner'] = '';
if (function_exists('posix_getpwuid')) { // Probably Linux
$owner = posix_getpwuid(fileowner($_SERVER['SCRIPT_FILENAME']));
// This *should* be an array, but we've seen this return a bool in some chrooted environments
if (is_array($owner)) {
$start_settings['owner'] = $owner['name'];
}
}
if (($start_settings['owner'] === 'root') || ($start_settings['owner'] === '0')) {
$start_settings['owner_is_admin'] = true;
} else {
$start_settings['owner_is_admin'] = false;
}
$start_settings['writable'] = $this->storagePathIsWritable();
$start_settings['gd'] = extension_loaded('gd');
return view('setup/index')
->with('step', 1)
->with('start_settings', $start_settings)
->with('section', 'Pre-Flight Check');
}
/**
* Determine if the .env file accessible via a browser.
*
* @return bool This method will return true when exceptions (such as curl exception) is thrown.
* Check the log files to see more details about the exception.
*/
protected function dotEnvFileIsExposed() : bool
{
try {
return Http::withoutVerifying()->timeout(10)
->accept('*/*')
->get(URL::to('.env'))
->successful();
} catch (\Exception $e) {
Log::debug($e->getMessage());
return true;
}
}
/**
* Determine if the app storage path is writable.
*/
protected function storagePathIsWritable(): bool
{
return File::isWritable(storage_path()) &&
File::isWritable(storage_path('framework')) &&
File::isWritable(storage_path('framework/cache')) &&
File::isWritable(storage_path('framework/sessions')) &&
File::isWritable(storage_path('framework/views')) &&
File::isWritable(storage_path('logs'));
}
/**
* Save the first admin user from Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*
*/
public function postSaveFirstAdmin(SetupUserRequest $request) : RedirectResponse
{
$user = new User();
$user->first_name = $data['first_name'] = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->email = $data['email'] = $request->input('email');
$user->activated = 1;
$permissions = ['superuser' => 1];
$user->permissions = json_encode($permissions);
$user->username = $data['username'] = $request->input('username');
$user->password = bcrypt($request->input('password'));
$data['password'] = $request->input('password');
$settings = new Setting();
$settings->full_multiple_companies_support = $request->input('full_multiple_companies_support', 0);
$settings->site_name = $request->input('site_name');
$settings->alert_email = $request->input('email');
$settings->alerts_enabled = 1;
$settings->pwd_secure_min = 10;
$settings->brand = 1;
$settings->locale = $request->input('locale', 'en-US');
$settings->default_currency = $request->input('default_currency', 'USD');
$settings->created_by = 1;
$settings->email_domain = $request->input('email_domain');
$settings->email_format = $request->input('email_format');
$settings->next_auto_tag_base = 1;
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
$settings->zerofill_count = $request->input('zerofill_count') ?: 0;
if ((! $user->isValid()) || (! $settings->isValid())) {
return redirect()->back()->withInput()->withErrors($user->getErrors())->withErrors($settings->getErrors());
} else {
$user->save();
Auth::login($user, true);
$settings->save();
if ($request->input('email_creds') == '1') {
$data = [];
$data['email'] = $user->email;
$data['username'] = $user->username;
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['password'] = $request->input('password');
$user->notify(new FirstAdminNotification($data));
}
return redirect()->route('setup.done');
}
}
/**
* Return the admin user creation form in Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function getSetupUser() : View
{
return view('setup/user')
->with('step', 3)
->with('section', 'Create a User');
}
/**
* Return the view that tells the user that the Setup is done.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function getSetupDone() : View
{
return view('setup/done')
->with('step', 4)
->with('section', 'Done!');
}
/**
* Migrate the database tables, and return the output
* to a view for Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function getSetupMigrate() : View
{
Artisan::call('migrate', ['--force' => true]);
if ((! file_exists(storage_path().'/oauth-private.key')) || (! file_exists(storage_path().'/oauth-public.key'))) {
Artisan::call('migrate', ['--path' => 'vendor/laravel/passport/database/migrations', '--force' => true]);
Artisan::call('passport:install', ['--no-interaction' => true]);
}
return view('setup/migrate')
->with('output', 'Databases installed!')
->with('step', 2)
->with('section', 'Create Database Tables');
}
/**
* Return a view that shows some of the key settings.
@@ -399,12 +174,10 @@ class SettingsController extends Controller
}
$setting->brand = $request->input('brand', '1');
$setting->header_color = $request->input('header_color');
$setting->support_footer = $request->input('support_footer');
$setting->version_footer = $request->input('version_footer');
$setting->footer_text = $request->input('footer_text');
$setting->skin = $request->input('skin');
$setting->allow_user_skin = $request->input('allow_user_skin', '0');
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
$setting->load_remote = $request->input('load_remote', 0);
@@ -418,6 +191,11 @@ class SettingsController extends Controller
$request->validate(['site_name' => 'required']);
}
$setting->header_color = $request->input('header_color');
$setting->link_light_color = $request->input('link_light_color', '#296282');
$setting->link_dark_color = $request->input('link_dark_color', '#296282');
$setting->nav_link_color = $request->input('nav_link_color', '#FFFFFF');
$setting->site_name = $request->input('site_name', 'Snipe-IT');
$setting->custom_css = $request->input('custom_css');
+270
View File
@@ -0,0 +1,270 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\SetupUserRequest;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\FirstAdminNotification;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
use \Illuminate\Contracts\View\View;
/**
* This controller handles all actions related to Settings for
* the Snipe-IT Asset Management application.
*
* @version v1.0
*/
class SetupController extends Controller
{
/**
* Checks to see whether or not the database has a migrations table
* and a user, otherwise display the setup view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*
* @return \Illuminate\Contracts\View\View | \Illuminate\Http\Response
*/
public function getSetupIndex() : View
{
$start_settings['php_version_min'] = false;
if (version_compare(PHP_VERSION, config('app.min_php'), '<')) {
return response('<center><h1>This software requires PHP version '.config('app.min_php').' or greater. This server is running '.PHP_VERSION.'. </h1><h2>Please upgrade PHP on this server and try again. </h2></center>', 500);
}
try {
$conn = DB::select('select 2 + 2');
$start_settings['db_conn'] = true;
$start_settings['db_name'] = DB::connection()->getDatabaseName();
$start_settings['db_error'] = null;
} catch (\PDOException $e) {
$start_settings['db_conn'] = false;
$start_settings['db_name'] = config('database.connections.mysql.database');
$start_settings['db_error'] = $e->getMessage();
}
$start_settings['url_config'] = trim(config('app.url'), '/'). '/setup';
$start_settings['real_url'] = request()->url();
$start_settings['url_valid'] = $start_settings['url_config'] === $start_settings['real_url'];
$start_settings['php_version_min'] = true;
// Curl the .env file to make sure it's not accessible via a browser
$start_settings['env_exposed'] = $this->dotEnvFileIsExposed();
if (App::Environment('production') && (true == config('app.debug'))) {
$start_settings['debug_exposed'] = true;
} else {
$start_settings['debug_exposed'] = false;
}
$environment = app()->environment();
if ('production' != $environment) {
$start_settings['env'] = $environment;
$start_settings['prod'] = false;
} else {
$start_settings['env'] = $environment;
$start_settings['prod'] = true;
}
$start_settings['owner'] = '';
if (function_exists('posix_getpwuid')) { // Probably Linux
$owner = posix_getpwuid(fileowner($_SERVER['SCRIPT_FILENAME']));
// This *should* be an array, but we've seen this return a bool in some chrooted environments
if (is_array($owner)) {
$start_settings['owner'] = $owner['name'];
}
}
if (($start_settings['owner'] === 'root') || ($start_settings['owner'] === '0')) {
$start_settings['owner_is_admin'] = true;
} else {
$start_settings['owner_is_admin'] = false;
}
$start_settings['writable'] = $this->storagePathIsWritable();
$start_settings['gd'] = extension_loaded('gd');
return view('setup/index')
->with('step', 1)
->with('start_settings', $start_settings)
->with('section', trans('general.setup_config_check'))
->with('icon', 'fa-regular fa-rectangle-list');
}
/**
* Determine if the .env file accessible via a browser.
*
* @return bool This method will return true when exceptions (such as curl exception) is thrown.
* Check the log files to see more details about the exception.
*/
protected function dotEnvFileIsExposed() : bool
{
try {
return Http::withoutVerifying()->timeout(10)
->accept('*/*')
->get(URL::to('.env'))
->successful();
} catch (\Exception $e) {
Log::debug($e->getMessage());
return true;
}
}
/**
* Determine if the app storage path is writable.
*/
protected function storagePathIsWritable(): bool
{
return File::isWritable(storage_path()) &&
File::isWritable(storage_path('framework')) &&
File::isWritable(storage_path('framework/cache')) &&
File::isWritable(storage_path('framework/sessions')) &&
File::isWritable(storage_path('framework/views')) &&
File::isWritable(storage_path('logs'));
}
/**
* Save the first admin user from Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*
*/
public function postSaveFirstAdmin(SetupUserRequest $request) : RedirectResponse
{
$user = new User();
$user->first_name = $data['first_name'] = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->email = $data['email'] = $request->input('email');
$user->activated = 1;
$permissions = ['superuser' => 1];
$user->permissions = json_encode($permissions);
$user->username = $data['username'] = $request->input('username');
$user->password = bcrypt($request->input('password'));
$data['password'] = $request->input('password');
$settings = new Setting();
$settings->full_multiple_companies_support = $request->input('full_multiple_companies_support', 0);
$settings->site_name = $request->input('site_name');
$settings->alert_email = $request->input('email');
$settings->alerts_enabled = 1;
$settings->pwd_secure_min = 10;
$settings->brand = 1;
$settings->link_light_color = $request->input('link_light_color', '#296282');
$settings->link_dark_color = $request->input('link_dark_color', '#296282');
$settings->nav_link_color = $request->input('nav_link_color', '#FFFFFF');
$settings->locale = $request->input('locale', 'en-US');
$settings->default_currency = $request->input('default_currency', 'USD');
$settings->created_by = 1;
$settings->email_domain = $request->input('email_domain');
$settings->email_format = $request->input('email_format');
$settings->next_auto_tag_base = 1;
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
$settings->zerofill_count = $request->input('zerofill_count') ?: 0;
if ((! $user->isValid()) || (! $settings->isValid())) {
return redirect()->back()->withInput()->withErrors($user->getErrors())->withErrors($settings->getErrors());
} else {
$user->save();
Auth::login($user, true);
$settings->save();
if ($request->input('email_creds') == '1') {
$data = [];
$data['email'] = $user->email;
$data['username'] = $user->username;
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['password'] = $request->input('password');
$user->notify(new FirstAdminNotification($data));
}
return redirect()
->route('setup.done')
->with('section', trans('general.setup_create_admin'))
->with('icon', 'fa-solid fa-champagne-glasses')
->with('success', trans('admin/settings/general.create_admin_success'));
}
}
/**
* Return the admin user creation form in Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function getSetupUser() : View
{
return view('setup/user')
->with('step', 3)
->with('section', trans('general.setup_create_admin'))
->with('icon', 'fa-solid fa-user-plus');
}
/**
* Return the view that tells the user that the Setup is done.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function getSetupDone() : View
{
return view('setup/done')
->with('success', trans('general.create_admin_success'))
->with('step', 4)
->with('icon', 'fa-solid fa-champagne-glasses fa-shake')
->with('section', trans('general.setup_done'));
}
/**
* Migrate the database tables, and return the output
* to a view for Setup.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v3.0]
*/
public function setupMigrate()
{
Artisan::call('migrate', ['--force' => true]);
$output = Artisan::output();
if ((! file_exists(storage_path().'/oauth-private.key')) || (! file_exists(storage_path().'/oauth-public.key'))) {
Artisan::call('migrate', ['--path' => 'vendor/laravel/passport/database/migrations', '--force' => true]);
Artisan::call('passport:install', ['--no-interaction' => true]);
}
return view('setup/migrate')
->with('success', trans('general.create_admin_success'))
->with('output', trim($output))
->with('step', 2)
->with('section', trans('general.setup_create_database'))
->with('icon', 'fa-solid fa-database');
}
}
@@ -2,9 +2,11 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Import;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@@ -155,7 +157,31 @@ class UploadedFilesController extends Controller
}
// The file doesn't seem to really exist, so report an error
return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.delete.error', 1));
return redirect()->back()->withFragment('files')->with('error', trans_choice('general.file_upload_status.delete.error', 1));
}
public function downloadImport(Import $import) {
$this->authorize('import');
if ($import = Import::find($import->id)) {
if ((auth()->user()->id != $import->created_by) && (!auth()->user()->isSuperUser())) {
return redirect()->back()->with('error', trans('general.file_upload_status.file_not_found'));
}
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/imports/' . $import->file_path, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/imports/' . $import->file_path)) {
return response()->download(config('app.private_uploads') . '/imports/' . $import->file_path);
}
}
return redirect()->back()->with('error', trans('general.file_upload_status.file_not_found'));
}
+1
View File
@@ -44,6 +44,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\CheckForTwoFactor::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
\App\Http\Middleware\AssetCountForSidebar::class,
\App\Http\Middleware\CheckColorSettings::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
@@ -0,0 +1,75 @@
<?php
namespace App\Http\Middleware;
use App\Models\Setting;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Support\Facades\Auth;
class CheckColorSettings
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Set defaults in case this is accessed via the /setup screen
$nav_color = '#ffffff';
$link_dark_color = '#89c9ed';
$link_light_color = '#3c8dbc';
if ($settings = Setting::getSettings()) {
$nav_color = $settings->nav_link_color;
$link_dark_color = $settings->link_dark_color;
$link_light_color = $settings->link_light_color;
}
// Override system settings
if ($request->user()) {
if ($request->user()->nav_color) {
$nav_color = $request->user()->nav_color;
}
if ($request->user()->link_dark_color) {
$link_dark_color = $request->user()->link_dark_color;
}
if ($request->user()->nav_color) {
$link_light_color = $request->user()->link_light_color;
}
}
view()->share('nav_link_color', $nav_color);
view()->share('link_dark_color', $link_dark_color);
view()->share('link_light_color', $link_light_color);
return $next($request);
}
}
@@ -0,0 +1,68 @@
<?php
namespace App\Http\Requests;
use App\Http\Requests\Traits\MayContainCustomFields;
use App\Models\Asset;
use Illuminate\Foundation\Http\FormRequest;
use App\Helpers\Helper;
use App\Models\Setting;
use App\Models\AssetModel;
use App\Rules\UniqueUndeleted;
use Illuminate\Support\Str;
class CreateMultipleAssetRequest extends ImageUploadRequest //should I extend from StoreAssetRequest? FIXME OR TODO OR THINKME
{
use MayContainCustomFields;
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true; //TODO - should I do the auth check here?
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
//grab the rules for serials and asset_tags, and tweak them into an array context for multi-create usage
$modelRules = (new Asset)->getRules();
unset($modelRules['serial']);
$asset_tag_rules = $modelRules['asset_tag'];
unset($modelRules['asset_tag']);
// now we replace the 'not_array' rule with 'distinct'
array_splice($asset_tag_rules, array_search('not_array', $asset_tag_rules), 1, 'distinct');
// and replace the 'unique_undeleted' rule with the Rule object
foreach ($asset_tag_rules as $i => $asset_tag_rule) {
if (Str::startsWith($asset_tag_rule, 'unique_undeleted')) {
$asset_tag_rules[$i] = new UniqueUndeleted('assets', 'asset_tag');
}
}
$serials_unique = Setting::getSettings()['unique_serial'];
$serials_required = AssetModel::find($this?->model_id)?->require_serial;
$serial_rules = ['string'];
if ($serials_unique) {
// $serial_rules[] = 'unique_undeleted:assets,serial';
$serial_rules[] = new UniqueUndeleted('assets', 'serial');
$serial_rules[] = 'distinct';
}
if ($serials_required) {
$serial_rules[] = 'required';
} else {
$serial_rules[] = 'nullable';
}
return array_merge($modelRules, [
'serials.*' => $serial_rules,
'asset_tags.*' => $asset_tag_rules,
]);
}
}
-1
View File
@@ -28,7 +28,6 @@ class SetupUserRequest extends Request
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
'email' => 'email|unique:users,email',
'password' => 'required|min:8|confirmed',
'email_domain' => 'required|min:4',
];
}
+1
View File
@@ -93,6 +93,7 @@ abstract class Importer
'min_amt' => 'minimum quantity',
'remote' => 'remote',
'vip' => 'vip',
'tag_color' => 'tag color',
];
/**
* Map of item fields->csv names
+4
View File
@@ -75,6 +75,7 @@ class LocationImporter extends ItemImporter
$this->item['manager'] = trim($this->findCsvMatch($row, 'manager'));
$this->item['manager_username'] = trim($this->findCsvMatch($row, 'manager_username'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
if ($this->findCsvMatch($row, 'parent_location')) {
@@ -96,6 +97,9 @@ class LocationImporter extends ItemImporter
$location->update($this->sanitizeItemForUpdating($location));
} else {
Log::debug('Creating location');
if ($this->findCsvMatch($row, 'company')) {
$this->item['company_id'] = $this->createOrFetchCompany(trim($this->findCsvMatch($row, 'company')));
}
$location->fill($this->sanitizeItemForStoring($location));
}
+18 -1
View File
@@ -347,6 +347,7 @@ class Importer extends Component
$this->locations_fields = [
'id' => trans('general.id'),
'company' => trans('general.company'),
'name' => trans('general.name'),
'address' => trans('general.address'),
'address2' => trans('general.importer.address2'),
@@ -360,6 +361,7 @@ class Importer extends Component
'parent_location' => trans('admin/locations/table.parent'),
'state' => trans('general.state'),
'zip' => trans('general.zip'),
'tag_color' => trans('general.tag_color'),
];
$this->suppliers_fields = [
@@ -608,6 +610,14 @@ class Importer extends Component
[
'Manager Username',
],
'tag_color' =>
[
'color',
'tag color',
'label color',
'color code',
trans('general.tag_color'),
],
];
$this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean
@@ -663,6 +673,13 @@ class Importer extends Component
return;
}
if ((auth()->user()->id != $import->created_by) && (!auth()->user()->isSuperUser())) {
$this->message = trans('general.generic_model_not_found', ['model' => trans('general.import')]);
$this->message_type = 'danger';
return;
}
if (Storage::delete('private_uploads/imports/' . $import->file_path)) {
$import->delete();
$this->message = trans('admin/hardware/message.import.file_delete_success');
@@ -673,7 +690,7 @@ class Importer extends Component
return;
}
$this->message = trans('admin/hardware/message.import.file_delete_error');
$this->message = trans('general.generic_model_not_found', ['model' => trans('general.import')]);
$this->message_type = 'danger';
}
+13
View File
@@ -246,6 +246,19 @@ class Accessory extends SnipeModel
->with('assignedTo');
}
/**
* Establishes the accessory -> users relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function users()
{
return $this->belongsToMany(\App\Models\AccessoryCheckout::class, 'accessories_checkout')
->with('assignedTo');
}
/**
* Establishes the accessory -> admin user relationship
*
+24
View File
@@ -48,6 +48,30 @@ class CheckoutAcceptance extends Model
default => class_basename($type),
};
}
/**
* Accessor for the checkoutable item's category name.
*
* @return Attribute
*/
protected function checkoutableCategoryName(): Attribute
{
return Attribute::make(
get: function () {
$item = $this->checkoutable;
if ($item instanceof Asset) {
return $item->model?->category?->name;
}
if ($item instanceof LicenseSeat) {
return $item->license?->category?->name;
}
return $item->category?->name;
},
);
}
/**
* The resource that was is out
*
+3 -3
View File
@@ -13,7 +13,7 @@ class Checkoutable
public string $name,
public string $type,
public object $acceptance,
public object $assignee,
public readonly User|Asset|Location|null $assignee,
public readonly string $plain_text_category,
public readonly string $plain_text_model,
public readonly string $plain_text_name,
@@ -43,8 +43,8 @@ class Checkoutable
$name = optional($unaccepted_row->present())->nameUrl() ?? '';
}
if($unaccepted_row instanceof LicenseSeat){
$category = optional($unaccepted_row->license->category?->present())->nameUrl() ?? '';
$company = optional($unaccepted_row->license->company?->present())?->nameUrl() ?? '';
$category = optional($unaccepted_row->license?->category?->present())->nameUrl() ?? '';
$company = optional($unaccepted_row->license?->company?->present())?->nameUrl() ?? '';
$model = '';
$name = $unaccepted_row->license->present()->nameUrl() ?? '';
}
+29 -3
View File
@@ -110,6 +110,18 @@ class Component extends SnipeModel
'manufacturer' => ['name'],
];
public static function booted()
{
static::saving(function ($model) {
// We use 'sum_unconstrained_assets' as a 'cache' of the count of the sum of unconstrained assets, but
// Eloquent will gladly try to save the value of that attribute in the case where we populate it ourselves.
// But when it gets populated by 'withSum()' - it seems to work fine due to some Eloquent magic I am not
// aware of. During a save, the quantity may have changed or other aspects may have changed, so
// "invalidating the 'cache'" seems like a fair choice here.
unset($model->sum_unconstrained_assets);
});
}
public function isDeletable()
{
@@ -238,14 +250,28 @@ class Component extends SnipeModel
* @since [v5.0]
* @return int
*/
public function numCheckedOut()
public function numCheckedOut(bool $recalculate = false)
{
$checkedout = 0;
/**
*
* WARNING: This method caches the result, so if you're doing something
* that is going to change the number of checked-out items, make sure to pass
* 'true' as the first parameter to force this to recalculate the number of checked-out
* items!!!!!
*
*/
// In case there are elements checked out to assets that belong to a different company
// than this asset and full multiple company support is on we'll remove the global scope,
// so they are included in the count.
return $this->uncontrainedAssets->sum('pivot.assigned_qty');
if (is_null($this->sum_unconstrained_assets) || $recalculate) {
// This, in a components-listing context, is mostly important for when it sets a 'zero' which
// is *not* null - so we don't have to keep recalculating for un-checked-out components
// NOTE: doing this will add a 'pseudo-attribute' to the component in question, so we need to _remove_ this
// before we save - so that gets handled in the 'saving' callback defined in the 'booted' method, above.
$this->sum_unconstrained_assets = $this->uncontrainedAssets()->sum('assigned_qty') ?? 0;
}
return $this->sum_unconstrained_assets;
}
+23 -7
View File
@@ -95,23 +95,39 @@ class L4736_A extends L4736
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
$fields = $record->get('fields');
$fieldCount = count($fields);
foreach ($record->get('fields') as $field) {
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', self::LABEL_SIZE, 'L',
$usableWidth, self::LABEL_SIZE, true, 0
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += self::LABEL_SIZE + self::LABEL_MARGIN;
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.01
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$currentY += $fieldSize + $fieldMargin;
}
}
+28 -7
View File
@@ -59,23 +59,44 @@ class L6009_A extends L6009
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
foreach ($record->get('fields') as $field) {
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', self::LABEL_SIZE, 'L',
$usableWidth, self::LABEL_SIZE, true, 0
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += self::LABEL_SIZE + self::LABEL_MARGIN;
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.01
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$currentY += $fieldSize + $fieldMargin;
}
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Models\Labels\Tapes\Brother;
class TZe_241 extends TZe_18mm
{
private const LABEL_SIZE = 5.0;
private const LABEL_MARGIN = 0.6;
private const FIELD_SIZE = 5.0;
private const FIELD_MARGIN = 0.8;
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 50.0;
}
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 2;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return false;
}
public function preparePDF($pdf){}
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$usableWidth = $pa->w;
$usableHeight = $pa->h;
$fields = $record->get('fields') ?? [];
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
}
}
}
+26 -9
View File
@@ -30,12 +30,12 @@ class TZe_24mm_E extends TZe_24mm
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$currentY = $pa->y1 -2;
$usableWidth = $pa->w;
$usableHeight = $pa->h - self::BARCODE1D_SIZE;
$barcodeSize = $usableHeight - self::TAG_SIZE;
$barcodeSize = ($usableHeight - self::TAG_SIZE) * 1.2;
if ($record->has('barcode2d')) {
static::writeText(
@@ -70,10 +70,27 @@ class TZe_24mm_E extends TZe_24mm
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
$fields = $record->get('fields');
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
// Write label and value on the same line
// Calculate label width with proportional character spacing
$labelWidth = $pdf->GetStringWidth($field['label'], 'freesans', '', self::LABEL_SIZE);
$labelWidth = $pdf->GetStringWidth($field['label'], 'freesans', '', $labelSize);
$charCount = strlen($field['label']);
$spacingPerChar = 0.5;
$totalSpacing = $charCount * $spacingPerChar;
@@ -82,18 +99,18 @@ class TZe_24mm_E extends TZe_24mm
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', 'B', self::LABEL_SIZE, 'L',
$adjustedWidth, self::LABEL_SIZE, true, 0, $spacingPerChar
'freesans', 'B', $labelSize, 'L',
$adjustedWidth, $labelSize, 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
'freesans', 'B', $fieldSize, 'L',
$usableWidth - $adjustedWidth - 2, $fieldSize, true, 0, 0.3
);
$currentY += max(self::LABEL_SIZE, self::FIELD_SIZE) + self::FIELD_MARGIN;
$currentY += max($labelSize, $fieldSize) +$fieldMargin;
}
@@ -12,6 +12,8 @@ class LabelWriter_11354 extends LabelWriter
private const TITLE_MARGIN = 0.50;
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
private const LABEL_SIZE = 2.8;
private const LABEL_MARGIN = 0.6;
public function getUnit()
{
@@ -102,14 +104,47 @@ class LabelWriter_11354 extends LabelWriter
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE
- self::BARCODE_MARGIN;
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.3
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
}
}
@@ -91,14 +91,47 @@ class LabelWriter_1933081 extends LabelWriter
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE // bottom tag text
- self::BARCODE_MARGIN; // gap between fields and 1D
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.3
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
}
if ($record->has('barcode1d')) {
@@ -91,16 +91,48 @@ class LabelWriter_2112283 extends LabelWriter
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$currentX, $currentY,
'freesans', '', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.3
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE // bottom tag text
- self::BARCODE_MARGIN; // gap between fields and 1D
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
+8 -5
View File
@@ -784,21 +784,24 @@ class License extends Depreciable
* @return \Illuminate\Database\Eloquent\Relations\Relation
* @see \App\Console\Commands\SendExpiringLicenseNotifications
*/
public function scopeExpiringLicenses($query, $days = 60)
public function scopeExpiringLicenses($query, $days = 60, $includeExpired = false)
{
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) {
->where(function ($query) use ($days, $includeExpired) {
$query->whereNotNull('expiration_date')
// Handle expiring licenses without termination dates
->where(function ($query) use ($days) {
->where(function ($query) use ($days, $includeExpired) {
$query->whereNull('termination_date')
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)])
//include expired licenses if requested
->when($includeExpired, function ($query) use ($days) {
$query->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)]);
+1 -1
View File
@@ -35,7 +35,7 @@ class Supplier extends SnipeModel
'state' => 'min:2|max:191|nullable',
'country' => 'min:2|max:191|nullable',
'zip' => 'max:10|nullable',
'url' => 'sometimes|nullable|string|max:250',
'url' => 'sometimes|url|nullable|string|max:250',
];
/**
@@ -74,7 +74,8 @@ class AcceptanceItemDeclinedNotification extends Notification
'company_name' => $this->company_name,
'qty' => $this->qty,
'admin' => $this->admin,
'intro_text' => trans('mail.acceptance_declined_greeting'),
'user' => $this->assigned_to,
'intro_text' => trans('mail.acceptance_declined_greeting', ['user' => $this->assigned_to]),
])
->subject('⚠️ '.trans('mail.acceptance_declined', ['user' => $this->assigned_to, 'item' => $this->item_name]))
->withSymfonyMessage(function (Email $message) {
@@ -50,7 +50,12 @@ class ExpectedCheckinNotification extends Notification
*/
public function toMail()
{
$today = Carbon::now();
$today = Carbon::today();
$expected = Carbon::parse($this->params->expected_checkin)->startOfDay();
$subjectText = $today->greaterThan($expected)
? trans('mail.Expected_Checkin_Notification_Pastdue', ['name' => $this->params->display_name])
: trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name]);
$message = (new MailMessage)->markdown('notifications.markdown.expected-checkin',
[
@@ -60,7 +65,7 @@ class ExpectedCheckinNotification extends Notification
'serial' => $this->params->serial,
'asset_tag' => $this->params->asset_tag,
])
->subject('⏰'. ($today > $this->params->expected_checkin) ? trans('mail.Expected_Checkin_Notification_Pastdue', ['name' => $this->params->display_name]) : trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name]))
->subject('⏰'. $subjectText)
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()->addTextHeader(
'X-System-Sender', 'Snipe-IT'
+1 -1
View File
@@ -25,7 +25,7 @@ class InventoryAlert extends Notification
public function __construct($params, $threshold)
{
$this->items = $params;
$this->threshold = $threshold;
$this->threshold = $threshold ?? 0;
}
/**
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Validation\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Contracts\Validation\DataAwareRule;
class UniqueUndeleted implements ValidationRule, ValidatorAwareRule
{
protected ?Validator $validator = null;
protected array $columns = [];
protected $data = [];
public function __construct(
public string $table,
string ...$columns,
)
{
$this->columns = $columns;
}
public function setValidator(Validator $validator): static
{
$this->validator = $validator;
$this->data = $validator->getData();
//TODO - can we somehow grab the ID of the route-model-bound object, and omit its ID?
// to do that, we'd have to know _which_ parameter in the validator is actually the R-M-B'ed
// parameter. Or maybe we just change the function signature to let you specify it.
return $this;
}
/**
* Run the validation rule.
*
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$query = DB::table($this->table)->whereNull('deleted_at');
$query->where($this->columns[0], '=', $value); //the first column to check
$translation_string = 'validation.unique_undeleted'; //the normal validation string for a single-column check
foreach (array_slice($this->columns, 1) as $column) {
$translation_string = 'validation.two_column_unique_undeleted';
$query->where($column, '=', $this->data[$column]);
}
if ($query->count() > 0) {
$fail($translation_string)->translate();
}
}
}
+5 -1
View File
@@ -63,6 +63,8 @@ $config = [
'region' => env('PUBLIC_AWS_DEFAULT_REGION'),
'bucket' => env('PUBLIC_AWS_BUCKET'),
'url' => env('PUBLIC_AWS_URL'),
'endpoint' => env('PUBLIC_AWS_ENDPOINT'),
'use_path_style_endpoint' => env('PUBLIC_AWS_PATH_STYLE'),
'root' => env('PUBLIC_AWS_BUCKET_ROOT'),
'visibility' => 'public'
],
@@ -78,6 +80,8 @@ $config = [
'region' => env('PRIVATE_AWS_DEFAULT_REGION'),
'bucket' => env('PRIVATE_AWS_BUCKET'),
'url' => env('PRIVATE_AWS_URL'),
'endpoint' => env('PRIVATE_AWS_ENDPOINT'),
'use_path_style_endpoint' => env('PRIVATE_AWS_PATH_STYLE'),
'root' => env('PRIVATE_AWS_BUCKET_ROOT'),
'visibility' => 'private'
],
@@ -168,4 +172,4 @@ $config['allowed_upload_mimetypes'] = implode(',', $config['allowed_upload_mimet
$config['allowed_upload_extensions_for_validator'] = implode(',', $config['allowed_upload_extensions_array']);
$config['allowed_upload_extensions'] = '.'.implode(', .', $config['allowed_upload_extensions_array']);
return $config;
return $config;
+5 -5
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v8.3.6',
'full_app_version' => 'v8.3.6 - build 20551-g523df21d8',
'build_version' => '20551',
'app_version' => 'v8.3.7',
'full_app_version' => 'v8.3.7 - build 20803-gdba8cb83b',
'build_version' => '20803',
'prerelease_version' => '',
'hash_version' => 'g523df21d8',
'full_hash' => 'v8.3.6-144-g523df21d8',
'hash_version' => 'gdba8cb83b',
'full_hash' => 'v8.3.7-250-gdba8cb83b',
'branch' => 'master',
);
@@ -0,0 +1,126 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$setting = DB::table('settings')->select(['skin', 'header_color'])->first();
Schema::table('settings', function (Blueprint $table) {
$table->string('link_dark_color')->after('header_color')->nullable()->default(null);
$table->string('link_light_color')->after('header_color')->nullable()->default(null);
$table->string('nav_link_color')->after('header_color')->nullable()->default('#ffffff');
});
Schema::table('users', function (Blueprint $table) {
$table->string('link_dark_color')->after('skin')->nullable()->default(null);
$table->string('link_light_color')->after('skin')->nullable()->default(null);
$table->string('nav_link_color')->after('skin')->nullable()->default('#ffffff');
});
// Set Snipe-IT defaults
$link_dark_color = '#89c9ed';
$link_light_color = '#296282';
$nav_color = '#ffffff';
$header_color = '#3c8dbc';
if ($setting) {
switch ($setting->skin) {
case ('green' || 'green-dark'):
$header_color = '#00a65a';
$link_dark_color = '#9ACD32';
$link_light_color = '#00a65a';
$nav_color = '#ffffff';
break;
case ('red' || 'red-dark'):
$header_color = '#dd4b39';
$link_dark_color = '#ed9a9a';
$link_light_color = '#dd4b39';
$nav_color = '#ffffff';
break;
case ('orange' || 'orange-dark'):
$header_color = '#FF851B';
$link_dark_color = '#FFA500';
$link_light_color = '#FF8C00';
$nav_color = '#ffffff';
break;
case ('black' || 'black-dark'):
$header_color = '#000000';
$link_dark_color = '#d4d2d2';
$link_light_color = '#454759';
$nav_color = '#ffffff';
break;
case ('purple' || 'purple-dark'):
$header_color = '#605ca8';
$link_dark_color = '#AC83FF';
$link_light_color = '#605ca8';
$nav_color = '#ffffff';
break;
case ('yellow' || 'yellow-dark') :
$header_color = '#FBCC34';
$link_dark_color = '#F0E68C';
$link_light_color = '#a69f08';
$nav_color = '#ffffff';
break;
case 'contrast':
$header_color = '#001F3F';
$link_dark_color = '#a6c9ed';
$link_light_color = '#2d4863';
$nav_color = '#ffffff';
break;
}
// Override the header color if the settings have one
if ($setting->header_color) {
$header_color = $setting->header_color;
}
DB::table('settings')->update([
'link_light_color' => $link_light_color,
'link_dark_color' => $link_dark_color,
'nav_link_color' => $nav_color,
'header_color' => $header_color]);
DB::table('users')->whereNull('skin')->update([
'link_light_color' => $link_light_color,
'link_dark_color' => $link_dark_color,
'nav_link_color' => $nav_color]);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('settings', function ($table) {
$table->dropColumn('link_dark_color');
$table->dropColumn('link_light_color');
$table->dropColumn('nav_link_color');
});
Schema::table('users', function ($table) {
$table->dropColumn('link_dark_color');
$table->dropColumn('link_light_color');
$table->dropColumn('nav_link_color');
});
}
};
-51
View File
@@ -1,51 +0,0 @@
<?php
// Snipe-IT Heroku Startup Script
// If DB_<value> values are set, ignore parser.
if (getenv("DB_DATABASE") || getenv("DB_HOST") || getenv("DB_USERNAME")) {
echo "Database Environment variables are manually set. Ignoring add-ins.";
} else if (getenv("CLEARDB_DATABASE_URL")) { // ClearDB Add-in
echo "Using ClearDB Heroku add-in." . PHP_EOL;
set_db(getenv('CLEARDB_DATABASE_URL'));
} else if (getenv("JAWSDB_MARIA_URL")) { // JawsDB Maria Add-in
echo "Using JawsDB Maria Heroku add-in." . PHP_EOL;
set_db(getenv("JAWSDB_MARIA_URL"));
} else if (getenv("JAWSDB_MYSQL_URL")) { // JawsDB MySQL Add-in
echo "Using JawsDB MySQL Heroku add-in." . PHP_EOL;
set_db(getenv("JAWSDB_MYSQL_URL"));
}
function set_db($uri) {
file_put_contents('./.env', 'DB_HOST=' . parse_url($uri, PHP_URL_HOST). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'DB_USERNAME=' . parse_url($uri, PHP_URL_USER). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'DB_PASSWORD=' . parse_url($uri, PHP_URL_PASS). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'DB_DATABASE=' . ltrim(parse_url($uri, PHP_URL_PATH), '/'). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'DB_PREFIX=' . 'null' . PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'DB_DUMP_PATH=' . 'null' . PHP_EOL, FILE_APPEND);
}
// If Heroku Redis is setup, let's get it working.
if (getenv("REDIS_URL")) { // Heroku Redis
echo "Setting up Heroku Redis." . PHP_EOL;
$url = getenv("REDIS_URL");
file_put_contents('./.env', 'REDIS_HOST=' . parse_url($url, PHP_URL_HOST). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'REDIS_PASSWORD=' . parse_url($url, PHP_URL_PASS). PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'REDIS_PORT=' . parse_url($url, PHP_URL_PORT). PHP_EOL, FILE_APPEND);
}
// Set up APP_TRUSTED_PROXIES to allow for the Heroku Router
// https://devcenter.heroku.com/articles/deploying-symfony4#trusting-the-heroku-router
file_put_contents('./.env', 'APP_TRUSTED_PROXIES=10.0.0.0/8' . PHP_EOL, FILE_APPEND);
// Set up GD
file_put_contents('./.env', 'IMAGE_LIB=gd' . PHP_EOL, FILE_APPEND);
// Set local FILESYSTEM_DISK and PUBLIC_FILESYSTEM_DISK
file_put_contents('./.env', 'FILESYSTEM_DISK=local' . PHP_EOL, FILE_APPEND);
file_put_contents('./.env', 'PUBLIC_FILESYSTEM_DISK=local_public' . PHP_EOL, FILE_APPEND);
// Set APP_CIPHER
file_put_contents('./.env', 'APP_CIPHER=AES-256-CBC' . PHP_EOL, FILE_APPEND);
?>
+4 -2
View File
@@ -42,7 +42,7 @@
"devDependencies": {
"all-contributors-cli": "^6.26.1",
"axios": "^1.11.0",
"jquery": "<3.6.0",
"jquery": "^3.7.1",
"laravel-mix": "^6.0.49",
"lodash": "^4.17.20",
"postcss": "^8.5.6",
@@ -6678,7 +6678,9 @@
}
},
"node_modules/jquery": {
"version": "3.5.1",
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"license": "MIT"
},
"node_modules/jquery-knob": {
+1 -1
View File
@@ -18,7 +18,7 @@
"devDependencies": {
"all-contributors-cli": "^6.26.1",
"axios": "^1.11.0",
"jquery": "<3.6.0",
"jquery": "^3.7.1",
"laravel-mix": "^6.0.49",
"lodash": "^4.17.20",
"postcss": "^8.5.6",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-6513
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-590
View File
@@ -1,590 +0,0 @@
/* iCheck plugin Minimal skin
----------------------------------- */
.icheckbox_minimal,
.iradio_minimal {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(minimal.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal {
background-position: 0 0;
}
.icheckbox_minimal.hover {
background-position: -20px 0;
}
.icheckbox_minimal.checked {
background-position: -40px 0;
}
.icheckbox_minimal.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal {
background-position: -100px 0;
}
.iradio_minimal.hover {
background-position: -120px 0;
}
.iradio_minimal.checked {
background-position: -140px 0;
}
.iradio_minimal.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal,
.iradio_minimal {
background-image: url(minimal@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* red */
.icheckbox_minimal-red,
.iradio_minimal-red {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(red.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-red {
background-position: 0 0;
}
.icheckbox_minimal-red.hover {
background-position: -20px 0;
}
.icheckbox_minimal-red.checked {
background-position: -40px 0;
}
.icheckbox_minimal-red.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-red.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-red {
background-position: -100px 0;
}
.iradio_minimal-red.hover {
background-position: -120px 0;
}
.iradio_minimal-red.checked {
background-position: -140px 0;
}
.iradio_minimal-red.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-red.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-red,
.iradio_minimal-red {
background-image: url(red@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* green */
.icheckbox_minimal-green,
.iradio_minimal-green {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(green.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-green {
background-position: 0 0;
}
.icheckbox_minimal-green.hover {
background-position: -20px 0;
}
.icheckbox_minimal-green.checked {
background-position: -40px 0;
}
.icheckbox_minimal-green.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-green.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-green {
background-position: -100px 0;
}
.iradio_minimal-green.hover {
background-position: -120px 0;
}
.iradio_minimal-green.checked {
background-position: -140px 0;
}
.iradio_minimal-green.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-green.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-green,
.iradio_minimal-green {
background-image: url(green@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* blue */
.icheckbox_minimal-blue,
.iradio_minimal-blue {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(blue.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-blue {
background-position: 0 0;
}
.icheckbox_minimal-blue.hover {
background-position: -20px 0;
}
.icheckbox_minimal-blue.checked {
background-position: -40px 0;
}
.icheckbox_minimal-blue.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-blue.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-blue {
background-position: -100px 0;
}
.iradio_minimal-blue.hover {
background-position: -120px 0;
}
.iradio_minimal-blue.checked {
background-position: -140px 0;
}
.iradio_minimal-blue.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-blue.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-blue,
.iradio_minimal-blue {
background-image: url(blue@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* aero */
.icheckbox_minimal-aero,
.iradio_minimal-aero {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(aero.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-aero {
background-position: 0 0;
}
.icheckbox_minimal-aero.hover {
background-position: -20px 0;
}
.icheckbox_minimal-aero.checked {
background-position: -40px 0;
}
.icheckbox_minimal-aero.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-aero.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-aero {
background-position: -100px 0;
}
.iradio_minimal-aero.hover {
background-position: -120px 0;
}
.iradio_minimal-aero.checked {
background-position: -140px 0;
}
.iradio_minimal-aero.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-aero.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-aero,
.iradio_minimal-aero {
background-image: url(aero@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* grey */
.icheckbox_minimal-grey,
.iradio_minimal-grey {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(grey.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-grey {
background-position: 0 0;
}
.icheckbox_minimal-grey.hover {
background-position: -20px 0;
}
.icheckbox_minimal-grey.checked {
background-position: -40px 0;
}
.icheckbox_minimal-grey.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-grey.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-grey {
background-position: -100px 0;
}
.iradio_minimal-grey.hover {
background-position: -120px 0;
}
.iradio_minimal-grey.checked {
background-position: -140px 0;
}
.iradio_minimal-grey.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-grey.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-grey,
.iradio_minimal-grey {
background-image: url(grey@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* orange */
.icheckbox_minimal-orange,
.iradio_minimal-orange {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(orange.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-orange {
background-position: 0 0;
}
.icheckbox_minimal-orange.hover {
background-position: -20px 0;
}
.icheckbox_minimal-orange.checked {
background-position: -40px 0;
}
.icheckbox_minimal-orange.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-orange.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-orange {
background-position: -100px 0;
}
.iradio_minimal-orange.hover {
background-position: -120px 0;
}
.iradio_minimal-orange.checked {
background-position: -140px 0;
}
.iradio_minimal-orange.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-orange.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-orange,
.iradio_minimal-orange {
background-image: url(orange@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* yellow */
.icheckbox_minimal-yellow,
.iradio_minimal-yellow {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(yellow.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-yellow {
background-position: 0 0;
}
.icheckbox_minimal-yellow.hover {
background-position: -20px 0;
}
.icheckbox_minimal-yellow.checked {
background-position: -40px 0;
}
.icheckbox_minimal-yellow.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-yellow.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-yellow {
background-position: -100px 0;
}
.iradio_minimal-yellow.hover {
background-position: -120px 0;
}
.iradio_minimal-yellow.checked {
background-position: -140px 0;
}
.iradio_minimal-yellow.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-yellow.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-yellow,
.iradio_minimal-yellow {
background-image: url(yellow@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* pink */
.icheckbox_minimal-pink,
.iradio_minimal-pink {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(pink.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-pink {
background-position: 0 0;
}
.icheckbox_minimal-pink.hover {
background-position: -20px 0;
}
.icheckbox_minimal-pink.checked {
background-position: -40px 0;
}
.icheckbox_minimal-pink.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-pink.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-pink {
background-position: -100px 0;
}
.iradio_minimal-pink.hover {
background-position: -120px 0;
}
.iradio_minimal-pink.checked {
background-position: -140px 0;
}
.iradio_minimal-pink.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-pink.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-pink,
.iradio_minimal-pink {
background-image: url(pink@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
/* purple */
.icheckbox_minimal-purple,
.iradio_minimal-purple {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(purple.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-purple {
background-position: 0 0;
}
.icheckbox_minimal-purple.hover {
background-position: -20px 0;
}
.icheckbox_minimal-purple.checked {
background-position: -40px 0;
}
.icheckbox_minimal-purple.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-purple.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-purple {
background-position: -100px 0;
}
.iradio_minimal-purple.hover {
background-position: -120px 0;
}
.iradio_minimal-purple.checked {
background-position: -140px 0;
}
.iradio_minimal-purple.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-purple.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-purple,
.iradio_minimal-purple {
background-image: url(purple@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, aero
----------------------------------- */
.icheckbox_minimal-aero,
.iradio_minimal-aero {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(aero.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-aero {
background-position: 0 0;
}
.icheckbox_minimal-aero.hover {
background-position: -20px 0;
}
.icheckbox_minimal-aero.checked {
background-position: -40px 0;
}
.icheckbox_minimal-aero.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-aero.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-aero {
background-position: -100px 0;
}
.iradio_minimal-aero.hover {
background-position: -120px 0;
}
.iradio_minimal-aero.checked {
background-position: -140px 0;
}
.iradio_minimal-aero.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-aero.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-aero,
.iradio_minimal-aero {
background-image: url(aero@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

+1 -1
View File
File diff suppressed because one or more lines are too long
-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, blue
----------------------------------- */
.icheckbox_minimal-blue,
.iradio_minimal-blue {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(blue.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-blue {
background-position: 0 0;
}
.icheckbox_minimal-blue.hover {
background-position: -20px 0;
}
.icheckbox_minimal-blue.checked {
background-position: -40px 0;
}
.icheckbox_minimal-blue.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-blue.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-blue {
background-position: -100px 0;
}
.iradio_minimal-blue.hover {
background-position: -120px 0;
}
.iradio_minimal-blue.checked {
background-position: -140px 0;
}
.iradio_minimal-blue.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-blue.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-blue,
.iradio_minimal-blue {
background-image: url(blue@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, green
----------------------------------- */
.icheckbox_minimal-green,
.iradio_minimal-green {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(green.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-green {
background-position: 0 0;
}
.icheckbox_minimal-green.hover {
background-position: -20px 0;
}
.icheckbox_minimal-green.checked {
background-position: -40px 0;
}
.icheckbox_minimal-green.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-green.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-green {
background-position: -100px 0;
}
.iradio_minimal-green.hover {
background-position: -120px 0;
}
.iradio_minimal-green.checked {
background-position: -140px 0;
}
.iradio_minimal-green.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-green.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-green,
.iradio_minimal-green {
background-image: url(green@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, grey
----------------------------------- */
.icheckbox_minimal-grey,
.iradio_minimal-grey {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(grey.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-grey {
background-position: 0 0;
}
.icheckbox_minimal-grey.hover {
background-position: -20px 0;
}
.icheckbox_minimal-grey.checked {
background-position: -40px 0;
}
.icheckbox_minimal-grey.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-grey.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-grey {
background-position: -100px 0;
}
.iradio_minimal-grey.hover {
background-position: -120px 0;
}
.iradio_minimal-grey.checked {
background-position: -140px 0;
}
.iradio_minimal-grey.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-grey.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-grey,
.iradio_minimal-grey {
background-image: url(grey@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, black
----------------------------------- */
.icheckbox_minimal,
.iradio_minimal {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(minimal.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal {
background-position: 0 0;
}
.icheckbox_minimal.hover {
background-position: -20px 0;
}
.icheckbox_minimal.checked {
background-position: -40px 0;
}
.icheckbox_minimal.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal {
background-position: -100px 0;
}
.iradio_minimal.hover {
background-position: -120px 0;
}
.iradio_minimal.checked {
background-position: -140px 0;
}
.iradio_minimal.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal,
.iradio_minimal {
background-image: url(minimal@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, orange
----------------------------------- */
.icheckbox_minimal-orange,
.iradio_minimal-orange {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(orange.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-orange {
background-position: 0 0;
}
.icheckbox_minimal-orange.hover {
background-position: -20px 0;
}
.icheckbox_minimal-orange.checked {
background-position: -40px 0;
}
.icheckbox_minimal-orange.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-orange.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-orange {
background-position: -100px 0;
}
.iradio_minimal-orange.hover {
background-position: -120px 0;
}
.iradio_minimal-orange.checked {
background-position: -140px 0;
}
.iradio_minimal-orange.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-orange.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-orange,
.iradio_minimal-orange {
background-image: url(orange@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, pink
----------------------------------- */
.icheckbox_minimal-pink,
.iradio_minimal-pink {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(pink.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-pink {
background-position: 0 0;
}
.icheckbox_minimal-pink.hover {
background-position: -20px 0;
}
.icheckbox_minimal-pink.checked {
background-position: -40px 0;
}
.icheckbox_minimal-pink.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-pink.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-pink {
background-position: -100px 0;
}
.iradio_minimal-pink.hover {
background-position: -120px 0;
}
.iradio_minimal-pink.checked {
background-position: -140px 0;
}
.iradio_minimal-pink.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-pink.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-pink,
.iradio_minimal-pink {
background-image: url(pink@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, purple
----------------------------------- */
.icheckbox_minimal-purple,
.iradio_minimal-purple {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(purple.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-purple {
background-position: 0 0;
}
.icheckbox_minimal-purple.hover {
background-position: -20px 0;
}
.icheckbox_minimal-purple.checked {
background-position: -40px 0;
}
.icheckbox_minimal-purple.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-purple.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-purple {
background-position: -100px 0;
}
.iradio_minimal-purple.hover {
background-position: -120px 0;
}
.iradio_minimal-purple.checked {
background-position: -140px 0;
}
.iradio_minimal-purple.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-purple.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-purple,
.iradio_minimal-purple {
background-image: url(purple@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-59
View File
@@ -1,59 +0,0 @@
/* iCheck plugin Minimal skin, red
----------------------------------- */
.icheckbox_minimal-red,
.iradio_minimal-red {
display: inline-block;
*display: inline;
vertical-align: middle;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
background: url(red.png) no-repeat;
border: none;
cursor: pointer;
}
.icheckbox_minimal-red {
background-position: 0 0;
}
.icheckbox_minimal-red.hover {
background-position: -20px 0;
}
.icheckbox_minimal-red.checked {
background-position: -40px 0;
}
.icheckbox_minimal-red.disabled {
background-position: -60px 0;
cursor: default;
}
.icheckbox_minimal-red.checked.disabled {
background-position: -80px 0;
}
.iradio_minimal-red {
background-position: -100px 0;
}
.iradio_minimal-red.hover {
background-position: -120px 0;
}
.iradio_minimal-red.checked {
background-position: -140px 0;
}
.iradio_minimal-red.disabled {
background-position: -160px 0;
cursor: default;
}
.iradio_minimal-red.checked.disabled {
background-position: -180px 0;
}
/* HiDPI support */
@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.icheckbox_minimal-red,
.iradio_minimal-red {
background-image: url(red@2x.png);
-webkit-background-size: 200px 20px;
background-size: 200px 20px;
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
.skin-black .main-header .navbar{background-color:#111}.skin-black .main-header .navbar .nav>li>a{color:#fff}.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav>.active>a,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-black .main-header .navbar .sidebar-toggle{color:#fff}.skin-black .main-header .navbar .sidebar-toggle:hover{background-color:#040404}@media (max-width:767px){.skin-black .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-black .main-header .navbar .dropdown-menu li a{color:#333}.skin-black .main-header .navbar .dropdown-menu li a:hover{background:#040404}}.skin-black .main-header li.user-header{background-color:#111}.skin-black .content-header{background:transparent}.skin-black .left-side,.skin-black .main-sidebar,.skin-black .wrapper{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{background:#1a2226;color:#4b646f}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li:hover>a{background:#1e282c;border-left-color:#111;color:#fff}.skin-black .sidebar-menu>li>.treeview-menu{background:#2c3b41;margin:0 1px}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .treeview-menu>li>a{color:#8aa4af}.skin-black .treeview-menu>li.active>a,.skin-black .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border:1px solid #374850;border-radius:3px;margin:10px}.skin-black .sidebar-form .btn,.skin-black .sidebar-form input[type=text]{background-color:#374850;border:1px solid transparent;box-shadow:none;height:35px;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type=text]{border-bottom-left-radius:2px;border-bottom-right-radius:0;border-top-left-radius:2px;border-top-right-radius:0;color:#666}.skin-black .sidebar-form input[type=text]:focus,.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{border-bottom-left-radius:0;border-bottom-right-radius:2px;border-top-left-radius:0;border-top-right-radius:2px;color:#999}.skin-black.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn,.btn:hover{color:#000}.btn .btn-primary:link,.btn.btn-primary,.btn:hover .btn-primary:link,.btn:hover.btn-primary{background-color:#505156;border-color:#fff;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#111;border-color:#1f1f21;color:#fff}.btn.btn-white:hover,.btn.btn-white:link,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:link,.btn:hover.btn-white:visited{color:#fff}a{color:var(--link)}a:hover{color:var(--hover-link)}a:visited{color:var(--visited-link)}.text-primary{color:#000}:root{--button-default:#000;--button-primary:#000;--button-hover:#000;--header:#111;--text-main:#bbb;--text-sub:#9b9b9b;--link:#black;--visited-link:#111;--hover-link:#999;--nav-link:#fff;--light-link:#fff}.btn-danger.btn-sm.disabled,a.btn-danger:link,a.btn-danger:visited,a.btn-info:link,a.btn-info:visited,a.btn-warning:link,a.btn-warning:visited{color:#fff}.far fa-life-ring{color:var(--link)}.sidebar-toggle-mobile{color:#fff!important}.skin-black .main-header .navbar .nav>li>a{text-decoration:none}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#111}.search-highlight,.search-highlight:hover{background-color:#e9d15b}
/*# sourceMappingURL=skin-black.css.map*/
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
.skin-black .main-header .navbar{background-color:#111}.skin-black .main-header .navbar .nav>li>a{color:#fff}.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav>.active>a,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-black .main-header .navbar .sidebar-toggle{color:#fff}.skin-black .main-header .navbar .sidebar-toggle:hover{background-color:#040404}@media (max-width:767px){.skin-black .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-black .main-header .navbar .dropdown-menu li a{color:#333}.skin-black .main-header .navbar .dropdown-menu li a:hover{background:#040404}}.skin-black .main-header li.user-header{background-color:#111}.skin-black .content-header{background:0 0}.skin-black .left-side,.skin-black .main-sidebar,.skin-black .wrapper{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{background:#1a2226;color:#4b646f}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li:hover>a{background:#1e282c;border-left-color:#111;color:#fff}.skin-black .sidebar-menu>li>.treeview-menu{background:#2c3b41;margin:0 1px}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .treeview-menu>li>a{color:#8aa4af}.skin-black .treeview-menu>li.active>a,.skin-black .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border:1px solid #374850;border-radius:3px;margin:10px}.skin-black .sidebar-form .btn,.skin-black .sidebar-form input[type=text]{background-color:#374850;border:1px solid transparent;box-shadow:none;height:35px;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type=text]{border-bottom-left-radius:2px;border-bottom-right-radius:0;border-top-left-radius:2px;border-top-right-radius:0;color:#666}.skin-black .sidebar-form input[type=text]:focus,.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{border-bottom-left-radius:0;border-bottom-right-radius:2px;border-top-left-radius:0;border-top-right-radius:2px;color:#999}.skin-black.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn,.btn:hover{color:#000}.btn .btn-primary:link,.btn.btn-primary,.btn:hover .btn-primary:link,.btn:hover.btn-primary{background-color:#505156;border-color:#fff;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#111;border-color:#1f1f21;color:#fff}.btn.btn-white:hover,.btn.btn-white:link,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:link,.btn:hover.btn-white:visited{color:#fff}a{color:var(--link)}a:hover{color:var(--hover-link)}a:visited{color:var(--visited-link)}.text-primary{color:#000}:root{--button-default:#000;--button-primary:#000;--button-hover:#000;--header:#111;--text-main:#bbb;--text-sub:#9b9b9b;--link:#black;--visited-link:#111;--hover-link:#999;--nav-link:#fff;--light-link:#fff}.btn-danger.btn-sm.disabled,a.btn-danger:link,a.btn-danger:visited,a.btn-info:link,a.btn-info:visited,a.btn-warning:link,a.btn-warning:visited{color:#fff}.far fa-life-ring{color:var(--link)}.sidebar-toggle-mobile{color:#fff!important}.skin-black .main-header .navbar .nav>li>a{text-decoration:none}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#111}.search-highlight,.search-highlight:hover{background-color:#e9d15b}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#333}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{background:#1a2226;color:#4b646f}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li:hover>a{background:#1e282c;border-left-color:#3c8dbc;color:#fff}.skin-blue .sidebar-menu>li>.treeview-menu{background:#2c3b41;margin:0 1px}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border:1px solid #374850;border-radius:3px;margin:10px}.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text]{background-color:#374850;border:1px solid transparent;box-shadow:none;height:35px;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type=text]{border-bottom-left-radius:2px;border-bottom-right-radius:0;border-top-left-radius:2px;border-top-right-radius:0;color:#666}.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{border-bottom-left-radius:0;border-bottom-right-radius:2px;border-top-left-radius:0;border-top-right-radius:2px;color:#999}.skin-blue.layout-top-nav .main-header>.logo .logo-variant{background-color:unset}.btn .btn-primary:link,.btn.btn-primary,.btn:hover .btn-primary:link,.btn:hover.btn-primary,btn-sm .btn-primary:link,btn-sm.btn-primary{background-color:#307095;border-color:#23536f;color:#fff!important}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover,btn-sma.btn-primary:hover{background-color:#23536f;border-color:#23536f;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link,btn-sm.btn-white:link{background-color:#307095;color:#fff}.btn.btn-white:hover,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:visited,btn-sm.btn-white:hover,btn-sm.btn-white:visited{background-color:#173648;color:#fff}.btn-danger,.btn-danger:link,.btn-danger:visited,.btn-warning,.btn-warning:link,.btn-warning:visited,a.btn-danger:hover,a.btn-warning:hover{color:#fff}.btn-default:link,.btn-default:visited,a.btn-default:hover{color:#505156}:root{--button-default:#505156;--button-primary:#1d455b;--button-hover:#173648;--header:#3c8dbc;--text-main:#bbb;--text-sub:#9b9b9b;--link:#296282;--visited-link:#5fa4cc;--hover-link:#86bad8;--nav-link:#fff;--light-link:#fff}a.btn-danger:link,a.btn-danger:visited,a.btn-info:link,a.btn-info:visited,a.btn-warning:link,a.btn-warning:visited{color:#fff}a:link{color:var(--link)}a:visited{color:var(--visited-link)}a:hover{color:var(--hover-link)}.text-primary{color:#23536f}.far fa-life-ring{color:var(--link)}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc}.search-highlight,.search-highlight:hover{background-color:#e9d15b}a.settings_button:hover,a.settings_button:link,a.settings_button:visited{color:#3c8dbc}a.label.label-default:link{color:#307095}a.label.label-default:visited{color:#23536f}a.label.label-default:hover{background-color:#bbb;color:#296282}
/*# sourceMappingURL=skin-blue.css.map*/
File diff suppressed because one or more lines are too long

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