Compare commits

..

311 Commits

Author SHA1 Message Date
snipe
b9e19468e5 Bumped version for v4.9.5 2020-09-15 18:43:19 -07:00
snipe
5b68a321a6 Added comapny formatter to asset maintenance report - fixes [ch15119] 2020-09-08 18:15:45 -07:00
snipe
37568ae9ec Merge pull request #8365 from snipe/fixes/8338_google_maps_CSP
Fixed #8338 - Added google maps to CSP
2020-08-25 20:49:37 -07:00
snipe
32ad9050cf Added google maps to CSP 2020-08-25 20:48:53 -07:00
snipe
01a832169c Merge pull request #8364 from snipe/fixes/8335_assigned_to_null_on_status_assetlist
Fixed #8335 - added assignedTo scope on status labels API call for assetlist
2020-08-25 20:38:31 -07:00
snipe
3c6883489c Added assignedTo scope 2020-08-25 20:37:30 -07:00
snipe
bcad49ce79 Try to better handle slack “too many requests” issue 2020-08-14 16:10:22 -07:00
snipe
b5acca89d7 Check for admin for slack notifications 2020-08-14 16:02:15 -07:00
snipe
e52919cf1b Merge pull request #8327 from snipe/features/checkin_license_from_all_users
Checkin license from all users cli tool
2020-08-14 15:35:15 -07:00
snipe
29f3a5c48f Use more verbose annotation for Auth::user if/else 2020-08-14 15:27:40 -07:00
snipe
134e8e6fb9 Moved user email nulling until after the save 2020-08-14 15:25:07 -07:00
Brady Wetherington
714576be45 Merge pull request #8328 from snipe/fix_deprecation_report
Fix deprecation report for customers with many active assets
2020-08-14 15:24:03 -07:00
Brady Wetherington
5128992940 Fix deprecation report for customers with many active assets 2020-08-14 15:03:03 -07:00
snipe
0291323502 Use the user as the target 2020-08-14 14:57:58 -07:00
snipe
e0f6f9b839 Artisan command to check in licenses from all users 2020-08-14 14:43:37 -07:00
snipe
f1a6308002 Check for Auth::user before trying to log id (for cli) 2020-08-14 14:43:07 -07:00
snipe
b999c50a2e Merge pull request #8316 from Godmartinz/bug/ch15028/missing-or-incorrect-error-message-translation
Looks great, thank you!
2020-08-12 12:37:47 -07:00
Godfrey M
e3906b245c added translation for admin/licenses/message.not_found 2020-08-12 12:27:18 -07:00
Brady Wetherington
9ca20e4964 Merge pull request #8313 from snipe/improve_ldap_search_error_reporting
Improve ldap search error reporting
2020-08-11 17:33:19 -07:00
Brady Wetherington
456a74d88c De-merge out incorrectly merged files. Whoops! 2020-08-11 16:41:20 -07:00
Brady Wetherington
799c059070 Add internationalized version of LDAP error message 2020-08-11 16:39:02 -07:00
Brady Wetherington
c62d43a778 Improve Exception management in Artisan LDAP Sync method. Still need to localize this better 2020-08-11 16:39:02 -07:00
Brady Wetherington
b725bd0fae Add @PeterUpfold as a contributor 2020-08-11 16:39:02 -07:00
Brady Wetherington
e0644dbbf6 Merge pull request #8105 from PeterUpfold/PeterUpfold-7661workaround
Propose workaround for #7661 — suppress E_DEPRECATED on ldap_control_paged_result()
2020-08-10 17:22:31 -07:00
snipe
5b6925b00c Removed debugging :( 2020-08-04 21:00:37 -07:00
snipe
df17a859bf Changed modal IDs so manager creation modal works on user creation main page 2020-08-04 20:59:54 -07:00
snipe
24c43056ba Moved pGenerator script to default layout footer
This fixes an issue where the password generator wouldn’t load in a modal in Chrome
2020-08-04 20:58:28 -07:00
snipe
606b7e905d Small edits to PR template
Slight text changes to ask specifics about versions
2020-07-31 17:02:33 -07:00
snipe
d73ddad477 Created a PR template
First draft of the PR guidelines template
2020-07-31 16:59:26 -07:00
snipe
9a39cf721e Merge pull request #8258 from ballertv/features/consumable-api
This looks great, thank you!
2020-07-31 12:18:49 -07:00
Brady Wetherington
7410b16835 Merge pull request #8270 from snipe/improve_ad_useraccountcontrol_v4
Add new useraccountcontrol value for valid AD users
2020-07-24 16:22:44 -07:00
andres
8994f3e15e cleanup 2020-07-22 19:57:06 -04:00
andres
d23f1a77ca implement checkout API 2020-07-22 19:56:31 -04:00
snipe
e955c983a3 Merge pull request #8250 from snipe/features/adds_addr_city_state_to_importer
Added address, city, state and country to importer and city to bulk editor
2020-07-22 13:43:29 -07:00
Brady Wetherington
b09e7d19b3 Add new useraccountcontrol value for valid AD users; document algorithm and values 2020-07-22 13:32:16 -07:00
snipe
2fa17ac185 Merge pull request #8254 from Godmartinz/gmartinez_adds_email_formats
Added firstinitial.lastname, lastname_firstinitial, firstnamelastname…
2020-07-22 12:06:31 -07:00
Godfrey Martinez
3b1e46f72b Update general.php 2020-07-22 11:25:57 -07:00
Godfrey Martinez
0c1a1de2a2 Update general.php
fixed typo
2020-07-22 11:24:36 -07:00
Godfrey M
20c9ae5818 Added firstinitial.lastname, lastname_firstinitial, firstnamelastname and firstnamelastinitial to username formats 2020-07-22 10:21:19 -07:00
snipe
eed41e4549 Moved address down further, fixed broken HTML 2020-07-21 16:57:32 -07:00
snipe
b750f4754f Added city to bulk user importer 2020-07-21 16:49:54 -07:00
snipe
c17a06792a Added address, city, state, country to user importer 2020-07-21 16:49:38 -07:00
snipe
4f76cc6cfb I don’t actually know what this file is for 2020-07-21 16:46:13 -07:00
snipe
b905154373 Fixed #8247 - added notes field to user details display 2020-07-20 14:29:32 -07:00
snipe
daf748e531 Bumped hash 2020-07-17 12:32:01 -07:00
snipe
799a93c46a Allow for email/username search on users 2020-07-17 12:11:32 -07:00
snipe
34aa12e229 Merge pull request #8239 from snipe/fixes/api_rtd_to_location_on_create
Set location_id to rtd_location_id on asset creation
2020-07-16 17:44:13 -07:00
snipe
897757bd04 Removed added line for location 2020-07-16 17:43:44 -07:00
snipe
c7125c3937 Set location_id to rtd_location_id on asset creation 2020-07-16 16:34:39 -07:00
snipe
81a6332889 Removed license ID from seats table cookie info
This typically wouldn’t be necessary, since most people would want to view the same *types* of data across licenses
2020-07-14 13:55:38 -07:00
snipe
6e563f6e4b Merge branch 'master' of https://github.com/snipe/snipe-it 2020-07-13 21:16:54 -07:00
snipe
5320f5c67c Disallow non-super users from editing their own permissions 2020-07-13 21:16:45 -07:00
snipe
7f69ae953b Merge pull request #8227 from snipe/fix_select2_ajax_pulldowns
Changes how we do AJAX calls via Select2 for dynamic drop-down menus
2020-07-13 21:16:00 -07:00
Brady Wetherington
17f6fbabfa Switch to 'items' to maintain compatbility with other internal API's 2020-07-13 21:12:03 -07:00
snipe
c79f8c1baf Merge pull request #8207 from EDVLeer/patch-1
Update snipeit.sh
2020-07-13 17:42:16 -07:00
Brady Wetherington
e7a820f7c9 Changes how we do AJAX calls via Select2 for dynamic drop-down menus 2020-07-13 17:14:31 -07:00
snipe
12c92e30b7 Show whether or not the user was imported via LDAP in the view page 2020-07-10 16:21:27 -07:00
snipe
fd10b755b0 Removed the sr-only tag in table headers
It was breaking Bootstrap Tables column selector :(
2020-07-10 11:30:01 -07:00
snipe
dbbb7680d9 A few more fixes for the cli
Do not check out a piece of software if it’s already been checked out to the user
2020-07-09 21:12:50 -07:00
snipe
cf0dd5bbad Small fixes for cli tool 2020-07-09 20:43:13 -07:00
snipe
25e53d8c7f Merge pull request #8216 from snipe/features/checkout_license_to_all_users
Added CLI tool to checkout license to all users
2020-07-09 20:27:01 -07:00
snipe
89d433b41a Removed duplicate seat call 2020-07-09 20:26:02 -07:00
snipe
e2570ada6f CLI tool to checkout a license to ALL users 2020-07-09 20:04:05 -07:00
snipe
45afe725a1 Only try to get the company if there is an auth’d user
(Needed for command line tools, where no Auth::user() is present)
2020-07-09 20:03:47 -07:00
EDVLeer
536401fe0f Update snipeit.sh
Ubuntu 20.04
2020-07-07 08:21:36 +02:00
snipe
ec6ed256fb Bumped minor version 2020-07-06 18:45:43 -07:00
snipe
2aaa7bed2d Merge pull request #8183 from snipe/features/merge_users
Added merge utility
2020-06-25 18:37:41 -07:00
snipe
cc9f1577a4 Removed unused use directives 2020-06-25 17:43:53 -07:00
snipe
ab1fe8be0c Added merge utility 2020-06-25 17:42:39 -07:00
snipe
339bdddc38 Fix for Vue js not loading due to CSP :( 2020-06-25 11:00:33 -07:00
snipe
35b9cf4b70 Fixed missing db prefix on scopeDueOrOverdueForAudit 2020-06-23 02:41:59 -07:00
snipe
7ccb41371e Removed unoptimized images directive
securityheaders.com is claiming it’s onrecognized, even though I got that directive from their site, so… whatever. ¯\_(ツ)_/¯
2020-06-23 01:09:39 -07:00
snipe
2e60a457bf Dumb fix for feature-policy being dumb. 2020-06-23 01:07:00 -07:00
snipe
2390d2160b Merge pull request #8164 from snipe/features/additional_security_headers
Additional security headers
2020-06-23 00:27:47 -07:00
snipe
00b051b8c7 Added a few more comments 2020-06-23 00:26:09 -07:00
snipe
05b3a9ad7e Config variable for HSTS 2020-06-22 23:17:27 -07:00
snipe
4fb880384f Changed comment 2020-06-22 22:37:14 -07:00
snipe
43042ad841 Consolidated ReferrerPolicy into new SecurityHeaders file 2020-06-22 22:35:59 -07:00
snipe
a716382ac4 Removed CSP middleware (it’s added in the general header) 2020-06-22 22:33:37 -07:00
snipe
36c8f7f4f1 Additional security headers 2020-06-22 22:31:01 -07:00
snipe
b42801f6ae Merge pull request #8163 from snipe/fixes/fix-for-css-on-column-selector
Fixed weird padlock display in asset listing with encrypted custom fields
2020-06-22 20:47:35 -07:00
snipe
946129f206 Made quote style consistent 2020-06-22 20:45:20 -07:00
snipe
b941ef1e08 Pulled CSS font awesome styles out of the blade and into overrides.css 2020-06-22 20:41:40 -07:00
snipe
d1aa11ec89 Fix for weird padlock display in asset listing with encrypted custom fields 2020-06-22 20:29:19 -07:00
snipe
de4934f21d Merge pull request #8162 from Godmartinz/godfreymartinez-ghi-font-size-of-qr_text
Fixed #8161 and #8114 - font-size for labels used static values in blade instead of using values from settings
2020-06-22 17:28:38 -07:00
Godfrey M
b10076b015 corrected an error where font-size for labels were static in settings. 2020-06-22 17:04:39 -07:00
snipe
af06e42056 Bumped version 2020-06-17 11:17:25 -07:00
snipe
9a2440dc4b Merge pull request #8141 from snipe/fixes/better_handling_when_license_is_invalid
Better handle the logic to determine if we should display the license checkout blade [ch13792]
2020-06-16 20:20:07 -07:00
snipe
2ac1c1636c Better handle the logic to determine if we should display the license checkout blade 2020-06-16 16:12:57 -07:00
Peter Upfold
004ecad059 Force suppress deprecation warning on ldap_control_paged_result() 2020-06-03 08:59:50 +01:00
snipe
beae8efb21 Merge pull request #8088 from Godmartinz/Label_Woes
Barcode resizing and text adjustment
2020-05-27 23:01:33 -07:00
Godfrey M
9839e5e566 adjusted for all label text, removed local variable 2020-05-27 12:27:40 -07:00
snipe
d14ab7e3e1 Porting change from #8053 to master
Signed-off-by: snipe <snipe@snipe.net>
2020-05-27 00:22:44 -07:00
Godfrey M
e7f74d94c1 Label_Woes 2020-05-26 17:22:45 -07:00
Godfrey M
e97cf011b6 Label_Woes 2020-05-26 17:15:39 -07:00
Godfrey M
ed23505054 Label_Woes 2020-05-26 17:10:45 -07:00
snipe
001e721530 Merge pull request #8063 from dmeltzer/backport-8092
BACKPORT: Fix Missing Category selection in Asset Model Modal dialog - [ch14635]
2020-05-20 10:21:52 -07:00
Daniel Meltzer
8210da6e82 Fix Missing Category selection in Asset Model Modal dialog.
A select html tag needs a full closing tag. is not valid. This was causing the select2 js to barf and eat additional information.
2020-05-20 10:29:27 -04:00
snipe
f88683766b Roll back previous change
Signed-off-by: snipe <snipe@snipe.net>
2020-05-14 00:55:47 -07:00
snipe
e4385c0f8c Fixes #8051 regression
Signed-off-by: snipe <snipe@snipe.net>
2020-05-14 00:48:30 -07:00
snipe
0550fe0ffa Fix for session fixation vulnerability
Signed-off-by: snipe <snipe@snipe.net>
2020-05-12 10:31:54 -07:00
snipe
7fb3a9b82c Merge pull request #8043 from snipe/features/backup-optional-in-import-and-ldap
Added option to disable backup in import
2020-05-11 22:41:36 -07:00
snipe
ecb1e87fe6 Updated assets
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 20:45:15 -07:00
snipe
f43df5f041 Fixed form label
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 20:44:46 -07:00
snipe
95cc48e422 Added option to disable backup in import
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 20:41:10 -07:00
snipe
9a2ed804ca Fixed mismatched HTML header tags
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 20:28:42 -07:00
snipe
d20fad28e5 Use more modern request helper
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 20:28:24 -07:00
snipe
ae813ddf75 Add @alek13 as a contributor 2020-05-11 18:11:16 -07:00
snipe
bb42109c0c Added a clarifying comment
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 18:10:45 -07:00
snipe
f46ecf8ec0 Updated composer lock
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 18:07:20 -07:00
snipe
b9e821c0e6 Small fix for Group Functional Tests
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 18:07:14 -07:00
snipe
9ee28c7513 Switched to use info instead of danger on undeployable statuses
Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 18:07:02 -07:00
snipe
1a8ba06702 Merge branch 'master' of https://github.com/snipe/snipe-it 2020-05-11 17:53:32 -07:00
snipe
0fd232e70d Fixed group functional test
(We had changed the minimum to 2 instead of 3)

Signed-off-by: snipe <snipe@snipe.net>
2020-05-11 17:53:24 -07:00
snipe
ee4d69b1c5 Merge pull request #8041 from alek13/patch-1
use supported package for slack
2020-05-11 17:52:45 -07:00
Alexander Chibrikin
d1ad111949 use supported package for slack
see https://github.com/maknz/slack/issues/94
2020-05-11 20:31:13 +03:00
snipe
31c5350941 Fixed incorrect route for groups edit
Signed-off-by: snipe <snipe@snipe.net>
2020-05-01 01:05:48 -07:00
snipe
7eb70e17e0 Merge pull request #7993 from snipe/fixes/7989_column_selector
Fixed #7989 - Converted table heading icons in People to CSS glyphs
2020-04-24 04:50:37 -07:00
snipe
3dfcb46991 Minor formatting changes
Signed-off-by: snipe <snipe@snipe.net>
2020-04-24 04:41:08 -07:00
snipe
96eb96f964 Removed stray val (typo)
Signed-off-by: snipe <snipe@snipe.net>
2020-04-24 04:27:00 -07:00
snipe
a2f08bd3ba Added comments
Signed-off-by: snipe <snipe@snipe.net>
2020-04-24 04:08:54 -07:00
snipe
e009fbe59f Converted table heading icons in People to CSS glyphs
Signed-off-by: snipe <snipe@snipe.net>
2020-04-24 04:04:53 -07:00
snipe
5bb4f271aa Fixed #7987 - allow toggle of required/optional in custom fields/fieldsets
Signed-off-by: snipe <snipe@snipe.net>
2020-04-24 00:47:19 -07:00
snipe
154db9a416 This literally never fucking worked. Ever. Shoot me. 2020-04-09 22:20:05 -07:00
snipe
cf9d0201e0 Catch weird edge case where target wouldn’t have an ID (?!) 2020-04-09 20:23:02 -07:00
snipe
7ebd21bc04 Added sr-only to the unstyles image upload button 2020-04-09 19:29:16 -07:00
snipe
5707df0239 Check that location isset in checkout 2020-04-09 18:32:34 -07:00
snipe
197a84be94 Commented out rtd_location_id override - why did we do that? 2020-04-09 14:17:39 -07:00
snipe
b4fa4c77d7 Check for rtd_location_id before trying to assign 2020-04-09 14:14:30 -07:00
snipe
cfec142c3b Better handle models without a fieldset in the asset request [RB 9935] 2020-04-09 11:18:54 -07:00
snipe
48dfc699d7 Bumped version 2020-04-09 10:49:50 -07:00
snipe
ec723a3da1 Changed LDAP messaging 2020-04-09 10:37:51 -07:00
snipe
f8a72db696 Changed LDAP 600 to 500, clearer error messages on LDAP test 2020-04-09 09:55:44 -07:00
snipe
83ee64f155 Added missing CSS class to text-only logo 2020-04-08 17:25:14 -07:00
snipe
b7d12ff944 Changed placeholder text back to lighter grey - it was confusing when darker 2020-04-08 17:24:59 -07:00
snipe
0858fec7f1 Fixed CSS issue where text-only logos at the top would be the wrong color 2020-04-08 17:24:38 -07:00
snipe
206bd675f2 Pulled slack validation out of setting model validation so it doesn’t fail mysteriously on other pages 2020-04-08 15:07:02 -07:00
snipe
92695782ff Add @anthonypburns as a contributor 2020-04-08 11:48:18 -07:00
snipe
c447e4d29b Add @joshi-redbridge as a contributor 2020-04-08 11:48:03 -07:00
snipe
811f89b1de Add @JoKneeMo as a contributor 2020-04-08 11:47:37 -07:00
snipe
be3e572440 Bumped version 2020-04-08 11:24:30 -07:00
snipe
824ebc19c0 Updated assets 2020-04-08 11:24:17 -07:00
snipe
a0f7fdc57a Merge branch 'fixes/accessibility_fixes'
# Conflicts:
#	public/css/build/all.css
#	public/css/dist/all.css
#	public/js/build/all.js
#	public/js/build/vue.js
#	public/js/build/vue.js.map
#	public/js/dist/all.js
#	public/mix-manifest.json
#	resources/assets/js/components/importer/importer-file.vue
2020-04-08 11:19:42 -07:00
snipe
450c1b9d56 Updated faker library to be compatible with PHP 7.4 2020-04-08 11:13:15 -07:00
snipe
79232fc434 Fixed #7947 - Added rtd_location_id to API search 2020-04-08 11:00:04 -07:00
snipe
0b3f511534 Fixed compact() errors 2020-04-07 17:26:56 -07:00
snipe
7f18983a49 Update local instance of composer.phar on upgrade. (#7940)
Co-authored-by: Johnny Moore <jmoore@eventide.com>
2020-04-07 13:21:55 -07:00
snipe
b7d9790acb Fixed color style for btn-sm.btn-warning and btn-sm.btn-danger 2020-04-06 21:59:30 -07:00
snipe
1a5785a8d3 Fixed dashboard header size 2020-04-06 21:30:28 -07:00
snipe
320d660e83 Updated chartjs 2020-04-06 21:17:26 -07:00
snipe
fb903b2fda Added comments, better indenting 2020-04-06 21:05:42 -07:00
snipe
c18646d096 Yellow highlight on selected rows 2020-04-06 21:00:11 -07:00
snipe
7bf398aca4 More settings filter fixes 2020-04-06 20:47:05 -07:00
snipe
f6bb655383 Prevent form submission 2020-04-06 20:17:44 -07:00
snipe
19f71face9 Added filtering to settings page 2020-04-06 20:11:13 -07:00
snipe
d82b94e281 Pull blue stylesheet if no skin was selected 2020-04-06 19:04:49 -07:00
snipe
893944403e Check for location_id being set before trying to set it on checkout via API 2020-04-06 15:54:40 -07:00
snipe
0d3c18d1df Fixed importer vue code for niceer layout 2020-04-06 15:09:37 -07:00
snipe
d7873f257d Fixed CSP for importer 2020-04-06 14:18:45 -07:00
Johnny Moore
7e3f718797 Update local instance of composer.phar on upgrade. 2020-04-03 13:13:30 -04:00
snipe
be79a1f3d6 Bumped hash 2020-04-02 19:27:41 -07:00
snipe
a8032ac388 Switched to btn-primary for submit 2020-04-02 18:38:54 -07:00
snipe
21d8225696 Use button primary for higher contrast 2020-04-02 18:33:26 -07:00
snipe
766c2b22cb Removed stray tag 2020-04-02 18:33:10 -07:00
snipe
db79f92423 Updatedb select2 to latest for aria fixes 2020-04-02 18:22:25 -07:00
snipe
bdddab5b8b Added role=“option” to option items, additional icon font fixes 2020-04-02 18:17:21 -07:00
snipe
031adc3be4 Small display fixes to importer table 2020-04-01 19:51:36 -07:00
snipe
e7c1418314 Fixed possible typo in CSP 2020-04-01 19:47:42 -07:00
snipe
c906026acd Merge branch 'master' into fixes/accessibility_fixes 2020-04-01 14:04:02 -07:00
snipe
56c2740b68 Radio button tweaks 2020-04-01 06:56:27 -07:00
snipe
4688d62b9f Small UI fixes 2020-04-01 06:50:37 -07:00
snipe
99686bd73a Added labels for bulk actions 2020-04-01 06:48:02 -07:00
snipe
120e224961 Add the ability to remove all group memberships in bulk 2020-04-01 06:35:13 -07:00
snipe
e27d69a31d Remove viewport restrictions 2020-04-01 06:18:37 -07:00
Ivan Nieto
c492ba7245 Adding changes to 'upload-table' for being painted in CSS (#7934) 2020-04-01 05:24:05 -07:00
snipe
53658e365f Removed duplicate HTML tag 2020-04-01 04:36:28 -07:00
snipe
4cfa0e36b1 Added labels to password reset 2020-04-01 04:30:54 -07:00
snipe
9d9b5d3885 Added form labels to auth screens 2020-04-01 04:29:54 -07:00
snipe
36f9905be0 Removed test code 2020-04-01 04:29:35 -07:00
snipe
a815e0ab8c Fixed stupid curly quotes 2020-04-01 03:55:44 -07:00
snipe
6bfec08a8c Updated vue files with aria tags and labels 2020-04-01 03:53:05 -07:00
snipe
2d2cd68061 Switched to h2 from h4 2020-04-01 03:51:08 -07:00
snipe
fd642e95eb Hide success icons from screen readers 2020-04-01 03:25:07 -07:00
snipe
9ab3370be5 Hide icons in error messages 2020-04-01 03:21:15 -07:00
snipe
4dcc1ffdbc More form labels 2020-04-01 02:22:24 -07:00
snipe
7d466f3584 Update user uploads for more data to work with recport 2020-04-01 02:22:16 -07:00
snipe
7718abaa72 Added aria-hidden 2020-04-01 01:26:44 -07:00
snipe
59c5a1ea87 Added aria label to form helper 2020-04-01 01:26:32 -07:00
snipe
0cf70c9e16 Fixed select2 placeholder 2020-04-01 01:26:19 -07:00
snipe
6174f9b93f Check that there is actually a filed ID submitted 2020-04-01 01:25:31 -07:00
snipe
c3d2e8ff26 Added aria-hidden to inline form error text 2020-04-01 00:18:20 -07:00
snipe
192f703885 Trying placeholder CSS styling for the high contrast skin
Doesn’t seem to work :(
2020-04-01 00:15:58 -07:00
snipe
be93b23488 Added aria-label to form fields 2020-04-01 00:15:33 -07:00
snipe
b079d0d6d5 Use btn-primary for submit button 2020-03-31 23:38:10 -07:00
snipe
c6c75cc11f Fixed missing </h3 tags 2020-03-31 23:36:56 -07:00
snipe
b188285bc9 Fixed empty table headings 2020-03-31 23:02:24 -07:00
snipe
6d659a84b8 Removed extra div tag 2020-03-31 22:53:58 -07:00
snipe
3e3828229d Removed _tab from tab names 2020-03-31 22:52:21 -07:00
snipe
bf6a0f8d2f Added aria-hidden 2020-03-31 22:50:38 -07:00
snipe
3873c4b253 Added aria form labels to upload modal 2020-03-31 22:50:29 -07:00
snipe
6cc23f69f9 Added aria form labels 2020-03-31 22:50:14 -07:00
snipe
a467a6999e Use upload modal 2020-03-31 22:50:07 -07:00
snipe
e0eb10ca1e Added aria-hidden tags 2020-03-31 22:49:49 -07:00
snipe
99c4c73c09 Fixed aria labels on dropdowns 2020-03-31 21:53:26 -07:00
snipe
bde45cbb34 Fixed h3 to h2 for semantic header levels 2020-03-31 21:30:24 -07:00
snipe
c408c27bf4 Updated dark skins 2020-03-31 21:10:55 -07:00
Joshi
b14f37d966 Purposal for fix on issue 6251 - max upload filesize (#7930)
* Added a check and sed to startup.sh for managing max upload size inside the docker container. Issue encountered in 6251

* Changed startup.sh to bash instead of sh to have better variable control. Changed single ticks to double ticks for the sed so that the variable PHP_UPLOAD_LIMIT is expanded
2020-03-31 21:02:10 -07:00
snipe
bfa9c0c528 Updated dark skins with updated styles 2020-03-31 20:58:54 -07:00
snipe
9cc9cddd68 Fixed visited button text color 2020-03-31 20:39:41 -07:00
snipe
fe2261c88d A few more aria-hidden fixes 2020-03-31 19:34:01 -07:00
snipe
6aeb3c0a47 Use divs instead of layout table on view 2020-03-31 19:27:21 -07:00
snipe
dfaa1c9578 Use the same “no results” treatment for each tab 2020-03-31 18:45:43 -07:00
snipe
0ef1dfe061 Switched from layout tables to decorated divs for asset detail page 2020-03-31 18:39:58 -07:00
snipe
ba8bcd6413 Slight tweaks to div table styles 2020-03-31 18:39:41 -07:00
snipe
7854003ec2 Added sr-only text to “made with love” footer 2020-03-31 17:29:20 -07:00
snipe
c71dd9b68a Added sr-only text 2020-03-31 17:29:06 -07:00
snipe
dfeabbc85d Added table-styles div CSS
(Still needs to be applied for dark mode skins)
2020-03-31 17:28:40 -07:00
snipe
b8b9ac8a1b Fixed mismatched <b>/<strong> tags 2020-03-28 18:00:46 -07:00
snipe
80ac2607cd Added alt tags to assigned assets view 2020-03-28 18:00:19 -07:00
snipe
3552fb1fd8 Added alt tags to profile image 2020-03-28 18:00:05 -07:00
snipe
54a96b8453 Fixed <b> to <strong> 2020-03-28 17:30:17 -07:00
snipe
03be4e74df Higher contrast pagination 2020-03-28 04:00:46 -07:00
snipe
e9ddd1af81 Added th content on activity report 2020-03-28 04:00:26 -07:00
snipe
f305885e8e Regenerated assets 2020-03-28 03:43:35 -07:00
snipe
f0b9cd7820 Changed header for h2 2020-03-28 03:43:24 -07:00
snipe
59accca89d Added form labelss 2020-03-28 03:43:08 -07:00
snipe
e72ebfb94b Added placeholder text for modal header 2020-03-28 03:42:45 -07:00
snipe
0b7316d548 Changed side heading to h2 2020-03-28 03:42:27 -07:00
snipe
d0cf76989a Fixed table headers for custom fields 2020-03-28 02:52:14 -07:00
snipe
90a2bf7c9c Regenerated high contrast menu 2020-03-28 02:42:34 -07:00
snipe
95945412b1 Fixed label 2020-03-28 02:42:04 -07:00
snipe
c299efca0c Darkened the home screen boxes for high contrast 2020-03-28 02:41:43 -07:00
snipe
5e4918579a Added table headers 2020-03-28 02:41:27 -07:00
snipe
db75f0e894 Regenerated assets 2020-03-28 01:38:01 -07:00
snipe
5a6c13e364 Added aria-hidden and sr-only tags to minus/collapse symbol 2020-03-28 01:37:49 -07:00
snipe
4b22f07dd7 Fixed less attribute names 2020-03-28 01:37:27 -07:00
snipe
57cb5146fc Just use navy for links 2020-03-28 01:37:13 -07:00
snipe
07708f530e Use label for field in column dropdown 2020-03-28 01:36:55 -07:00
snipe
5c68353e62 Use non-min bs tables for now 2020-03-28 01:36:35 -07:00
snipe
53728e5c71 Removed whitespace 2020-03-28 01:36:22 -07:00
snipe
b965d170ab Added accessibility features to bootstrap tables 2020-03-28 00:51:26 -07:00
snipe
34a1bb7152 Fixed contrast skin 2020-03-28 00:24:37 -07:00
snipe
8787f228d9 Fixed asset build 2020-03-28 00:16:28 -07:00
snipe
03cde9a72c Added less processing for skin files 2020-03-28 00:08:21 -07:00
snipe
623655b6f6 New dark background skins 2020-03-28 00:07:34 -07:00
snipe
da6830225a Remove light colored less files - it looks crappy 2020-03-28 00:06:51 -07:00
snipe
a729410fe8 Regenerated JS 2020-03-27 22:03:30 -07:00
snipe
bba4036e53 Updated skin theme 2020-03-27 22:03:09 -07:00
snipe
39c71c6027 Regenerate new skin css files 2020-03-27 22:02:57 -07:00
snipe
03a9219a7c Fixed duplicate color: attribute 2020-03-27 22:01:43 -07:00
snipe
a8f6bbd86a Added alt text to image formatter in bootstrap tables 2020-03-27 22:01:24 -07:00
snipe
9a2ee2638b Updated color-specific skins 2020-03-27 21:46:00 -07:00
snipe
2a813244a2 Wrapped text in h5s, added aria hidden to icons 2020-03-27 21:45:27 -07:00
snipe
b50894fca1 Use a variable in the settings to determine what css classes to use 2020-03-27 21:44:50 -07:00
snipe
41fa2d1aa1 Added aria handlers and sr-only text 2020-03-27 21:44:32 -07:00
snipe
54d39c04ad Added additional themes to dropdown 2020-03-27 21:44:18 -07:00
snipe
3c1365b2c8 Added color variables 2020-03-27 21:44:00 -07:00
snipe
5858c90e71 Minor formatting change (spacing) 2020-03-27 15:50:01 -07:00
snipe
f0ef06ebe1 Added more alt tags 2020-03-27 15:35:29 -07:00
snipe
700f7de748 Added alt text to logo 2020-03-27 14:10:56 -07:00
snipe
af2ea7ac03 Added aria-hidden="true" to create dropdown in topnav
Since it’s technically duplicated content
2020-03-27 14:08:01 -07:00
snipe
690d8255c9 Removed the title attribute from the select2 output
It contains duplicate info since its auto-generated by select2
2020-03-27 14:07:30 -07:00
snipe
aded2193a2 Added aria-hidden="true" to top navigation elements 2020-03-27 13:29:00 -07:00
snipe
6d99b2a68c Added “skip to content” link 2020-03-27 13:28:48 -07:00
snipe
55a619778f Added language header 2020-03-25 16:32:33 -07:00
snipe
6066c249d5 Moved gate to the top of the method 2020-03-06 16:01:13 -08:00
Ivan Nieto
025ea93f05 Fix for when a user with the correct permissions couldn't update Manufacturers. (#7882)
* Changed the ability name from 'edit' to 'update'. Changed the order of execution: first checks if the manufacturer exists, then checks permissions

* Handles the update method, that also has the ability parameter as edit instead of update"
q

* Revert "Handles the update method, that also has the ability parameter as edit instead of update""

This reverts commit d7dc0e451e.

* Handles the update method, that also has the ability parameter as 'edit' instead of 'update'
2020-03-06 15:59:51 -08:00
snipe
54fd8f81ff Added permissions on user api (#7883)
* Add permissions to user edit API

* Add user permissions on user create/update API endpoint
2020-03-06 15:28:46 -08:00
snipe
ca43554327 Fixes search by serial or tag even if they have slashes in them (#7879)
* Fixes search by serial or tag even if they have slashes in them

* Added support for url param byTag and bySerial

* Fixed typo comments

* Sojme additional comments to clarify use-cases

* Updated comments for clarity
2020-03-06 14:55:20 -08:00
snipe
61bdb88ba5 Add @ColinMcNeil as a contributor 2020-03-04 22:38:09 -08:00
snipe
36696ab56e Add @bigtreeEdo as a contributor 2020-03-04 22:37:57 -08:00
snipe
f0f9b93652 Add @Godmartinz as a contributor 2020-03-04 22:37:45 -08:00
snipe
a2fae76eaf Bumped version 2020-03-04 22:37:17 -08:00
snipe
8b2f8ef3cb Spelling is hard :( 2020-03-04 22:19:59 -08:00
snipe
5307e57bd9 Fix for CVE-2019-10772
Vuln in SVG sanitizer library
2020-03-04 22:15:31 -08:00
snipe
15518852aa Added validation to reject email addresses over 250 characters 2020-03-04 22:08:07 -08:00
snipe
60fc1d3f6d Added/matched forgotten password strings in lang files 2020-03-04 22:07:35 -08:00
snipe
d1a8d76d85 Set maxlength in password reset form to 250 2020-03-04 22:06:43 -08:00
snipe
803f5ad0ab Fixed #7870: fixed SSL connectivity for PaaS DBs (#7874) 2020-03-04 19:39:23 -08:00
Godfrey Martinez
0e0fe967e4 BadMethodCallException Method update does [ch10544] (#7804) 2020-02-10 19:27:23 -08:00
snipe
192917cc84 Slightly better fix for requestable import bug 2020-02-10 17:34:32 -08:00
snipe
81880645ed Possible requestable fix 2020-02-10 11:40:39 -08:00
snipe
9eb4b0dda7 Disallow 0 as a number for labels per page 2020-02-04 19:14:58 -08:00
snipe
2f0ed129f0 Use “invalid barcode” image and suppress errors when barcode format is wrong 2020-02-04 18:15:01 -08:00
snipe
3361b859c0 Changes offset to use the actual item count as override instead of 0 (#7788) 2020-02-04 12:32:24 -08:00
bigtreeEdo
e27a9b137b added 'requestable' to fillable attributes. (#7787) 2020-02-03 19:37:03 -08:00
snipe
89e2a3ae3c Fixed #7752 - reformat /api/v1/users/me to use transformer 2020-01-30 13:12:43 -08:00
snipe
5f85d8132b Fix for weird JSON parsing in actionlogs (#7753)
* Fix for weird JSON parsing in actionlogs

* Removed debugging code

* Check for the meta array

(If no fields, no array)
2020-01-24 17:31:43 -08:00
snipe
ca1285ec08 Updated favicon 2020-01-23 19:49:46 -08:00
Ivan Nieto
75bf8f3d58 Remove not existent variable 'id' in the redirect causing [ch10602] (#7732) 2020-01-17 16:12:24 -08:00
snipe
324da7c0c8 Include correct license, asset, etc count on user show API call 2019-12-19 18:09:53 -08:00
snipe
779fc6d195 Added license endpoint for users 2019-12-19 18:00:36 -08:00
Colin McNeil
db59106c3e Move ldap import ini settings to config (#7679) 2019-12-19 11:51:55 -08:00
snipe
88fb1370f0 Added slightly friendlier error handling for assets without models
This scenario should never happen, barring someone manually editing their data, but better to handle that scenario in a more user-friendly way.
2019-12-06 18:17:03 -08:00
snipe
943cf40247 Merge branch 'master' of https://github.com/snipe/snipe-it 2019-12-06 13:14:31 -08:00
snipe
ff57f10e9f Fix for searching on child location names (#7646)
* Fix for child locations

* Reverts temp changes to indenter
2019-12-06 13:14:10 -08:00
snipe
91bb76fd8a Bumped version 2019-12-06 13:05:20 -08:00
snipe
893454dca7 Updated translations 2019-12-06 12:03:04 -08:00
snipe
de0b5a6149 Fixes #6440 - quote marks in the right place 2019-12-06 11:04:16 -08:00
Dustin B
8fd4e35244 Closes #6440 Print All Assigned - New Tab (#7135)
Should add the functionality to, by default open in a new tab and not reference back to the source page. Reduces overhead and should resolve #6440. 

Untested, need confirmation.
2019-12-06 11:00:01 -08:00
snipe
e71e57f16a Fixed XSS vulnerability in SVG image uploads [ch10476] (#7639)
* Added enshrined/svg-sanitize

* Added modular image resizing/SVG cleaning method

(This already exists in v5, so I mostly ported it forward and added the SVG sanitizer.)

* Use improved handleImages method to upload/resize/clean images

* Removed $old_image

This is handled in the ImageUpload request now
2019-12-05 22:23:05 -08:00
snipe
3f5840d390 Bumped vendor files 2019-12-05 19:53:01 -08:00
dependabot[bot]
d3f4205f09 Bump symfony/http-foundation from 3.4.30 to 3.4.36 (#7638)
Bumps [symfony/http-foundation](https://github.com/symfony/http-foundation) from 3.4.30 to 3.4.36.
- [Release notes](https://github.com/symfony/http-foundation/releases)
- [Changelog](https://github.com/symfony/http-foundation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/symfony/http-foundation/compare/v3.4.30...v3.4.36)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-05 19:37:00 -08:00
Godfrey Martinez
5b946087c4 added a proper response for password errors (#7636) 2019-12-05 17:49:56 -08:00
snipe
ff8d98c97c Update child assets to reflect asset parent location (#7458) 2019-12-04 16:19:25 -08:00
snipe
2fbbe430b5 Removed escaping on custom fields in presenter (#7631) 2019-12-03 17:42:13 -08:00
Godfrey Martinez
f0af750b0a Fixed comment (#7617)
* Set theme jekyll-theme-hacker

* fixed commenty about scopebyDeprecationID being identified as a method to location ID

* fixed commenty about scopebyDeprecationID being identified as a method to location ID
2019-11-22 16:13:42 -08:00
snipe
88cf456386 Adding Dept to license seats (#7609)
* Adding Dept to license seats

* Added query scope to order by department

* Make license seat department sortable

* Disable license seat internal search - this never actually worked
2019-11-21 22:03:56 -08:00
snipe
d8049209ca Fixed bug where deleted consumable would throw an error on print page 2019-11-21 21:43:54 -08:00
snipe
dd40ddf5a5 Fixed an error on audit due list when no audit_warning_days had been set [ch9764] 2019-11-21 21:34:41 -08:00
snipe
a73fd24695 Fix maintenances permissions check to allow users who can edit assets to edit maintenances 2019-11-08 17:02:17 -08:00
652 changed files with 32187 additions and 11450 deletions

View File

@@ -1668,6 +1668,69 @@
"contributions": [
"code"
]
},
{
"login": "Godmartinz",
"name": "Godfrey Martinez",
"avatar_url": "https://avatars2.githubusercontent.com/u/47435081?v=4",
"profile": "https://github.com/Godmartinz",
"contributions": [
"code"
]
},
{
"login": "bigtreeEdo",
"name": "bigtreeEdo",
"avatar_url": "https://avatars1.githubusercontent.com/u/2075128?v=4",
"profile": "https://github.com/bigtreeEdo",
"contributions": [
"code"
]
},
{
"login": "ColinMcNeil",
"name": "Colin McNeil",
"avatar_url": "https://avatars0.githubusercontent.com/u/5000430?v=4",
"profile": "https://colinmcneil.me/",
"contributions": [
"code"
]
},
{
"login": "JoKneeMo",
"name": "JoKneeMo",
"avatar_url": "https://avatars0.githubusercontent.com/u/421625?v=4",
"profile": "https://github.com/JoKneeMo",
"contributions": [
"code"
]
},
{
"login": "joshi-redbridge",
"name": "Joshi",
"avatar_url": "https://avatars0.githubusercontent.com/u/54849013?v=4",
"profile": "http://www.redbridge.se",
"contributions": [
"code"
]
},
{
"login": "anthonypburns",
"name": "Anthony Burns",
"avatar_url": "https://avatars2.githubusercontent.com/u/15731458?v=4",
"profile": "https://github.com/anthonypburns",
"contributions": [
"code"
]
},
{
"login": "alek13",
"name": "Alexander Chibrikin",
"avatar_url": "https://avatars2.githubusercontent.com/u/1972329?v=4",
"profile": "http://phpprofi.ru/",
"contributions": [
"code"
]
}
]
}

View File

@@ -26,6 +26,7 @@ DB_COLLATION=utf8mb4_unicode_ci
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
DB_SSL=false
DB_SSL_IS_PAAS=false
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
@@ -70,6 +71,7 @@ ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
@@ -116,3 +118,5 @@ FILESYSTEM_DISK=local
APP_CIPHER=AES-256-CBC
GOOGLE_MAPS_API=
BACKUP_ENV=true
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600

40
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,40 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B
**Test Configuration**:
* PHP version:
* MySQL version
* Webserver version
* OS version
# Checklist:
- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
v6.11.3

View File

@@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=master)](https://travis-ci.org/snipe/snipe-it) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-182-orange.svg?style=flat-square)](#contributors) [![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
[![All Contributors](https://img.shields.io/badge/all_contributors-189-orange.svg?style=flat-square)](#contributors) [![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
## Snipe-IT - Open Source Asset Management System
@@ -106,6 +106,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") | [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars2.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

0
_config.yml Normal file
View File

View File

@@ -0,0 +1,95 @@
<?php
namespace App\Console\Commands;
use App\Models\LicenseSeat;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\License;
use Illuminate\Database\Eloquent\Model;
class CheckinLicensesFromAllUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:checkin-from-all {--license_id=} {--notify}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Checks in licenses from all users';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$license_id = $this->option('license_id');
$notify = $this->option('notify');
if (!$license_id) {
$this->error('ERROR: License ID is required.');
return false;
}
if (!$license = License::where('id','=',$license_id)->first()) {
$this->error('Invalid license ID');
return false;
}
$this->info('Checking in ALL seats for '.$license->name);
$licenseSeats = LicenseSeat::where('license_id', '=', $license_id)
->whereNotNull('assigned_to')
->with('user')
->get();
$this->info(' There are ' .$licenseSeats->count(). ' seats checked out: ');
if (!$notify) {
$this->info('No mail will be sent.');
}
foreach ($licenseSeats as $seat) {
$this->info($seat->user->username .' has a license seat for '.$license->name);
$seat->assigned_to = null;
if ($seat->save()) {
// Override the email address so we don't notify on checkin
if (!$notify) {
$seat->user->email = null;
}
// Log the checkin
$seat->logCheckin($seat->user, 'Checked in via cli tool');
}
}
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace App\Console\Commands;
use App\Models\LicenseSeat;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\License;
use Illuminate\Database\Eloquent\Model;
class CheckoutLicenseToAllUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:checkout-to-all {--license_id=} {--notify}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$license_id = $this->option('license_id');
$notify = $this->option('notify');
if (!$license_id) {
$this->error('ERROR: License ID is required.');
return false;
}
if (!$license = License::where('id','=',$license_id)->with('assignedusers')->first()) {
$this->error('Invalid license ID');
return false;
}
$users = User::whereNull('deleted_at')->with('licenses')->get();
if ($users->count() > $license->getAvailSeatsCountAttribute()) {
$this->info('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
}
$this->info('Checking out '.$users->count().' of '.$license->getAvailSeatsCountAttribute().' seats for '.$license->name);
if (!$notify) {
$this->info('No mail will be sent.');
}
foreach ($users as $user) {
// Check to make sure this user doesn't already have this license checked out
// to them
if ($user->licenses->where('id', '=', $license_id)->count()) {
$this->info($user->username .' already has this license checked out to them. Skipping... ');
continue;
}
// If the license is valid, check that there is an available seat
if ($license->availCount()->count() < 1) {
$this->error('ERROR: No available seats');
return false;
}
$this->info($license->availCount()->count().' seats left');
// Get the seat ID
$licenseSeat = $license->freeSeat();
// Update the seat with checkout info,
$licenseSeat->assigned_to = $user->id;
if ($licenseSeat->save()) {
// Temporarily null the user's email address so we don't send mail if we're not supposed to
if (!$notify) {
$user->email = null;
}
// Log the checkout
$licenseSeat->logCheckout('Checked out via cli tool', $user);
$this->info('License '.$license_id.' seat '.$licenseSeat->id.' checked out to '.$user->username);
}
}
}
}

View File

@@ -42,9 +42,8 @@ class LdapSync extends Command
*/
public function handle()
{
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
@@ -125,7 +124,16 @@ class LdapSync extends Command
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
try {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
} catch (\Exception $e) { // FIXME: this is stolen from line 77 or so above
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => trans('admin/users/message.error.ldap_could_not_search')." Location: ".$ldap_loc['name']." (ID: ".$ldap_loc['id'].") cannot connect to \"".$ldap_loc["ldap_ou"]."\" - ".$e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
@@ -188,8 +196,33 @@ class LdapSync extends Command
// Sync activated state for Active Directory.
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
/* The following is _probably_ the correct logic, but we can't use it because
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224', '4260352'
'512', // 0x200 NORMAL_ACCOUNT
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'4260352',// 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
'1049088',// 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
];
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
}
@@ -240,7 +273,7 @@ class LdapSync extends Command
}
}
} else if ($this->option('json_summary')) {
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ];
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ]; // hardcoding the error to false and the error_message to blank seems a bit weird
$this->info(json_encode($json_summary));
} else {
return $summary;

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\User;
use Carbon\Carbon;
class MergeUsersByUsername extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:merge-users';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows you to merge the history of users. It looks for users without an email address as their username and merges them into the version that does have an email username.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Get the list of users who have an email address as their username
$users = User::where('username', 'LIKE', '%@%')->whereNull('deleted_at')->get();
foreach ($users as $user) {
$parts = explode("@", $user->username);
$bad_users = User::where('username', '=', $parts[0])->whereNull('deleted_at')->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations')->get();
foreach ($bad_users as $bad_user) {
$this->info($bad_user->username.' ('.$bad_user->id.') will be merged into '.$user->username.' ('.$user->id.') ');
// Walk the list of assets
foreach ($bad_user->assets as $asset) {
$this->info( 'Updating asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
$asset->assigned_to = $user->id;
$asset->save();
}
// Walk the list of licenses
foreach ($bad_user->licenses as $license) {
$this->info( 'Updating license '.$license->name.' '.$license->id.' to user '.$user->id);
$bad_user->licenses()->updateExistingPivot($license->id, ['assigned_to' => $user->id]);
}
// Walk the list of consumables
foreach ($bad_user->consumables as $consumable) {
$this->info( 'Updating consumable '.$consumable->id.' to user '.$user->id);
$bad_user->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $user->id]);
}
// Walk the list of accessories
foreach ($bad_user->accessories as $accessory) {
$this->info( 'Updating accessory '.$accessory->id.' to user '.$user->id);
$bad_user->accessories()->updateExistingPivot($accessory->id, ['assigned_to' => $user->id]);
}
// Walk the list of logs
foreach ($bad_user->userlog as $log) {
$this->info( 'Updating action log record '.$log->id.' to user '.$user->id);
$log->target_id = $user->id;
$log->save();
}
// Update any manager IDs
$this->info( 'Updating managed user records to user '.$user->id);
User::where('manager_id', '=', $bad_user->id)->update(['manager_id' => $user->id]);
// Update location manager IDs
foreach ($bad_user->managedLocations as $managedLocation) {
$this->info( 'Updating managed location record '.$managedLocation->name.' to manager '.$user->id);
$managedLocation->manager_id = $user->id;
$managedLocation->save();
}
// Mark the user as deleted
$this->info( 'Marking the user as deleted');
$bad_user->deleted_at = Carbon::now()->timestamp;
$bad_user->save();
}
}
}
}

View File

@@ -85,26 +85,7 @@ class AccessoriesController extends Controller
$accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id');
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 800, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
$accessory->image = $file_name;
}
}
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
// Was the accessory created?
@@ -165,28 +146,7 @@ class AccessoriesController extends Controller
$accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id');
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 800, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) {
unlink($path.'/'.$accessory->image);
}
$accessory->image = $file_name;
}
}
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
// Was the accessory updated?
@@ -238,7 +198,7 @@ class AccessoriesController extends Controller
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', compact('id')));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**

View File

@@ -49,7 +49,9 @@ class AccessoriesController extends Controller
$accessories->where('supplier_id','=',$request->input('supplier_id'));
}
$offset = (($accessories) && (request('offset') > $accessories->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -44,7 +44,9 @@ class AssetMaintenancesController extends Controller
$maintenances->where('asset_id', '=', $request->input('asset_id'));
}
$offset = (($maintenances) && (request('offset') > $maintenances->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($maintenances) && ($request->get('offset') > $maintenances->count())) ? $maintenances->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -60,7 +60,9 @@ class AssetModelsController extends Controller
$assetmodels->TextSearch($request->input('search'));
}
$offset = (($assetmodels) && (request('offset') > $assetmodels->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($assetmodels) && ($request->get('offset') > $assetmodels->count())) ? $assetmodels->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -121,6 +121,10 @@ class AssetsController extends Controller
$assets->where('assets.location_id', '=', $request->input('location_id'));
}
if ($request->filled('rtd_location_id')) {
$assets->where('assets.rtd_location_id', '=', $request->input('rtd_location_id'));
}
if ($request->filled('supplier_id')) {
$assets->where('assets.supplier_id', '=', $request->input('supplier_id'));
}
@@ -144,7 +148,11 @@ class AssetsController extends Controller
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
$offset = (($assets) && (request('offset') > $assets->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($assets) && ($request->get('offset') > $assets->count())) ? $assets->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -445,6 +453,7 @@ class AssetsController extends Controller
$asset->supplier_id = $request->get('supplier_id', 0);
$asset->requestable = $request->get('requestable', 0);
$asset->rtd_location_id = $request->get('rtd_location_id', null);
$asset->location_id = $request->get('rtd_location_id', null);
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
@@ -525,6 +534,10 @@ class AssetsController extends Controller
$location = $target->location_id;
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
$location = $target->location_id;
Asset::where('assigned_type', '\\App\\Models\\Asset')->where('assigned_to', $id)
->update(['location_id' => $target->location_id]);
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
$location = $target->id;
}
@@ -608,16 +621,14 @@ class AssetsController extends Controller
$target = Asset::where('id','!=',$asset_id)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id;
// Override with the asset's location_id if it has one
if ($target->location_id!='') {
$asset->location_id = ($target) ? $target->location_id : '';
}
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_asset');
$error_payload['target_type'] = 'asset';
} elseif (request('checkout_to_type')=='user') {
// Fetch the target and set the asset's new location_id
$target = User::find(request('assigned_user'));
$asset->location_id = ($target) ? $target->location_id : '';
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_user');
$error_payload['target_type'] = 'user';
}
@@ -636,11 +647,12 @@ class AssetsController extends Controller
$asset_name = request('name', null);
// Set the location ID to the RTD location id if there is one
if ($asset->rtd_location_id!='') {
$asset->location_id = $target->rtd_location_id;
}
// Wait, why are we doing this? This overrides the stuff we set further up, which makes no sense.
// TODO: Follow up here. WTF. Commented out for now.
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
// $asset->location_id = $target->rtd_location_id;
// }

View File

@@ -30,7 +30,9 @@ class CategoriesController extends Controller
$categories = $categories->TextSearch($request->input('search'));
}
$offset = (($categories) && (request('offset') > $categories->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($categories) && ($request->get('offset') > $categories->count())) ? $categories->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -41,7 +41,9 @@ class CompaniesController extends Controller
$companies->TextSearch($request->input('search'));
}
$offset = (($companies) && (request('offset') > $companies->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($companies) && ($request->get('offset') > $companies->count())) ? $companies->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -43,7 +43,9 @@ class ComponentsController extends Controller
$components->where('location_id','=',$request->input('location_id'));
}
$offset = (($components) && (request('offset') > $components->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\User;
use App\Http\Transformers\ConsumablesTransformer;
use App\Helpers\Helper;
@@ -44,7 +45,9 @@ class ConsumablesController extends Controller
}
$offset = (($consumables) && (request('offset') > $consumables->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -155,7 +158,7 @@ class ConsumablesController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.delete.success')));
}
/**
/**
* Returns a JSON response containing details on the users associated with this consumable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -194,4 +197,55 @@ class ConsumablesController extends Controller
$data = array('total' => $consumableCount, 'rows' => $rows);
return $data;
}
/**
* Checkout a consumable
*
* @author [A. Gutierrez] [<andres@baller.tv>]
* @param int $id
* @since [v4.9.5]
* @return JsonResponse
*/
public function checkout(Request $request, $id)
{
// Check if the consumable exists
if (is_null($consumable = Consumable::find($id))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.does_not_exist')));
}
$this->authorize('checkout', $consumable);
if ($consumable->qty > 0) {
// Check if the user exists
$assigned_to = $request->input('assigned_to');
if (is_null($user = User::find($assigned_to))) {
// Return error message
return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
}
// Update the consumable data
$consumable->assigned_to = e($assigned_to);
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'user_id' => $user->id,
'assigned_to' => $assigned_to
]);
// Log checkout event
$logaction = $consumable->logCheckout(e($request->input('note')), $user);
$data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name;
$data['item_name'] = $consumable->name;
$data['checkout_date'] = $logaction->created_at;
$data['note'] = $logaction->note;
$data['require_acceptance'] = $consumable->requireAcceptance();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No consumables remaining'));
}
}

View File

@@ -15,7 +15,7 @@ class DepartmentsController extends Controller
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @author [Godfrey Martinez] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
@@ -39,7 +39,9 @@ class DepartmentsController extends Controller
$departments = $departments->TextSearch($request->input('search'));
}
$offset = (($departments) && (request('offset') > $departments->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($departments) && ($request->get('offset') > $departments->count())) ? $departments->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -161,5 +163,28 @@ class DepartmentsController extends Controller
return (new SelectlistTransformer)->transformSelectlist($departments);
}
/**
* Update the specified resource in storage.
*
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Department::class);
$departments = Department::findOrFail($id);
$departments->fill($request->all());
if ($departments->save()) {
return response()
->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer())->transformdepartment($departments), trans('admin/departments/message.update.success')));
}
return response()
->json(Helper::formatStandardApiResponse('error', null, $departments->getErrors()));
}
}

View File

@@ -28,7 +28,9 @@ class DepreciationsController extends Controller
$depreciations = $depreciations->TextSearch($request->input('search'));
}
$offset = (($depreciations) && (request('offset') > $depreciations->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($depreciations) && ($request->get('offset') > $depreciations->count())) ? $depreciations->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -28,7 +28,9 @@ class GroupsController extends Controller
$groups = $groups->TextSearch($request->input('search'));
}
$offset = (($groups) && (request('offset') > $groups->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($groups) && ($request->get('offset') > $groups->count())) ? $groups->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -119,7 +119,14 @@ class ImportController extends Controller
{
$this->authorize('import');
// Run a backup immediately before processing
Artisan::call('backup:run');
if ($request->has('run-backup')) {
\Log::debug('Backup manually requested via importer');
Artisan::call('backup:run');
} else {
\Log::debug('NO BACKUP requested via importer');
}
$errors = $request->import(Import::find($import_id));
$redirectTo = "hardware.index";
switch ($request->get('import-type')) {

View File

@@ -82,7 +82,9 @@ class LicensesController extends Controller
}
$offset = (($licenses) && (request('offset') > $licenses->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($licenses) && ($request->get('offset') > $licenses->count())) ? $licenses->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -228,14 +230,21 @@ class LicensesController extends Controller
$this->authorize('view', $license);
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
$seats = LicenseSeat::where('license_seats.license_id', $licenseId)
->with('license', 'user', 'asset', 'user.department');
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort')=='department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
}
$total = $seats->count();
$offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {

View File

@@ -52,8 +52,9 @@ class LocationsController extends Controller
}
$offset = (($locations) && (request('offset') > $locations->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($locations) && ($request->get('offset') > $locations->count())) ? $locations->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -232,23 +233,28 @@ class LocationsController extends Controller
}
if ($request->filled('search')) {
\Log::debug('Searching... ');
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->input('search').'%');
}
$locations = $locations->orderBy('name', 'ASC')->get();
$locations_with_children = [];
foreach ($locations as $location) {
if(!array_key_exists($location->parent_id, $locations_with_children)) {
if (!array_key_exists($location->parent_id, $locations_with_children)) {
$locations_with_children[$location->parent_id] = [];
}
$locations_with_children[$location->parent_id][] = $location;
}
$location_options = Location::indenter($locations_with_children);
if ($request->filled('search')) {
$locations_formatted = $locations;
} else {
$location_options = Location::indenter($locations_with_children);
$locations_formatted = new Collection($location_options);
}
$locations_formatted = new Collection($location_options);
$paginated_results = new LengthAwarePaginator($locations_formatted->forPage($page, 500), $locations_formatted->count(), 500, $page, []);
//return [];

View File

@@ -37,9 +37,9 @@ class ManufacturersController extends Controller
}
$offset = (($manufacturers) && (request('offset') > $manufacturers->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($manufacturers) && ($request->get('offset') > $manufacturers->count())) ? $manufacturers->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -20,8 +20,8 @@ class SettingsController extends Controller
{
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
\Log::debug('LDAP is not enabled so cannot test.');
return response()->json(['message' => 'LDAP is not enabled, so we cannot test LDAP connections.'], 400);
}
\Log::debug('Preparing to test LDAP connection');
@@ -33,13 +33,13 @@ class SettingsController extends Controller
Ldap::bindAdminToLdap($connection);
return response()->json(['message' => 'It worked!'], 200);
} catch (\Exception $e) {
\Log::debug('Bind failed');
\Log::debug('LDAP connected but Bind failed. Please check your LDAP settings and try again.');
return response()->json(['message' => $e->getMessage()], 400);
//return response()->json(['message' => $e->getMessage()], 500);
}
} catch (\Exception $e) {
\Log::debug('Connection failed but we cannot debug it any further on our end.');
return response()->json(['message' => $e->getMessage()], 600);
\Log::info('LDAP connection failed but we cannot debug it any further on our end.');
return response()->json(['message' => 'The LDAP connection failed but we cannot debug it any further on our end. The error from the server is: '.$e->getMessage()], 500);
}

View File

@@ -30,7 +30,9 @@ class StatuslabelsController extends Controller
$statuslabels = $statuslabels->TextSearch($request->input('search'));
}
$offset = (($statuslabels) && (request('offset') > $statuslabels->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -207,7 +209,7 @@ class StatuslabelsController extends Controller
{
$this->authorize('view', Statuslabel::class);
$this->authorize('index', Asset::class);
$assets = Asset::where('status_id','=',$id);
$assets = Asset::where('status_id','=',$id)->with('assignedTo');
$allowed_columns = [
'id',

View File

@@ -33,7 +33,9 @@ class SuppliersController extends Controller
$suppliers = $suppliers->TextSearch($request->input('search'));
}
$offset = (($suppliers) && (request('offset') > $suppliers->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($suppliers) && ($request->get('offset') > $suppliers->count())) ? $suppliers->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -13,6 +13,8 @@ use App\Models\Asset;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\LicensesTransformer;
use Auth;
class UsersController extends Controller
{
@@ -74,6 +76,14 @@ class UsersController extends Controller
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
if ($request->filled('email')) {
$users = $users->where('users.email', '=', $request->input('email'));
}
if ($request->filled('username')) {
$users = $users->where('users.username', '=', $request->input('username'));
}
if ($request->filled('group_id')) {
$users = $users->ByGroup($request->get('group_id'));
}
@@ -87,7 +97,10 @@ class UsersController extends Controller
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$offset = (($users) && (request('offset') > $users->count())) ? 0 : request('offset', 0);
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($users) && ($request->get('offset') > $users->count())) ? $users->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
@@ -203,6 +216,17 @@ class UsersController extends Controller
$user = new User;
$user->fill($request->all());
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
// Strip out the superuser permission if the API user isn't a superadmin
if (!Auth::user()->isSuperUser()) {
unset($permissions_array['superuser']);
}
$user->permissions = $permissions_array;
}
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$user->password = bcrypt($request->get('password', $tmp_pass));
@@ -228,7 +252,7 @@ class UsersController extends Controller
public function show($id)
{
$this->authorize('view', User::class);
$user = User::findOrFail($id);
$user = User::withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count')->findOrFail($id);
return (new UsersTransformer)->transformUser($user);
}
@@ -257,6 +281,23 @@ class UsersController extends Controller
$user->password = bcrypt($request->input('password'));
}
// 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 superuser permission if the API user isn't a superadmin
if (!Auth::user()->isSuperUser()) {
unset($permissions_array['superuser']);
}
$user->permissions = $permissions_array;
}
// 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)]);
@@ -355,6 +396,23 @@ class UsersController extends Controller
return (new AccessoriesTransformer)->transformAccessories($accessories, $accessories->count());
}
/**
* Return JSON containing a list of licenses assigned to a user.
*
* @author [N. Mathar] [<snipe@snipe.net>]
* @since [v5.0]
* @param $userId
* @return string JSON
*/
public function licenses($id)
{
$this->authorize('view', User::class);
$this->authorize('view', License::class);
$user = User::where('id', $id)->withTrashed()->first();
$licenses = $user->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
/**
* Reset the user's two-factor status
*
@@ -393,6 +451,6 @@ class UsersController extends Controller
*/
public function getCurrentUserInfo(Request $request)
{
return response()->json($request->user());
return (new UsersTransformer)->transformUser($request->user());
}
}

View File

@@ -90,23 +90,7 @@ class AssetModelsController extends Controller
$model->fieldset_id = e($request->input('custom_fieldset'));
}
if (Input::file('image')) {
$image = Input::file('image');
$file_name = str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
$path = app('models_upload_path');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
$model->image = $file_name;
}
$model = $request->handleImages($model,600, public_path().'/uploads/models');
// Was it created?
if ($model->save()) {
@@ -182,37 +166,7 @@ class AssetModelsController extends Controller
}
}
$old_image = $model->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$model->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $model->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('models_upload_path').$file_name);
} else {
$image->move(app('models_upload_path'), $file_name);
}
$model->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('models_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$model = $request->handleImages($model,600, public_path().'/uploads/models');
if ($model->save()) {
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
@@ -305,11 +259,8 @@ class AssetModelsController extends Controller
if (isset($model->id)) {
return view('models/view', compact('model'));
}
// Prepare the error message
$error = trans('admin/models/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('models.index')->with('error', $error);
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
/**

View File

@@ -137,6 +137,7 @@ class AssetsController extends Controller
$asset->supplier_id = request('supplier_id', 0);
$asset->requestable = request('requestable', 0);
$asset->rtd_location_id = request('rtd_location_id', null);
if ($asset->assigned_to=='') {
$asset->location_id = $request->input('rtd_location_id', null);
@@ -326,7 +327,7 @@ class AssetsController extends Controller
unlink(public_path().'/uploads/assets/'.$asset->image);
$asset->image = '';
} catch (\Exception $e) {
\Log::info($e);
\Log::debug($e);
}
}
@@ -394,6 +395,12 @@ class AssetsController extends Controller
if ($asset->save()) {
// Update any assigned assets with the new location_id from the parent asset
Asset::where('assigned_type', '\\App\\Models\\Asset')->where('assigned_to', $asset->id)
->update(['location_id' => $asset->location_id]);
// Redirect to the new asset page
\Session::flash('success', trans('admin/hardware/message.update.success'));
return response()->json(['redirect_url' => route("hardware.show", $assetId)]);
@@ -433,22 +440,63 @@ class AssetsController extends Controller
/**
* Searches the assets table by asset tag, and redirects if it finds one
* Searches the assets table by tag, and redirects if it finds one.
*
* This is used by the top search box in Snipe-IT, but as of 4.9.x
* can also be used as a url segment.
*
* https://yoursnipe.com/hardware/bytag/?assetTag=foo
*
* OR
*
* https://yoursnipe.com/hardware/bytag/foo
*
* The latter is useful if you're doing home-grown barcodes, or
* some other automation where you don't always know the internal ID of
* an asset and don't want to query for it.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $tag
* @since [v3.0]
* @return Redirect
*/
public function getAssetByTag(Request $request)
public function getAssetByTag(Request $request, $tag = null)
{
$topsearch = ($request->get('topsearch')=="true");
if (!$asset = Asset::where('asset_tag', '=', $request->get('assetTag'))->first()) {
// We need this part to determine whether a url query parameter has been passed, OR
// whether it's the url fragment we need to look at
$tag = ($request->get('assetTag')) ? $request->get('assetTag') : $tag;
if (!$asset = Asset::where('asset_tag', '=', $tag)->first()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('view', $asset);
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
}
/**
* Searches the assets table by serial, and redirects if it finds one
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $serial
* @since [v4.9.1]
* @return Redirect
*/
public function getAssetBySerial(Request $request, $serial = null)
{
$serial = ($request->get('serial')) ? $request->get('serial') : $serial;
if (!$asset = Asset::where('serial', '=', $serial)->first()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('view', $asset);
return redirect()->route('hardware.show', $asset->id);
}
/**
* Return a QR code for the asset
*
@@ -499,6 +547,7 @@ class AssetsController extends Controller
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->alt_barcode).'-'.str_slug($asset->asset_tag).'.png';
if (isset($asset->id, $asset->asset_tag)) {
if (file_exists($barcode_file)) {
$header = ['Content-type' => 'image/png'];
return response()->file($barcode_file, $header);
@@ -507,10 +556,22 @@ class AssetsController extends Controller
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 96.000000000001;
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
try {
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
} catch (\Exception $e) {
\Log::debug('Error creating barcode: '.$e->getMessage());
\Log::debug('This usually happens because the asset tags are of a format that is not compatible with the selected barcode type.');
$img = file_get_contents(public_path().'/uploads/barcodes/invalid_barcode.gif');
return response($img)->header('Content-type', 'image/gif');
}
}
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ForgotPasswordController extends Controller
{
@@ -41,6 +42,8 @@ class ForgotPasswordController extends Controller
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
}
/**
* Send a reset link to the given user.
*
@@ -49,11 +52,21 @@ class ForgotPasswordController extends Controller
*/
public function sendResetLinkEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
/**
* Let's set a max character count here to prevent potential
* buffer overflow issues with attackers sending very large
* payloads through.
*/
$this->validate($request, ['email' => 'required|email|max:250']);
/**
* If we find a matching email with an activated user, we will
* send the password reset link to the user.
*
* Once we have attempted to send the link, we will examine the response
* then see the message we need to show to the user. Finally, we'll send out a proper response.
*/
$response = $this->broker()->sendResetLink(
array_merge(
$request->only('email'),
@@ -65,9 +78,25 @@ class ForgotPasswordController extends Controller
return redirect()->route('login')->with('status', trans($response));
}
// If an error was returned by the password broker, we will get this message
// translated so we can notify a user of the problem. We'll redirect back
// to where the users came from so they can attempt this process again.
/**
* If an error was returned by the password broker, we will get this message
* translated so we can notify a user of the problem. We'll redirect back
* to where the users came from so they can attempt this process again.
*
* HOWEVER, we do not want to translate the message if the user isn't found
* or isn't active, since that would allow an attacker to walk through
* a dictionary attack and figure out registered user email addresses.
*
* Instead we tell the user we've sent an email even though we haven't.
* It's bad UX, but better security. The compromises we sometimes have to make.
*/
if ($response == 'passwords.user') {
\Log::debug('User with email '.$request->input('email').' attempted a password reset request but was not found. No email was sent.');
return redirect()->route('login')->with('success', trans('passwords.user_inactive'));
}
return back()->withErrors(
['email' => trans($response)]
);

View File

@@ -303,8 +303,8 @@ class LoginController extends Controller
*/
public function logout(Request $request)
{
$request->session()->forget('2fa_authed');
$request->session()->regenerate(true);
Auth::logout();
$settings = Setting::getSettings();

View File

@@ -83,17 +83,7 @@ class CategoriesController extends Controller
$category->checkin_email = $request->input('checkin_email', '0');
$category->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/categories/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$category->image = $file_name;
}
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
if ($category->save()) {
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.create.success'));
@@ -152,37 +142,12 @@ class CategoriesController extends Controller
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$old_image = $category->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$category->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $category->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('categories_upload_path').$file_name);
} else {
$image->move(app('categories_upload_path'), $file_name);
}
$category->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('categories_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
if ($category->save()) {
// Redirect to the new category page
@@ -254,10 +219,7 @@ class CategoriesController extends Controller
->with('category_type_route',$category_type_route);
}
// Prepare the error message
$error = trans('admin/categories/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('categories.index')->with('error', $error);
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}

View File

@@ -63,16 +63,7 @@ final class CompaniesController extends Controller
$company = new Company;
$company->name = $request->input('name');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/companies/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$company->image = $file_name;
}
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
if ($company->save()) {
return redirect()->route('companies.index')
@@ -121,36 +112,12 @@ final class CompaniesController extends Controller
$company->name = $request->input('name');
$old_image = $company->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$company->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $company->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('companies_upload_path').$file_name);
} else {
$image->move(app('companies_upload_path'), $file_name);
}
$company->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('companies_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
if ($company->save()) {

View File

@@ -91,16 +91,7 @@ class ComponentsController extends Controller
$component->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/components/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$component->image = $file_name;
}
$component = $request->handleImages($component,600, public_path().'/uploads/components');
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
@@ -164,18 +155,7 @@ class ComponentsController extends Controller
$component->purchase_cost = request('purchase_cost');
$component->qty = Input::get('qty');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/components/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$component->image = $file_name;
} elseif ($request->input('image_delete')=='1') {
$component->image = null;
}
$component = $request->handleImages($component,600, public_path().'/uploads/components');
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
@@ -219,10 +199,8 @@ class ComponentsController extends Controller
$this->authorize('view', $component);
return view('components/view', compact('component'));
}
// Prepare the error message
$error = trans('admin/components/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('components.index')->with('error', $error);
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
/**

View File

@@ -87,16 +87,8 @@ class ConsumablesController extends Controller
$consumable->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/consumables/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$consumable->image = $file_name;
}
$consumable = $request->handleImages($consumable,600, public_path().'/uploads/components');
if ($consumable->save()) {
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
@@ -212,7 +204,7 @@ class ConsumablesController extends Controller
if (isset($consumable->id)) {
return view('consumables/view', compact('consumable'));
}
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
/**

View File

@@ -179,14 +179,59 @@ class CustomFieldsetsController extends Controller
$this->authorize('update', $set);
foreach ($set->fields as $field) {
if ($field->id == $request->input('field_id')) {
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
if ($request->filled('field_id')) {
foreach ($set->fields as $field) {
if ($field->id == $request->input('field_id')) {
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
}
}
$results = $set->fields()->attach(Input::get('field_id'), ["required" => ($request->input('required') == "on"),"order" => $request->input('order', 1)]);
return redirect()->route("fieldsets.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
}
return redirect()->route("fieldsets.show", [$id])->with("error", 'No field selected.');
$results = $set->fields()->attach(Input::get('field_id'), ["required" => ($request->input('required') == "on"),"order" => $request->input('order', 1)]);
return redirect()->route("fieldsets.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
}
/**
* Set the field in a fieldset to required
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.0]
*/
public function makeFieldRequired($fieldset_id, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset = CustomFieldset::findOrFail($fieldset_id);
$fields[$field->id] = ['required' => 1];
$fieldset->fields()->syncWithoutDetaching($fields);
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
->with("success", trans('Field successfully set to required'));
}
/**
* Set the field in a fieldset to optional
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.0]
*/
public function makeFieldOptional($fieldset_id, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset = CustomFieldset::findOrFail($fieldset_id);
$fields[$field->id] = ['required' => 0];
$fieldset->fields()->syncWithoutDetaching($fields);
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
->with("success", trans('Field successfully set to optional'));
}
}

View File

@@ -53,16 +53,7 @@ class DepartmentsController extends Controller
$department->user_id = Auth::user()->id;
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/departments/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$department->image = $file_name;
}
$department = $request->handleImages($department,600, public_path().'/uploads/departments');
if ($department->save()) {
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.create.success'));
@@ -88,7 +79,7 @@ class DepartmentsController extends Controller
if (isset($department->id)) {
return view('departments/view', compact('department'));
}
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist', compact('id')));
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
@@ -164,36 +155,7 @@ class DepartmentsController extends Controller
$department->fill($request->all());
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
$old_image = $department->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$department->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $department->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('departments_upload_path').$file_name);
} else {
$image->move(app('departments_upload_path'), $file_name);
}
$department->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('departments_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$department = $request->handleImages($department,600, public_path().'/uploads/departments');
if ($department->save()) {
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.update.success'));

View File

@@ -246,17 +246,23 @@ class LicensesController extends Controller
*/
public function getCheckout($licenceId)
{
// Check that the license is valid
if ($license = License::where('id',$licenceId)->first()) {
$this->authorize('checkout', $license);
// If the license is valid, check that there is an available seat
if ($license->getAvailSeatsCountAttribute() < 1) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
}
return view('licenses/checkout', compact('license'));
}
$this->authorize('checkout', $license);
return view('licenses/checkout', compact('license'));
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
@@ -455,7 +461,7 @@ class LicensesController extends Controller
$this->authorize('view', $license);
return view('licenses/view', compact('license'));
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
@@ -524,9 +530,8 @@ class LicensesController extends Controller
}
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
}
// Prepare the error message
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
return redirect()->route('licenses.index')->with('error', $error);
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
@@ -562,7 +567,7 @@ class LicensesController extends Controller
}
// Redirect to the licence management page
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
@@ -613,7 +618,7 @@ class LicensesController extends Controller
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}

View File

@@ -88,16 +88,7 @@ class LocationsController extends Controller
$location->manager_id = $request->input('manager_id');
$location->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/locations/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$location->image = $file_name;
}
$location = $request->handleImages($location,600, public_path().'/uploads/locations');
if ($location->save()) {
return redirect()->route("locations.index")->with('success', trans('admin/locations/message.create.success'));
@@ -162,37 +153,7 @@ class LocationsController extends Controller
$location->zip = $request->input('zip');
$location->ldap_ou = $request->input('ldap_ou');
$location->manager_id = $request->input('manager_id');
$old_image = $location->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$location->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $location->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('locations_upload_path').$file_name);
} else {
$image->move(app('locations_upload_path'), $file_name);
}
$location->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('locations_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$location = $request->handleImages($location,600, public_path().'/uploads/locations');
if ($location->save()) {
@@ -252,7 +213,7 @@ class LocationsController extends Controller
return view('locations/view', compact('location'));
}
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
}

View File

@@ -75,18 +75,7 @@ class ManufacturersController extends Controller
$manufacturer->support_url = $request->input('support_url');
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_slug($image->getClientOriginalName()).".".$image->getClientOriginalExtension();
$path = public_path('uploads/manufacturers/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$manufacturer->image = $file_name;
}
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
@@ -107,11 +96,14 @@ class ManufacturersController extends Controller
*/
public function edit($id = null)
{
$this->authorize('edit', Manufacturer::class);
// Handles manufacturer checks and permissions.
$this->authorize('update', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($item = Manufacturer::find($id))) {
if (!$item = Manufacturer::find($id)) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
}
// Show the page
return view('manufacturers/edit', compact('item'));
}
@@ -129,7 +121,7 @@ class ManufacturersController extends Controller
*/
public function update(ImageUploadRequest $request, $manufacturerId = null)
{
$this->authorize('edit', Manufacturer::class);
$this->authorize('update', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
// Redirect to the manufacturer page
@@ -142,37 +134,14 @@ class ManufacturersController extends Controller
$manufacturer->support_url = $request->input('support_url');
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
$old_image = $manufacturer->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$manufacturer->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $manufacturer->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('manufacturers_upload_path').$file_name);
} else {
$image->move(app('manufacturers_upload_path'), $file_name);
}
$manufacturer->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('manufacturers_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
if ($manufacturer->save()) {

View File

@@ -102,7 +102,7 @@ class ReportsController extends Controller
$depreciations = Depreciation::get();
// Grab all the assets
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'assetlog', 'company', 'model.category', 'model.depreciation')
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'company', 'model.category', 'model.depreciation')
->orderBy('created_at', 'DESC')->get();
return view('reports/depreciation', compact('assets'))->with('depreciations',$depreciations);

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Http\Controllers;
use enshrined\svgSanitize\Sanitizer;
use Input;
use Lang;
use Illuminate\Http\Request;
@@ -426,12 +427,23 @@ class SettingsController extends Controller
$file_name = "logo.".$image->getClientOriginalExtension();
$path = public_path('uploads');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 150, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
// This is kinda copypasta from the ImageUploadRequest - should refactor the ImageUploadRequest to better handle maybe
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
file_put_contents($path.'/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug($e);
}
}
$setting->logo = $file_name;
}
@@ -633,14 +645,24 @@ class SettingsController extends Controller
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
$setting->slack_endpoint = $request->input('slack_endpoint');
$setting->slack_channel = $request->input('slack_channel');
$setting->slack_botname = $request->input('slack_botname');
$validatedData = $request->validate([
'slack_endpoint' => 'url|required_with:slack_channel|nullable',
'slack_channel' => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
'slack_botname' => 'string|nullable',
]);
if ($setting->save()) {
if ($validatedData) {
$setting->slack_endpoint = $request->input('slack_endpoint');
$setting->slack_channel = $request->input('slack_channel');
$setting->slack_botname = $request->input('slack_botname');
$setting->save();
return redirect()->route('settings.index')
->with('success', trans('admin/settings/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}

View File

@@ -43,7 +43,7 @@ class StatuslabelsController extends Controller
return view('statuslabels.view')->with('statuslabel', $statuslabel);
}
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist', compact('id')));
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}

View File

@@ -78,17 +78,8 @@ class SuppliersController extends Controller
$supplier->notes = request('notes');
$supplier->url = $supplier->addhttp(request('url'));
$supplier->user_id = Auth::id();
$supplier = $request->handleImages($supplier,600, public_path().'/uploads/suppliers');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/suppliers/'.$file_name);
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$supplier->image = $file_name;
}
if ($supplier->save()) {
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.create.success'));
@@ -145,39 +136,7 @@ class SuppliersController extends Controller
$supplier->email = request('email');
$supplier->url = $supplier->addhttp(request('url'));
$supplier->notes = request('notes');
$old_image = $supplier->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$supplier->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $supplier->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('suppliers_upload_path').$file_name);
} else {
$image->move(app('suppliers_upload_path'), $file_name);
}
$supplier->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('suppliers_upload_path').$old_image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$supplier = $request->handleImages($supplier,600, public_path().'/uploads/suppliers');
if ($supplier->save()) {
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.update.success'));
@@ -236,11 +195,8 @@ class SuppliersController extends Controller
if (isset($supplier->id)) {
return view('suppliers/view', compact('supplier'));
}
// Prepare the error message
$error = trans('admin/suppliers/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('suppliers.index')->with('error', $error);
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.does_not_exist'));
}
}

View File

@@ -240,6 +240,12 @@ class UsersController extends Controller
if ($user->id == $request->input('manager_id')) {
return redirect()->back()->withInput()->with('error', 'You cannot be your own manager.');
}
// If the user isn't a superuser, don't let them edit their own permissions
if ((!Auth::user()->isSuperUser()) && ($user->id == Auth::user()->id)) {
return redirect()->back()->withInput()->with('error', 'You cannot edit your own permissions. Please contact an administrator.');
}
$this->authorize('update', $user);
// Figure out of this user was an admin before this edit
$orig_permissions_array = $user->decodePermissions();
@@ -429,6 +435,10 @@ class UsersController extends Controller
if ($request->filled('department_id')) {
$update_array['department_id'] = $request->input('department_id');
}
if ($request->filled('city')) {
$update_array['city'] = $request->input('city');
}
if ($request->filled('company_id')) {
$update_array['company_id'] = $request->input('company_id');
}
@@ -606,10 +616,12 @@ class UsersController extends Controller
*/
public function show($userId = null)
{
if(!$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($userId)) {
$error = trans('admin/users/message.user_not_found', compact('id'));
// Redirect to the user management page
return redirect()->route('users.index')->with('error', $error);
if (!$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')
->withTrashed()
->find($userId))
{
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
}
$userlog = $user->userlog->load('item');
@@ -706,10 +718,8 @@ class UsersController extends Controller
->with('userGroups', $userGroups)
->with('clone_user', $user_to_clone);
} catch (UserNotFoundException $e) {
// Prepare the error message
$error = trans('admin/users/message.user_not_found', compact('id'));
// Redirect to the user management page
return redirect()->route('users.index')->with('error', $error);
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found'));
}
}
@@ -731,30 +741,38 @@ class UsersController extends Controller
if (isset($user->id)) {
$this->authorize('update', $user);
foreach (Input::file('file') as $file) {
if (!$request->has('file')) {
\Log::debug('No file selected: ');
\Log::debug(print_r($request, true));
return redirect()->back()->with('error', 'No file submitted.');
$extension = $file->getClientOriginalExtension();
$filename = 'user-' . $user->id . '-' . str_random(8);
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
$upload_success = $file->move($destinationPath, $filename);
} else {
foreach ($request->file('file') as $file) {
//Log the uploaded file to the log
$logAction = new Actionlog();
$logAction->item_id = $user->id;
$logAction->item_type = User::class;
$logAction->user_id = Auth::user()->id;
$logAction->note = e(Input::get('notes'));
$logAction->target_id = null;
$logAction->created_at = date("Y-m-d H:i:s");
$logAction->filename = $filename;
$logAction->action_type = 'uploaded';
$logAction->save();
$extension = $file->getClientOriginalExtension();
$filename = 'user-' . $user->id . '-' . str_random(8);
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
$upload_success = $file->move($destinationPath, $filename);
//Log the uploaded file to the log
$logAction = new Actionlog();
$logAction->item_id = $user->id;
$logAction->item_type = User::class;
$logAction->target_type = User::class;
$logAction->target_id = $user->id;
$logAction->user_id = Auth::user()->id;
$logAction->note = $request->input('notes');
$logAction->created_at = date("Y-m-d H:i:s");
$logAction->filename = $filename;
$logAction->action_type = 'uploaded';
$logAction->save();
}
return redirect()->back()->with('success', 'File uploaded');
}
return JsonResponse::create($logAction);
}
return JsonResponse::create(["error" => "Failed validation: ".print_r($logAction->getErrors(), true)], 500);
return redirect()->route('users.index')->with('error', 'Error uploading files');
}
@@ -782,10 +800,8 @@ class UsersController extends Controller
$log->delete();
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
}
// Prepare the error message
$error = trans('admin/users/message.does_not_exist', compact('id'));
// Redirect to the licence management page
return redirect()->route('users.index')->with('error', $error);
return redirect()->route('users.index')->with('error', trans('admin/users/message.does_not_exist'));
}

View File

@@ -17,15 +17,12 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\FrameGuard::class,
\App\Http\Middleware\XssProtectHeader::class,
\App\Http\Middleware\ReferrerPolicyHeader::class,
\App\Http\Middleware\ContentSecurityPolicyHeader::class,
\App\Http\Middleware\NosniffGuard::class,
\Fideloper\Proxy\TrustProxies::class,
\App\Http\Middleware\CheckForSetup::class,
\App\Http\Middleware\CheckForDebug::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\SecurityHeaders::class,
];
/**

View File

@@ -1,35 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
class ContentSecurityPolicyHeader
{
/**
* Handle the given request and get the response.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
if ((config('app.debug')=='true') || (config('app.enable_csp')!='true')) {
$response = $next($request);
return $response;
}
$policy[] = "default-src 'self'";
$policy[] = "style-src 'self' 'unsafe-inline' oss.maxcdn.com";
$policy[] = "script-src 'self' 'unsafe-inline' oss.mafxcdn.com cdnjs.cloudflare.com'";
$policy[] = "connect-src 'self'";
$policy[] = "object-src 'none'";
$policy[] = "font-src 'self' data:";
$policy[] = "img-src 'self' data: gravatar.com";
$policy = join(';', $policy);
$response = $next($request);
$response->headers->set('Content-Security-Policy', $policy);
return $response;
}
}

View File

@@ -1,24 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
class FrameGuard
{
/**
* Handle the given request and get the response.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
$response = $next($request);
if (config('app.allow_iframing') == false) {
$response->headers->set('X-Frame-Options', 'SAMEORIGIN', false);
}
return $response;
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
class NosniffGuard
{
/**
* Handle the given request and get the response.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff', false);
return $response;
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
class ReferrerPolicyHeader
{
/**
* Handle the given request and get the response.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('Referrer-Policy', config('app.referrer_policy'));
return $response;
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace App\Http\Middleware;
use Closure;
class SecurityHeaders
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
// See https://securityheaders.com/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
public function handle($request, Closure $next)
{
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
// Ugh. Feature-Policy is dumb and clumsy and mostly irrelevant for Snipe-IT,
// since we don't provide any way to IFRAME anything in in the first place.
// There is currently no easy way to default ALL THE THINGS to 'none', but
// security audits will still ding you if you don't have this header, even
// though we don't allow IFRAMING in the first place.
//
// So for security and compliance sake, here we are. Sigh.
//
// See also:
// - https://developers.google.com/web/updates/2018/06/feature-policy
// - https://scotthelme.co.uk/a-new-security-header-feature-policy/
// - https://github.com/w3c/webappsec-feature-policy/issues/189
$feature_policy[] = "accelerometer 'none'";
$feature_policy[] = "ambient-light-sensor 'none'";
$feature_policy[] = "animations 'none'";
$feature_policy[] = "autoplay 'none'";
$feature_policy[] = "battery 'none'";
$feature_policy[] = "camera 'none'";
$feature_policy[] = "display-capture 'none'";
$feature_policy[] = "document-domain 'none'";
$feature_policy[] = "encrypted-media 'none'";
$feature_policy[] = "fullscreen 'none'";
$feature_policy[] = "geolocation 'none'";
$feature_policy[] = "gyroscope 'none'";
$feature_policy[] = "legacy-image-formats 'none'";
$feature_policy[] = "magnetometer 'none'";
$feature_policy[] = "microphone 'none'";
$feature_policy[] = "midi 'none'";
$feature_policy[] = "oversized-images 'none'";
$feature_policy[] = "payment 'none'";
$feature_policy[] = "picture-in-picture 'none'";
$feature_policy[] = "publickey-credentials 'none'";
$feature_policy[] = "sync-xhr 'none'";
$feature_policy[] = "unsized-media 'none'";
$feature_policy[] = "usb 'none'";
$feature_policy[] = "vibrate 'none'";
$feature_policy[] = "wake-lock 'none'";
$feature_policy[] = "xr-spatial-tracking 'none'";
$feature_policy = join(';', $feature_policy);
$response->headers->set('Feature-Policy', $feature_policy);
// Defaults to same-origin if REFERRER_POLICY is not set in the .env
$response->headers->set('Referrer-Policy', config('app.referrer_policy'));
// The .env var ALLOW_IFRAMING defaults to false (which disallows IFRAMING)
// if not present, but some unique cases require this to be enabled.
// For example, some IT depts have IFRAMED Snipe-IT into their IT portal
// for convenience so while it is normally disallowed, there is
// an override that exists.
if (config('app.allow_iframing') == false) {
$response->headers->set('X-Frame-Options', 'DENY');
}
// This defaults to false to maintain backwards compatibility for
// people who are not running Snipe-IT over TLS (shame, shame, shame!)
// Seriously though, please run Snipe-IT over TLS. Let's Encrypt is free.
// https://letsencrypt.org
if (config('app.enable_hsts') === true) {
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
// We have to exclude debug mode here because debugbar pulls from a CDN or two
// and it will break things.
if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) {
$csp_policy[] = "default-src 'self'";
$csp_policy[] = "style-src 'self' 'unsafe-inline'";
$csp_policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
$csp_policy[] = "connect-src 'self'";
$csp_policy[] = "object-src 'none'";
$csp_policy[] = "font-src 'self' data:";
$csp_policy[] = "img-src 'self' data: gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
$csp_policy = join(';', $csp_policy);
$response->headers->set('Content-Security-Policy', $csp_policy);
}
return $response;
}
private function removeUnwantedHeaders($headerList)
{
foreach ($headerList as $header)
header_remove($header);
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
class XssProtectHeader
{
/**
* Handle the given request and get the response.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
$mode = '1;mode=block';
$response = $next($request);
$response->headers->set('X-XSS-Protection', $mode);
return $response;
}
}

View File

@@ -51,7 +51,7 @@ class AssetRequest extends Request
if ($this->request->get('model_id') != '') {
$model = AssetModel::find($this->request->get('model_id'));
if (($model) && ($model->fieldset)) {
if (($model) && (isset($model->fieldset)) && ($model->fieldset)) {
$rules += $model->fieldset->validation_rules();
}
}

View File

@@ -2,7 +2,9 @@
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Models\SnipeModel;
use Intervention\Image\Facades\Image;
use enshrined\svgSanitize\Sanitizer;
class ImageUploadRequest extends Request
{
@@ -33,4 +35,83 @@ class ImageUploadRequest extends Request
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}
/**
* Handle and store any images attached to request
* @param SnipeModel $item Item the image is associated with
* @param String $path location for uploaded images, defaults to uploads/plural of item type.
* @return SnipeModel Target asset is being checked out to.
*/
public function handleImages($item, $w = 600, $path = null)
{
$type = strtolower(class_basename(get_class($item)));
if (is_null($path)) {
$path = str_plural($type);
}
\Log::debug('Trying to upload to '. $path);
if ($this->hasFile('image')) {
if (!config('app.lock_passwords')) {
if (!is_dir($path)) {
\Log::debug($path.' does not exist');
mkdir($path);
}
$image = $this->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = $type.'-'.str_random(18).'.'.$ext;
\Log::debug('File name will be: '.$file_name);
if ($image->getClientOriginalExtension()!=='svg') {
\Log::debug('Not an SVG - resize');
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
$upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
\Log::debug('This is an SVG');
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
file_put_contents($path.'/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug($e);
}
}
// Remove Current image if exists
if (($item->image) && (file_exists($path.'/'.$item->image))) {
try {
unlink($path.'/'.$item->image);
} catch (\Exception $e) {
\Log::debug($e);
}
}
$item->image = $file_name;
}
} elseif ($this->input('image_delete')=='1') {
try {
unlink($path.'/'.$item->image);
} catch (\Exception $e) {
\Log::debug($e);
}
$item->image = null;
}
return $item;
}
}

View File

@@ -30,18 +30,49 @@ class ActionlogsTransformer
// This is necessary since we can't escape special characters within a JSON object
if (($actionlog->log_meta) && ($actionlog->log_meta!='')) {
$meta_array = json_decode($actionlog->log_meta);
foreach ($meta_array as $key => $value) {
foreach ($value as $meta_key => $meta_value) {
if (is_array($meta_value)) {
foreach ($meta_value as $meta_value_key => $meta_value_value) {
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
if ($meta_array) {
foreach ($meta_array as $key => $value) {
foreach ($value as $meta_key => $meta_value) {
if (is_array($meta_value)) {
foreach ($meta_value as $meta_value_key => $meta_value_value) {
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
}
} else {
// This object stuff is weird, and is used to make up for the fact that
// older data can get strangely formatted if an asset existed,
// then a new custom field is added, and the asset is saved again.
// It can result in funnily-formatted strings like:
//
// {"_snipeit_right_sized_fault_tolerant_localareanetwo_1":
// {"old":null,"new":{"value":"1579490695972","_snipeit_new_field_2":2,"_snipeit_new_field_3":"Monday, 20 January 2020 2:24:55 PM"}}
// so we have to walk down that next level
if (is_object($meta_value)) {
foreach ($meta_value as $meta_value_key => $meta_value_value) {
if ($meta_value_key == 'value') {
$clean_meta[$key]['old'] = null;
$clean_meta[$key]['new'] = e($meta_value->value);
} else {
$clean_meta[$meta_value_key]['old'] = null;
$clean_meta[$meta_value_key]['new'] = e($meta_value_value);
}
}
} else {
$clean_meta[$key][$meta_key] = e($meta_value);
}
}
} else {
$clean_meta[$key][$meta_key] = e($meta_value);
}
}
}
}
}
@@ -58,7 +89,7 @@ class ActionlogsTransformer
'item' => ($actionlog->item) ? [
'id' => (int) $actionlog->item->id,
'name' => e($actionlog->item->getDisplayNameAttribute()),
'name' => ($actionlog->itemType()=='user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
'type' => e($actionlog->itemType()),
] : null,
'location' => ($actionlog->location) ? [

View File

@@ -5,6 +5,7 @@ use App\Models\AssetMaintenance;
use Gate;
use Illuminate\Database\Eloquent\Collection;
use App\Helpers\Helper;
use App\Models\Asset;
class AssetMaintenancesTransformer
{

View File

@@ -29,7 +29,14 @@ class LicenseSeatsTransformer
'name' => 'Seat '.$seat_count,
'assigned_user' => ($seat->user) ? [
'id' => (int) $seat->user->id,
'name'=> e($seat->user->present()->fullName)
'name'=> e($seat->user->present()->fullName),
'department'=>
($seat->user->department) ?
[
"id" => (int) $seat->user->department->id,
"name" => e($seat->user->department->name)
] : null
] : null,
'assigned_asset' => ($seat->asset) ? [
'id' => (int) $seat->asset->id,

View File

@@ -76,6 +76,8 @@ class AssetImporter extends ItemImporter
}
$this->item['image'] = $this->findCsvMatch($row, "image");
$this->item['requestable'] = $this->fetchHumanBoolean($this->findCsvMatch($row, "requestable"));;
$asset->requestable = $this->fetchHumanBoolean($this->findCsvMatch($row, "requestable"));
$this->item['warranty_months'] = intval($this->findCsvMatch($row, "warranty_months"));
$this->item['model_id'] = $this->createOrFetchAssetModel($row);

View File

@@ -63,6 +63,10 @@ abstract class Importer
'full_name' => 'full name',
'email' => 'email',
'username' => 'username',
'address' => 'address',
'city' => 'city',
'state' => 'state',
'country' => 'country',
'jobtitle' => 'job title',
'employee_num' => 'employee number',
'phone_number' => 'phone number',
@@ -443,11 +447,7 @@ abstract class Importer
public function fetchHumanBoolean($value)
{
if (($value =='1') || (strtolower($value) =='true') || (strtolower($value) =='yes'))
{
return '1';
}
return '0';
return (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
/**

View File

@@ -47,6 +47,10 @@ class UserImporter extends ItemImporter
$this->item['email'] = $this->findCsvMatch($row, 'email');
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
$this->item['address'] = $this->findCsvMatch($row, 'address');
$this->item['city'] = $this->findCsvMatch($row, 'city');
$this->item['state'] = $this->findCsvMatch($row, 'state');
$this->item['country'] = $this->findCsvMatch($row, 'country');
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
\Log::debug('UserImporter.php Activated: '.$this->findCsvMatch($row, 'activated'));

View File

@@ -14,7 +14,7 @@
| licensed to email | license_email | License |
| licensed to name | license_name | License |
| maintained | maintained | License |
| manager_id | | User |
| manager_id | | User |
| manufacturer | manufacturer | All |
| model name | asset_model | Asset |
| model number | model_number | Asset |
@@ -34,4 +34,8 @@
| User Related Fields | assigned_to | Asset |
| name | | |
| username | | |
| address | address | User |
| city | city | User |
| state | state | User |
| country | country | User |

View File

@@ -111,6 +111,7 @@ class Asset extends Depreciable
'status_id',
'supplier_id',
'warranty_months',
'requestable',
];
use Searchable;
@@ -225,10 +226,10 @@ class Asset extends Depreciable
if ($location != null) {
$this->location_id = $location;
} else {
if($target->location) {
if (isset($target->location)) {
$this->location_id = $target->location->id;
}
if($target instanceof Location) {
if ($target instanceof Location) {
$this->location_id = $target->id;
}
}
@@ -604,20 +605,26 @@ class Asset extends Depreciable
public function requireAcceptance()
{
return $this->model->category->require_acceptance;
if (($this->model) && ($this->model->category)) {
return $this->model->category->require_acceptance;
}
}
public function getEula()
{
$Parsedown = new \Parsedown();
if ($this->model->category->eula_text) {
return $Parsedown->text(e($this->model->category->eula_text));
} elseif ($this->model->category->use_default_eula == '1') {
return $Parsedown->text(e(Setting::getSettings()->default_eula_text));
} else {
return false;
if (($this->model) && ($this->model->category)) {
if ($this->model->category->eula_text) {
return $Parsedown->text(e($this->model->category->eula_text));
} elseif ($this->model->category->use_default_eula == '1') {
return $Parsedown->text(e(Setting::getSettings()->default_eula_text));
} else {
return false;
}
}
return false;
}
/**
@@ -821,9 +828,11 @@ class Asset extends Depreciable
public function scopeDueForAudit($query, $settings)
{
$interval = $settings->audit_warning_days ?? 0;
return $query->whereNotNull('assets.next_audit_date')
->where('assets.next_audit_date', '>=', Carbon::now())
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $settings->audit_warning_days DAY) <= '".Carbon::now()."'")
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
->where('assets.archived', '=', 0)
->NotArchived();
}
@@ -869,7 +878,7 @@ class Asset extends Depreciable
$interval = $settings->audit_warning_days ?? 0;
return $query->whereNotNull('assets.next_audit_date')
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
->whereRaw("DATE_SUB(".DB::getTablePrefix()."assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
->where('assets.archived', '=', 0)
->NotArchived();
}
@@ -1389,8 +1398,7 @@ class Asset extends Depreciable
/**
* Query builder scope to search on location ID
*
* Query builder scope to search on depreciation name
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $search Search term
*

View File

@@ -128,9 +128,12 @@ final class Company extends SnipeModel
} elseif (!static::isFullMultipleCompanySupportEnabled()) {
return true;
} else {
$current_user_company_id = Auth::user()->company_id;
$companyable_company_id = $companyable->company_id;
return ($current_user_company_id == null || $current_user_company_id == $companyable_company_id || Auth::user()->isSuperUser());
if (Auth::user()) {
$current_user_company_id = Auth::user()->company_id;
$companyable_company_id = $companyable->company_id;
return ($current_user_company_id == null || $current_user_company_id == $companyable_company_id || Auth::user()->isSuperUser());
}
}
}

View File

@@ -265,13 +265,13 @@ class Ldap extends Model
$search_results = ldap_search($ldapconn, $base_dn, '('.$filter.')');
if (!$search_results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Get results from page
$results = ldap_get_entries($ldapconn, $search_results);
if (!$results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Add results to result set
@@ -286,7 +286,7 @@ class Ldap extends Model
// Clean up after search
$result_set['count'] = $global_count;
$results = $result_set;
ldap_control_paged_result($ldapconn, 0);
@ldap_control_paged_result($ldapconn, 0);
return $results;

View File

@@ -55,4 +55,23 @@ class LicenseSeat extends Model implements ICompanyableChild
return false;
}
/**
* Query builder scope to order on department
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderDepartments($query, $order)
{
return $query->leftJoin('users as license_seat_users', 'license_seats.assigned_to', '=', 'license_seat_users.id')
->leftJoin('departments as license_user_dept', 'license_user_dept.id', '=', 'license_seat_users.department_id')
->orderBy('license_user_dept.name', $order);
}
}

View File

@@ -150,11 +150,13 @@ class Location extends SnipeModel
public static function indenter($locations_with_children, $parent_id = null, $prefix = '') {
$results = Array();
if (!array_key_exists($parent_id, $locations_with_children)) {
return [];
}
foreach ($locations_with_children[$parent_id] as $location) {
$location->use_text = $prefix.' '.$location->name;
$location->use_image = ($location->image) ? url('/').'/uploads/locations/'.$location->image : null;

View File

@@ -41,13 +41,20 @@ trait Loggable
$settings = Setting::getSettings();
$log = new Actionlog;
$log = $this->determineLogItemType($log);
if(Auth::user())
if (Auth::user()) {
$log->user_id = Auth::user()->id;
}
if (!isset($target)) {
throw new Exception('All checkout logs require a target');
throw new \Exception('All checkout logs require a target.');
return;
}
if (!isset($target->id)) {
throw new \Exception('That target seems invalid (no target ID available).');
return;
}
$log->target_type = get_class($target);
$log->target_id = $target->id;
@@ -138,7 +145,11 @@ trait Loggable
$log->location_id = null;
$log->note = $note;
$log->user_id = Auth::user()->id;
if (Auth::user()) {
$log->user_id = Auth::user()->id;
}
$log->logaction('checkin from');
$params = [
@@ -154,14 +165,24 @@ trait Loggable
$checkinClass = null;
if (method_exists($target, 'notify')) {
$target->notify(new static::$checkinClass($params));
try {
$target->notify(new static::$checkinClass($params));
} catch (\Exception $e) {
\Log::debug($e);
}
}
// Send to the admin, if settings dictate
$recipient = new \App\Models\Recipients\AdminRecipient();
if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) {
$recipient->notify(new static::$checkinClass($params));
try {
$recipient->notify(new static::$checkinClass($params));
} catch (\Exception $e) {
\Log::debug($e);
}
}
return $log;

View File

@@ -20,10 +20,7 @@ class Setting extends Model
'admin_cc_email' => 'email|nullable',
'default_currency' => 'required',
'locale' => 'required',
'slack_endpoint' => 'url|required_with:slack_channel|nullable',
'slack_channel' => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
'slack_botname' => 'string|nullable',
'labels_per_page' => 'numeric',
'labels_per_page' => 'numeric|min:1',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_pmargin_left' => 'numeric|nullable',

View File

@@ -237,7 +237,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
*/
public function userlog()
{
return $this->hasMany('\App\Models\Actionlog', 'target_id')->orderBy('created_at', 'DESC')->withTrashed();
return $this->hasMany('\App\Models\Actionlog', 'target_id')->where('target_type', '=', 'App\Models\User')->orderBy('created_at', 'DESC')->withTrashed();
}
/**
@@ -379,6 +379,18 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
} elseif ($format=='firstname') {
$username = str_slug($first_name);
}
elseif ($format=='firstinitial.lastname') {
$username = str_slug(substr($first_name, 0, 1). '.' . str_slug($last_name));
}
elseif ($format=='lastname_firstinitial') {
$username = str_slug($last_name).'_'.str_slug(substr($first_name, 0, 1));
}
elseif ($format=='firstnamelastname') {
$username = str_slug($first_name) . str_slug($last_name);
}
elseif ($format=='firstnamelastinitial') {
$username = str_slug(($first_name.substr($last_name, 0, 1)));
}
}
$user['first_name'] = $first_name;

View File

@@ -76,10 +76,18 @@ class CheckinLicenseNotification extends Notification
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if ($admin) {
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
} else {
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => 'CLI tool',
];
}

View File

@@ -67,7 +67,7 @@ class AssetModelPresenter extends Presenter
public function imageUrl()
{
if (!empty($this->image)) {
return '<img src="' . url('/') . '/uploads/models/' . $this->image . '" height=50 width=50>';
return '<img src="' . url('/') . '/uploads/models/' . $this->image . '" alt="'.$this->name.'" height="50" width="50">';
}
return '';
}

View File

@@ -258,14 +258,21 @@ class AssetPresenter extends Presenter
$query->whereHas('models');
})->get();
// Note: We do not need to e() escape the field names here, as they are already escaped when
// they are presented in the blade view. If we escape them here, custom fields with quotes in their
// name can break the listings page. - snipe
foreach ($fields as $field) {
$layout[] = [
"field" => 'custom_fields.'.$field->convertUnicodeDbSlug(),
"searchable" => true,
"sortable" => true,
"switchable" => true,
"title" => ($field->field_encrypted=='1') ?'<i class="fa fa-lock"></i> '.e($field->name) : e($field->name),
"formatter" => "customFieldsFormatter"
"title" => $field->name,
"formatter"=> 'customFieldsFormatter',
"escape" => true,
"class" => ($field->field_encrypted=='1') ? 'css-padlock' : '',
"visible" => true,
];
}
@@ -320,12 +327,14 @@ class AssetPresenter extends Presenter
$imagePath = '';
if ($this->image && !empty($this->image)) {
$imagePath = $this->image;
$imageAlt = $this->name;
} elseif ($this->model && !empty($this->model->image)) {
$imagePath = $this->model->image;
$imageAlt = $this->model->name;
}
$url = config('app.url');
if (!empty($imagePath)) {
$imagePath = "<img src='{$url}/uploads/assets/{$imagePath}' height=50 width=50>";
$imagePath = '<img src="'.$url.'/uploads/assets/'.$imagePath.' height="50" width="50" alt="'.$imageAlt.'">';
}
return $imagePath;
}
@@ -391,7 +400,7 @@ class AssetPresenter extends Presenter
public function eol_date()
{
if (( $this->purchase_date ) && ( $this->model ) && ($this->model->model->eol) ) {
if (( $this->purchase_date ) && ( $this->model->model ) && ($this->model->model->eol) ) {
$date = date_create($this->purchase_date);
date_add($date, date_interval_create_from_date_string($this->model->model->eol . ' months'));
return date_format($date, 'Y-m-d');
@@ -512,6 +521,6 @@ class AssetPresenter extends Presenter
public function glyph()
{
return '<i class="fa fa-barcode"></i>';
return '<i class="fa fa-barcode" aria-hidden="true"></i>';
}
}

View File

@@ -49,7 +49,7 @@ class CompanyPresenter extends Presenter
"field" => "assets_count",
"searchable" => false,
"sortable" => true,
"title" => '<span class="hidden-xs"><i class="fa fa-barcode"></i></span><span class="hidden-md hidden-lg">'.trans('general.assets').'</span>',
"title" => '<span class="hidden-xs"><i class="fa fa-barcode" aria-hidden="true"></i></span><span class="hidden-md hidden-lg">'.trans('general.assets').'</span>',
"visible" => true,
],[

View File

@@ -176,6 +176,15 @@ class LicensePresenter extends Presenter
"visible" => true,
"formatter" => "usersLinkObjFormatter"
], [
"field" => "department",
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => trans('general.department'),
"visible" => false,
"formatter" => "departmentNameLinkFormatter"
],
[
"field" => "assigned_asset",
"searchable" => false,
"sortable" => false,
@@ -191,7 +200,8 @@ class LicensePresenter extends Presenter
"title" => trans('general.location'),
"visible" => true,
"formatter" => "locationsLinkObjFormatter"
], [
],
[
"field" => "checkincheckout",
"searchable" => false,
"sortable" => false,

View File

@@ -190,7 +190,7 @@ class LocationPresenter extends Presenter
public function glyph()
{
return '<i class="fa fa-map-marker"></i>';
return '<i class="fa fa-map-marker" aria-hidden="true"></i>';
}
public function fullName() {

View File

@@ -171,21 +171,22 @@ class UserPresenter extends Presenter
"formatter" => "usersLinkObjFormatter"
],
[
"field" => "assets_count",
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Assets</span>'
.'<span class="hidden-xs"><i class="fa fa-barcode fa-lg"></i></span>',
"visible" => true,
'field' => 'assets_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'escape' => true,
'class' => 'css-barcode',
'title' => 'Assets',
'visible' => true,
],
[
"field" => "licenses_count",
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Licenses</span>'
.'<span class="hidden-xs"><i class="fa fa-floppy-o fa-lg"></i></span>',
'class' => 'css-license',
"title" => 'License',
"visible" => true,
],
[
@@ -193,8 +194,8 @@ class UserPresenter extends Presenter
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Consumables</span>'
.'<span class="hidden-xs"><i class="fa fa-tint fa-lg"></i></span>',
'class' => 'css-consumable',
"title" => 'Consumables',
"visible" => true,
],
[
@@ -202,8 +203,8 @@ class UserPresenter extends Presenter
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Accessories</span>'
.'<span class="hidden-xs"><i class="fa fa-keyboard-o fa-lg"></i></span>',
'class' => 'css-accessory',
"title" => 'Accessories',
"visible" => true,
],
[
@@ -323,9 +324,14 @@ class UserPresenter extends Presenter
return config('app.url').'/uploads/avatars/'.$this->avatar;
}
if ((Setting::getSettings()->load_remote=='1') && ($this->email!='')) {
$gravatar = md5(strtolower(trim($this->email)));
return "//gravatar.com/avatar/".$gravatar;
if (Setting::getSettings()->load_remote=='1') {
if ($this->model->gravatar!='') {
$gravatar = md5(strtolower(trim($this->model->gravatar)));
return "//gravatar.com/avatar/".$gravatar;
} elseif ($this->email!='') {
$gravatar = md5(strtolower(trim($this->email)));
return "//gravatar.com/avatar/".$gravatar;
}
}
// Set a fun, gender-neutral default icon
@@ -353,6 +359,6 @@ class UserPresenter extends Presenter
public function glyph()
{
return '<i class="fa fa-user"></i>';
return '<i class="fa fa-user" aria-hidden="true"></i>';
}
}

View File

@@ -14,6 +14,7 @@
"doctrine/inflector": "^1.3",
"doctrine/instantiator": "^1.2",
"eduardokum/laravel-mail-auto-embed": "^1.0",
"enshrined/svg-sanitize": "^0.13.3",
"erusev/parsedown": "^1.7",
"fideloper/proxy": "^4.1",
"guzzlehttp/guzzle": "^6.3",
@@ -24,7 +25,7 @@
"laravel/tinker": "^1.0",
"laravelcollective/html": "^5.5",
"league/csv": "^9.2",
"maknz/slack": "^1.7",
"alek13/slack": "^1.7",
"neitanod/forceutf8": "^2.0",
"patchwork/utf8": "^1.3",
"phpdocumentor/reflection-docblock": "^4.0",
@@ -43,7 +44,7 @@
"require-dev": {
"codeception/codeception": "2.3.6",
"filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4",
"fzaninotto/faker": "1.9.1",
"phpunit/php-token-stream": "1.4.11",
"phpunit/phpunit": "~6.0",
"roave/security-advisories": "dev-master",

1854
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -197,19 +197,33 @@ return [
/*
|--------------------------------------------------------------------------
| ALLOW I-FRAMING
|--------------------------------------------------------------------------
|
| Normal users will never need to edit this. This option lets you run
| Snipe-IT within an I-Frame, which is normally disabled by default for
| security reasons, to prevent clickjacking. It should normally be set to false.
|
*/
|--------------------------------------------------------------------------
| ALLOW I-FRAMING
|--------------------------------------------------------------------------
|
| Normal users will never need to edit this. This option lets you run
| Snipe-IT within an I-Frame, which is normally disabled by default for
| security reasons, to prevent clickjacking. It should normally be set to false.
|
*/
'allow_iframing' => env('ALLOW_IFRAMING', false),
/*
|--------------------------------------------------------------------------
| ENABLE HTTP Strict Transport Security (HSTS)
|--------------------------------------------------------------------------
|
| This is set to default false for backwards compatibilty but should be
| set to true if the hosting environment allows it.
|
| See https://scotthelme.co.uk/hsts-the-missing-link-in-tls/
|
*/
'enable_hsts' => env('ENABLE_HSTS', false),
/*
|--------------------------------------------------------------------------
| REFERRER-POLICY

View File

@@ -87,12 +87,14 @@ return [
//'exclude_tables' => ['table1', 'table2'],
//'add_extra_option' => '--optionname=optionvalue',
],
'options' => (env('DB_SSL')) ? [
PDO::MYSQL_ATTR_SSL_KEY => env('DB_SSL_KEY_PATH'), // /path/to/key.pem
PDO::MYSQL_ATTR_SSL_CERT => env('DB_SSL_CERT_PATH'), // /path/to/cert.pem
PDO::MYSQL_ATTR_SSL_CA => env('DB_SSL_CA_PATH'), // /path/to/ca.pem
PDO::MYSQL_ATTR_SSL_CIPHER => env('DB_SSL_CIPHER')
] : []
'options' => (env('DB_SSL')) ? ((env('DB_SSL_IS_PAAS')) ? [
PDO::MYSQL_ATTR_SSL_CA => env('DB_SSL_CA_PATH'), // /path/to/ca.pem
] : [
PDO::MYSQL_ATTR_SSL_KEY => env('DB_SSL_KEY_PATH'), // /path/to/key.pem
PDO::MYSQL_ATTR_SSL_CERT => env('DB_SSL_CERT_PATH'), // /path/to/cert.pem
PDO::MYSQL_ATTR_SSL_CA => env('DB_SSL_CA_PATH'), // /path/to/ca.pem
PDO::MYSQL_ATTR_SSL_CIPHER => env('DB_SSL_CIPHER')
]) : []
],
'pgsql' => [

View File

@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v4.7.8',
'full_app_version' => 'v4.7.8 - build 4170-g4fe689dc5',
'build_version' => '4170',
'app_version' => 'v4.9.5',
'full_app_version' => 'v4.9.5 - build 4482-g5b68a321a',
'build_version' => '4482',
'prerelease_version' => '',
'hash_version' => 'g4fe689dc5',
'full_hash' => 'v4.7.8-7-g4fe689dc5',
'hash_version' => 'g5b68a321a',
'full_hash' => 'v4.9.5-44-g5b68a321a',
'branch' => 'master',
);

View File

@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# fix key if needed
if [ -z "$APP_KEY" ]
@@ -41,6 +41,14 @@ chown -R docker:root /var/lib/snipeit/data/*
chown -R docker:root /var/lib/snipeit/dumps
chown -R docker:root /var/lib/snipeit/keys
# Fix php settings
if [ -v "PHP_UPLOAD_LIMIT" ]
then
echo "Changing upload limit to ${PHP_UPLOAD_LIMIT}"
sed -i "s/^upload_max_filesize.*/upload_max_filesize = ${PHP_UPLOAD_LIMIT}M/" /etc/php/*/apache2/php.ini
fi
# If the Oauth DB files are not present copy the vendor files over to the db migrations
if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ]
then

5190
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,8 +35,9 @@
"jquery.iframe-transport": "^1.0.0",
"less": "less/less.js#efa6eb5306f28a7ef7e235d79ce854b780345591",
"less-loader": "^4.1.0",
"list.js": "^1.5.0",
"papaparse": "^4.3.3",
"select2": "^4.0.3",
"select2": "4.0.13",
"tether": "^1.4.0",
"vue-resource": "^1.3.3"
}

View File

@@ -35,7 +35,7 @@ a, a:link, a:visited, .btn-primary.hover {
background-color: var(--back-sub);
color: var(--header);
}
.btn-primary, .btn-primary.hover, .btn-primary:active, .btn-primary:hover, .text-blue {
.btn-primary, .btn-primary.hover, .btn-primary:active, .btn-primary:hover, .text-green {
color: var(--text-main)!important;
}
#componentsTable>tbody>tr>td>nobr>a>i.fa {
@@ -130,10 +130,10 @@ input[type=text], input[type=search] {
background-color: var(--back-main);
color: var(--text-main);
}
.skin-blue .main-header .navbar .dropdown-menu li a {
.skin-green .main-header .navbar .dropdown-menu li a {
color: var(--header);
}
.skin-blue .sidebar-menu>li.active>a, .skin-blue .sidebar-menu>li:hover>a, .sidebar-toggle:hover {
.skin-green .sidebar-menu>li.active>a, .skin-green .sidebar-menu>li:hover>a, .sidebar-toggle:hover {
background-color: var(--header)!important;
}
.tab-content, .tab-pane {

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.skin-black-light .main-header .navbar{background-color:#111}.skin-black-light .main-header .navbar .nav>li>a{color:#fff}.skin-black-light .main-header .navbar .nav .open>a,.skin-black-light .main-header .navbar .nav .open>a:focus,.skin-black-light .main-header .navbar .nav .open>a:hover,.skin-black-light .main-header .navbar .nav>.active>a,.skin-black-light .main-header .navbar .nav>li>a:active,.skin-black-light .main-header .navbar .nav>li>a:focus,.skin-black-light .main-header .navbar .nav>li>a:hover,.skin-black-light .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-black-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-black-light .main-header .navbar .sidebar-toggle:hover{background-color:#040404}@media (max-width:767px){.skin-black-light .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-black-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-black-light .main-header .navbar .dropdown-menu li a:hover{background:#040404}}.skin-black-light .main-header .logo{background-color:#111;color:#fff;border-bottom:0 solid transparent}.skin-black-light .main-header .logo:hover{background-color:#0e0e0e}.skin-black-light .main-header li.user-header{background-color:#111}.skin-black-light .content-header{background:transparent}.skin-black-light .left-side,.skin-black-light .main-sidebar,.skin-black-light .wrapper{background-color:#f9fafc}.skin-black-light .content-wrapper,.skin-black-light .main-footer{border-left:1px solid #d2d6de}.skin-black-light .user-panel>.info,.skin-black-light .user-panel>.info>a{color:#444}.skin-black-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-black-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-black-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-black-light .sidebar-menu>li.active>a,.skin-black-light .sidebar-menu>li:hover>a{color:#000;background:#f4f4f5}.skin-black-light .sidebar-menu>li.active{border-left-color:#111}.skin-black-light .sidebar-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-black-light .sidebar a{color:#444}.skin-black-light .sidebar a:hover{text-decoration:none}.skin-black-light .treeview-menu>li>a{color:#777}.skin-black-light .treeview-menu>li.active>a,.skin-black-light .treeview-menu>li>a:hover{color:#000}.skin-black-light .treeview-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px}.skin-black-light .sidebar-form .btn,.skin-black-light .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black-light .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black-light .sidebar-form input[type=text]:focus,.skin-black-light .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black-light .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
.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:#fff}.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{color:#4b646f;background:#1a2226}.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{color:#fff;background:#1e282c;border-left-color:#111}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.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-radius:3px;border:1px solid #374850;margin:10px}.skin-black .sidebar-form .btn,.skin-black .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.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{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn,.btn:hover{text-decoration:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#505156;border-color:#b5bbc8;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:#111;text-decoration:underline}a:hover{color:#000}a:visited{color:#111}.text-primary{color:#000}.skin-black .main-header .navbar .nav>li>a{text-decoration:none}
/*# sourceMappingURL=skin-black.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