Compare commits

..

3329 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
snipe
70c8ad9797 Bumped minor version 2019-10-28 13:55:21 -07:00
snipe
0290257734 Limit license seats to 999 to prevent latency 2019-10-28 13:48:18 -07:00
snipe
4fe689dc5d Merge branch 'master' of https://github.com/snipe/snipe-it 2019-10-21 15:45:17 -07:00
snipe
0769f585ea Disallow locations from being their own parents 2019-10-21 15:45:05 -07:00
snipe
04562e6d4a Added 4260352 to ldapsync enabled account constraint 2019-10-18 17:48:50 -07:00
snipe
22d2ad9248 Fixes nested location selectlist (#7483)
* Rename child locations method

* Use Ajax dropdown for locations selectlist for edit/create

* Removed locations database call on edit/create blades for faster loading

* Updated locations controller to use the new iterator

* Increase pagination on locations controller to 500

We’re already loading all of that data up beforehand anyway, so no point in keeping the query smaller.

* Fixed the else to make codacy happy

* Improve the design and performance of the nested location selectlist (#7484)

* Improve the design and performance of the nested location selectlist

* Fixed parse errors

* Removed debugging code/comments
2019-10-02 03:56:56 -07:00
snipe
6deb26fafe Remove unused variable 2019-09-30 19:37:52 -07:00
snipe
6c1de7ff05 Apply fix for #6642 to master 2019-09-30 19:21:57 -07:00
snipe
7f5f4a1297 Added softwarew support and hardware support to maintenance types 2019-09-24 01:34:23 -07:00
snipe
c68c0e1208 Account for limit if none is passed in the request 2019-09-03 20:28:49 -07:00
snipe
c256536d21 Math is hard 2019-09-03 14:29:58 -07:00
snipe
4159a0effa Bumped version 2019-09-03 14:12:13 -07:00
snipe
b8f7cd81eb Limit API request results per page (#7405) 2019-09-03 14:02:08 -07:00
snipe
b381528668 Added console rekey tool (#7330)
* Removed console command specifications, since they’re pulled dynamically now

* Added rekey console command

* Removed unused code

* Comment clarifiction

* Handle LDAP password
2019-09-03 11:03:32 -07:00
snipe
6d66d7e215 Removed withErrors on JSON response 2019-08-22 21:36:47 -07:00
snipe
b5bf8e9a37 Smaller chunking for custom report, add max_execution_time 2019-08-15 06:14:25 -07:00
snipe
ba197c8857 Fixed #7259 - upgraded phpdocumentor/reflection-docblock to v4 2019-08-15 03:02:24 -07:00
snipe
124b249df4 Fixed #7289 - git fetch before checkout in upgrade.php 2019-08-15 01:32:20 -07:00
snipe
2a6919c438 Fixed #7321 - added link to Helm Chart repo 2019-08-15 01:07:30 -07:00
snipe
8b4a9aa382 Fixes to history importer 2019-08-13 18:15:42 -07:00
snipe
99cd552d5c History importer fixes 2019-08-13 18:00:21 -07:00
snipe
6c7e5cb9cf Merge branch 'master' of https://github.com/snipe/snipe-it 2019-08-10 17:49:23 -07:00
snipe
6ebb01a081 Group related variables in .env 2019-08-10 17:48:03 -07:00
Greg Stamper
5591c861b9 Update README.md (#7334)
Add reference to CSV importer.
2019-08-07 23:12:46 -07:00
snipe
d37280567d Fixed CVE-2019-10742
https://nvd.nist.gov/vuln/detail/CVE-2019-10742
2019-08-06 21:11:38 -07:00
snipe
e7b0ee2539 Added accessories checkout/checkin API endpoint 2019-08-02 15:08:26 -07:00
snipe
c593b3645c Added comments to the ByFilter query scope for clarity 2019-07-31 14:24:01 -07:00
Ivan Nieto
28ae90fa8a Added condition to deal with fieldname 'rtd_location' which can be tried to be queried in some places and doesn't exist in database (#7317) 2019-07-31 13:55:21 -07:00
snipe
c7be25078e Bumped version 2019-07-26 13:02:47 -07:00
snipe
3dc2cc9f22 CORS for api (#7292)
* Added CORS support to API

* Changed order so CORS will still work if throttle hit

* Added APP_CORS_ALLOWED_ORIGINS env option

* Fixed typo

* Clarified header comments

* More clarification

* DIsable CORS allowed origins by default to replicate existing behavior

* Change variable name to be clearer
2019-07-26 12:38:31 -07:00
snipe
ab86e42b2e Fixed #7270 - Checking-in Assets via API Removes the Item's Asset Name 2019-07-26 12:37:38 -07:00
snipe
9af9ed9eb9 Add @mskrip as a contributor 2019-07-24 11:01:28 -07:00
snipe
250a797339 Fixed #7250 - permission issue for API fieldsets and fields endpoints
This applies the change from #7294 to master
2019-07-24 11:00:42 -07:00
snipe
a0f3fc6d76 Bumped hash 2019-07-18 14:37:54 -07:00
snipe
74e647fea7 Apply fix from PR #7273 to master 2019-07-18 14:37:48 -07:00
snipe
55ee90b25d Fixes #7252 form request changes (#7272)
* Fixes for #7252 - custom fields not validating / no validaton messages in API w/form requests

* Removed debug info

* More fixes for #7252

This is mostly working as intended, if not yet the way Laravel wants us to do it.

Right now, the API returns correctly, and the form UI will return highlighted errors, with the input filled in ~sometimes~. I’m not sure why it’s only sometimes yet, but this is potentially progress.

* Removed experimental method

* Check for digits_between:0,240 for warranty

* Removed debug code
2019-07-18 14:32:23 -07:00
snipe
eec445fcf5 Command to fix custom field unicode conversion differences between PHP versions (#7263) 2019-07-18 14:30:18 -07:00
snipe
cef22c3158 Caps asset warranty to 20 years 2019-07-18 09:49:58 -07:00
snipe
61fb38087e Bumped hash 2019-07-17 17:55:53 -07:00
snipe
0e93495ca2 Check that the user has assets and that the aset model is valid 2019-07-17 17:51:35 -07:00
snipe
444e250609 Fixed countable() strings on user destroy 2019-07-17 17:51:13 -07:00
snipe
77a6f6f400 Cap warranty months to 3 on the frontend blade 2019-07-17 12:15:15 -07:00
snipe
15bfd07f30 Cap warranty months to three characters
Filles rollbar 209
2019-07-17 12:13:15 -07:00
snipe
fecf8015a1 Added space between footer and custom message 2019-07-17 12:09:32 -07:00
snipe
79ab0d8dc2 Check for valid seat on hardware view 2019-07-17 12:09:18 -07:00
snipe
b4b6d6b571 Comment clarification on #7186 2019-07-15 15:31:09 -07:00
snipe
8c73a47afb Fixed #7186 - has vs filled in User’s API blanking out groups if no group_ids are passed 2019-07-15 15:27:02 -07:00
snipe
f82ffe378c Merge branch 'master' of https://github.com/snipe/snipe-it 2019-07-15 14:11:18 -07:00
snipe
984c2a8fd4 Better log message for bad LDAP connection 2019-07-15 14:10:57 -07:00
snipe
6736b1c4e7 Addresses #7238 - add PWA code to layout
Needs additional UX testing
2019-07-15 13:49:56 -07:00
Ivan Nieto
d409be6d43 Fix #6910: Add logic to manipulate the eloquent query. (#7006)
* Added company_id to consumables_users table

* Added logic to manage when a pivot table doesn't have the column company_id trough a join with users

* Remove a migration that tries to fix this problem, but is not longer necessary
2019-07-15 13:02:44 -07:00
Thomas Misilo
e1b33f3087 Spelling Correction (#7206)
Fixed Spelling for the word reqrite, to be rewrite.
2019-06-27 18:33:13 -07:00
snipe
740d5a6846 Downgrading rollbar for Laravel 5.5 2019-06-25 18:07:21 -07:00
snipe
d19df4ded8 Bumped version 2019-06-24 14:43:44 -07:00
Kasey
03a4512406 fixing previous commit's actual wiping of password (#7183)
replaced Input::fille('ldap_pword') with _filled_.   Should be good to go.  

https://github.com/snipe/snipe-it/issues/7179

https://github.com/snipe/snipe-it/issues/7169
2019-06-19 14:21:53 -07:00
snipe
de992e4df3 Fixed LDAP password blanking on save 2019-06-14 17:20:37 -07:00
snipe
a85251aa83 Fixed #7164 - change table name to permission_groups 2019-06-14 10:37:20 -07:00
snipe
26a1181765 Possible fix for reporting/admin migration back in time 2019-06-12 18:53:53 -07:00
snipe
cef030cf55 Fixed permission insert
//TODO

Handle this via model
2019-06-12 18:39:55 -07:00
snipe
2bfa05fd2d Back in time fix FOR #7145 for new installs on MySQL 8+ 2019-06-12 16:24:55 -07:00
snipe
30904dd019 Reduce minimum group name length to 2 (from 3)
eg: IT
2019-06-12 15:56:19 -07:00
snipe
1d0d25db37 Fixed #7145 - rename groups table to permissions_group for mysql 8 reserved word compatibility 2019-06-12 15:51:47 -07:00
snipe
cbff66c9db Improved error checking in locations importer 2019-06-10 18:50:41 -07:00
snipe
27231d49ea Small fixes for phpleague CSB reader v9 2019-06-03 22:05:16 -07:00
snipe
765417c0be Bumped point version 2019-05-31 14:17:26 -07:00
snipe
49a255c8fb Changed has to filled to fix bulk asset editing 2019-05-31 14:11:43 -07:00
snipe
925d3a23c6 Bumped hash 2019-05-31 13:11:07 -07:00
snipe
6966c132d0 Update language strings 2019-05-31 12:17:09 -07:00
snipe
c29ab90029 Added Filipino, corrected order for Spanish variations 2019-05-31 11:58:14 -07:00
snipe
d2bbc09892 Increased throttle to 120 requests per minute 2019-05-31 11:57:57 -07:00
snipe
74a2c29bc2 Fixed #7100 - Check if $user isset on checkin 2019-05-30 19:06:30 -07:00
snipe
2c64739e8f Removed old comments 2019-05-30 19:02:20 -07:00
snipe
37f950ab42 Fixed #7099 - set email to null by default for backup notifications 2019-05-29 19:09:37 -07:00
snipe
b07a254e60 Bumped version 2019-05-29 15:51:54 -07:00
snipe
087cdd859e Fixed #7092 - handle weird port forwarding/port numbers for baseUrl 2019-05-29 14:57:58 -07:00
snipe
829d44bd27 Fixed #7098 - updated backup config for deleteFile() method 2019-05-29 14:47:55 -07:00
snipe
db76090e10 Missed one 2019-05-28 19:12:53 -07:00
snipe
03cf3b5431 Bumped release again :( 2019-05-28 19:12:42 -07:00
snipe
40f101d471 Merge branch 'master' of https://github.com/snipe/snipe-it 2019-05-28 19:08:39 -07:00
snipe
7aa6ef5f6c Bumped version 2019-05-28 19:08:35 -07:00
Kasey
186b94751d Dockerfile update to bring us up to php v7.1 for Laravel 5.5 (#7084)
* bump up to php7.1

& change deprecated MAINTAINER to a LABEL so it is visible with `docker inspect`

* AND modapache ><

* 2 updates required to get software-properties+ppa
2019-05-28 16:58:32 -07:00
snipe
6f2717a876 Updated branch in version file 2019-05-28 13:22:59 -07:00
snipe
a014af4c47 Fixed #7083 - Removed user_exists constraint on department save
If the user has been deleted, this prevented the department from being successfully saved on edit
2019-05-28 13:18:31 -07:00
snipe
2dd31544fe Increased image size to 800px, added lightboxes 2019-05-24 19:11:08 -07:00
snipe
237acdcff0 Show accessory image on view page 2019-05-24 18:22:57 -07:00
snipe
5db7a7c196 More Atlassian clarifications 2019-05-24 18:10:19 -07:00
snipe
68acdff11b Added link to Atlassian plugin 2019-05-24 18:08:40 -07:00
snipe
58e72e5ee6 Derp - typo 2019-05-24 18:06:15 -07:00
snipe
6b43cd10ba Updated README 2019-05-24 18:05:28 -07:00
snipe
b2c9a38db8 Fixed casing in sync command 2019-05-24 16:12:43 -07:00
snipe
6dcdb5abae Only display the file if the log record can be found 2019-05-24 16:06:52 -07:00
snipe
56576d9e45 Fixed more camel-casing -> snake-casing 2019-05-24 16:01:12 -07:00
snipe
d5c3ee5ed0 Only try to get fieldset if model is valid 2019-05-24 15:44:54 -07:00
snipe
18db0a50f1 Only gtry to delete the file if a record is found in the log 2019-05-24 15:44:40 -07:00
snipe
d596ced0a0 Fixed free_seats_count variable name
(I forgot that Laravel switched camel case to snake case for their old 5.4 withCount variables)
2019-05-24 15:44:18 -07:00
snipe
78fb2b2239 Only build the log upload destination path if there is a matching record
Fixes [ch1232]
2019-05-24 15:28:53 -07:00
snipe
1472e9d5b5 Check for valid model before attempting to access fieldsets
Fixes [ch1249]
2019-05-24 15:03:15 -07:00
snipe
fcbc7e4540 Fixed Undefined variable user in $backto if checked out to a non-user
Fixes [ch9194]
2019-05-24 14:51:27 -07:00
snipe
93bf541ce7 Fixed missed consumables_count withCount() statement 2019-05-24 14:21:53 -07:00
snipe
0e48d7b080 Removed alert-danger from tests 2019-05-24 14:21:37 -07:00
snipe
1e6c85da41 Removed custom fields from AssignedSearch to prevent confusing data in selectlist
Fixes [ch9193]
2019-05-24 13:50:11 -07:00
snipe
c5a23e8f5e Fixed bug where sorting by company name in Users API did not work
Fixes [ch9200]
2019-05-24 13:37:20 -07:00
snipe
b6d2392303 Small fix for reordering fields
Fixes Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'order' cannot be null (SQL: insert into `custom_field_custom_fieldset` (`custom_field_id`, `custom_fieldset_id`, `order`, `required`) values (12, 7, , 0)) [ch1151]

This needs revisiting for a more solid fix, especially for data that was already entered bad.
2019-05-24 12:05:52 -07:00
snipe
9995f1a743 Fixed field mapping 2019-05-24 11:45:07 -07:00
snipe
d6f251e992 Updated importer to work with newer CSV Reader::getRecords() method 2019-05-24 11:44:57 -07:00
snipe
4be95eac4b Removed extra escaping on checkin 2019-05-24 11:44:39 -07:00
snipe
8914d14681 Tidied up license check 2019-05-24 05:26:52 -07:00
snipe
d4725b61be Check that a model exists before trying to fiddle with fieldsets 2019-05-24 04:48:04 -07:00
snipe
aa0b627fe7 Fixed missing asset validation 2019-05-24 04:47:35 -07:00
snipe
5be5e3271d Trying to fix ajax asset validation
This I think gets us closer, but still not handling the validation on the asset properly.

When I do a print_r of the validation in the other items, its looking for an error bag that looks something like this:

```
Illuminate\Support\MessageBag Object
(
    [messages:protected] => Array
        (
            [name] => Array
                (
                    [0] => The name field is required.
                )

            [seats] => Array
                (
                    [0] => The seats field is required.
                )

            [category_id] => Array
                (
                    [0] => The category id field is required.
                )

        )

    [format:protected] => :message
)
```

Currently the Assets ajax returns:

```
[2019-05-24 06:52:06] develop.ERROR: array (
  'messages' =>
  array (
    'model_id' =>
    array (
      0 => 'The model id field is required.',
    ),
    'status_id' =>
    array (
      0 => 'The status id field is required.',
    ),
    'asset_tag' =>
    array (
      0 => 'The asset tag field is required.',
    ),
  ),
)
```

So not sure why it’s not working.
2019-05-24 03:55:31 -07:00
snipe
dd5d5cc07c Handle JSON validation errors like 5.4 2019-05-24 01:12:38 -07:00
snipe
84c3709161 Handle JSON validation errors like 5.4 2019-05-24 01:12:21 -07:00
snipe
96e2d74ae3 Handle JSON validation errors like 5.4 2019-05-24 00:46:30 -07:00
snipe
bf93e8cc32 Use getReader instead of fetchAssoc for CSV parser
https://csv.thephpleague.com/9.0/upgrading/
2019-05-23 19:09:58 -07:00
snipe
d1a8955ef9 Bumped packages 2019-05-23 19:09:14 -07:00
snipe
149ac4bdf8 Removed cosole log 2019-05-23 17:52:53 -07:00
snipe
2d036c64e9 Change ->has() to ->filled() 2019-05-23 17:39:50 -07:00
snipe
8db2470ac4 Switch has() to filled() 2019-05-23 17:17:46 -07:00
snipe
79156ff8f4 Bumped version 2019-05-23 17:10:47 -07:00
snipe
8e86d780bf Fix for included files in backup 2019-05-23 17:08:51 -07:00
snipe
f6ef139111 Fixed baseUrl is undefined error
I literally cannot figure out how this ever worked before.
2019-05-23 16:56:22 -07:00
snipe
12ec2d1f7a Fixed custom field edit screen 2019-05-22 01:07:14 -07:00
snipe
0dfc28b0e8 Bumped laravel version in readme 2019-05-22 01:04:42 -07:00
snipe
b3132a4a8f Updated travis with new php versions 2019-05-22 01:03:11 -07:00
snipe
6cd25fbdeb Updated backup path in backup admin 2019-05-22 00:56:14 -07:00
snipe
4be8ba9f17 Updated withCount to use manual naming 2019-05-22 00:52:51 -07:00
snipe
df8008f1ed Renamed fire() to handle() 2019-05-22 00:52:32 -07:00
snipe
77547c528b Added the command loader to console kernel 2019-05-22 00:52:14 -07:00
snipe
bfb910f375 Set the serialization 2019-05-22 00:51:43 -07:00
snipe
57e80ee317 Removed old laravel backups config
This config file was renamed in a newer version of spatie laravel-backup
2019-05-22 00:51:33 -07:00
snipe
de1189295a Added spatie language files 2019-05-22 00:51:00 -07:00
snipe
20d0dce73e Use laravel v5.5 withCount manual aliases 2019-05-22 00:50:48 -07:00
snipe
144a32b1ca Removed debugbar service provider (autodiscovery) 2019-05-22 00:50:00 -07:00
snipe
8244a2ad23 New backups config for spatie 2019-05-22 00:49:38 -07:00
snipe
77c3b8f8c1 Updated packages 2019-05-22 00:07:49 -07:00
snipe
7b34cf1a31 Bumped version 2019-05-21 19:17:56 -07:00
snipe
21ec670531 Updated language strings 2019-05-21 19:17:00 -07:00
snipe
b2eacb147b Fixed #7046 - added user website url back into UI 2019-05-21 18:55:12 -07:00
snipe
729e3eb70d Updated email strings 2019-05-21 18:43:00 -07:00
snipe
978533b2f4 Fixed XSS vulnerability when creating a new categories, etc via modal on create
Same fix as before, because of the weird select2 post-parsing ajax behavior
2019-05-21 18:29:50 -07:00
snipe
0358d13ddb Fixed #7044 - API update deleted custom fields if they are not re-presented 2019-05-20 11:49:18 -07:00
snipe
c944304444 Updated packages
- Updating erusev/parsedown (v1.7.2 => 1.7.3): Downloading (100%)
  - Updating squizlabs/php_codesniffer (3.4.1 => 3.4.2): Downloading (100%)
  - Updating symfony/polyfill-mbstring (v1.10.0 => v1.11.0): Downloading (100%)
  - Updating symfony/var-dumper (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating league/flysystem (1.0.50 => 1.0.51): Downloading (100%)
  - Updating symfony/translation (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating nesbot/carbon (1.36.2 => 1.37.1): Downloading (100%)
  - Updating symfony/debug (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/console (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/finder (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/polyfill-ctype (v1.10.0 => v1.11.0): Downloading (100%)
  - Updating symfony/polyfill-php70 (v1.10.0 => v1.11.0): Downloading (100%)
  - Updating symfony/http-foundation (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/event-dispatcher (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/http-kernel (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/process (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/routing (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/polyfill-util (v1.10.0 => v1.11.0): Downloading (100%)
  - Updating symfony/polyfill-php56 (v1.10.0 => v1.11.0): Downloading (100%)
  - Updating symfony/psr-http-message-bridge (v1.1.1 => v1.1.2): Downloading (failed)
Downloading (100%)
  - Updating rollbar/rollbar (v1.7.5 => v1.8.1): Downloading (100%)
  - Updating symfony/yaml (v3.4.23 => v3.4.27): Downloading (100%)
  - Updating symfony/browser-kit (v3.4.23 => v3.4.27): Downloading (100%)
2019-05-20 10:10:46 -07:00
Bob Clough
096393389c Fixes #5054: LDAP users deactivated for none-ad (#7032)
When using none-AD ldap, users are automatically deactivated every LDAP
sync.  This commit changes the behaviour so that if the active flag isn't set,
the users are enabled.

Fixed #5054, at least for 4.X
2019-05-16 09:31:55 -07:00
snipe
9eb7b668d1 Fixed #6880 - correctly encrypt encrypted fields via the API 2019-05-15 19:33:30 -07:00
snipe
6728089106 Fixed #6883 - remove escaping of fields on LDAP import 2019-05-15 19:15:41 -07:00
snipe
33b59d7bed Bumped version 2019-05-15 16:45:07 -07:00
snipe
888bdbdb68 Added ability to update groups via API
Fixes [ch9139]
2019-05-15 16:39:34 -07:00
snipe
d67c931f6a Import locations from CSV via command line (#7021)
* Added import locations command

* Small fixes to location importer

* Added country, LDAP OU

* Cleaned up comments, added more clarification to what the script does
2019-05-13 02:27:19 -07:00
snipe
dbdc511eff Merge branch 'master' of https://github.com/snipe/snipe-it 2019-05-08 09:23:58 -04:00
snipe
f47b960566 Added API middleware to API routes to enable throttling
TODO: Figure out how to make this costumizable without touching the code
2019-05-08 09:23:54 -04:00
snipe
d016076806 Fixed #6956 - viewKeys policy inconsistent (#7009)
* Fixed #6956 - Added additional gates show showing/hiding license keys

* Modified gate to allow user to see licenses if they can create or edit the license as well
2019-05-08 08:14:49 -04:00
snipe
23fa5d0bf4 Fixed #7003 - crash when warranty months or purchase date is null 2019-05-07 15:33:57 -04:00
Joris van Eijden
486c708911 Leave the activated state for users alone in normal LDAP synchronisation. (#6988) 2019-05-06 09:40:53 -04:00
snipe
e5c2d77c7d Fixes #6204 - added email alerts and web/API access to assets due for audits (#6992)
* Added upcoming audit report

TODO: Fid diff/threshold math

* Added route to list overdue / upcoming assets via API

* Controller/API methods for due/overdue audits

We could probably skip this and just handle it via view in the routes…

* Added query scopes for due and overdue audits

* Added audit due console command to kernel

* Added ability to pass audit specs to main API asset search method

* Added audit presenter

* Added bootstrap-tables presenter formatter to display an audit button

* Added gated sidenav items to left nav

* Added audit due/overdue blades

* Cleanup on audit due/overdue console command

* Added language strings for audit views

* Fixed :threshold placeholder

* Removed unused setting variable

* Fixed next audit date math

* Added scope for both overdue and upcoming

* Derp. Wrong version

* Bumped version

(I will release this version officially tomorrow)
2019-05-05 22:32:52 -04:00
snipe
ce16eae508 Merge branch 'master' of https://github.com/snipe/snipe-it 2019-05-02 15:20:52 -07:00
snipe
dc73dbfbfd Fixed #6911 - note must be a string on license checkin 2019-05-02 15:20:47 -07:00
snipe
dae26e0378 Remove “Imported from LDAP” note override 2019-04-18 17:56:08 -04:00
snipe
1bb1f7342f Fixed #6922 - date_add crashing if EOL is null 2019-04-18 15:49:59 -04:00
snipe
420e8bc85a Allow phone number to be changed in Profile 2019-04-18 14:13:50 -04:00
snipe
a521523d45 Truncate imports table on seed 2019-04-18 14:13:33 -04:00
snipe
25884a893e Bumped version 2019-03-20 02:41:33 -07:00
snipe
d1e9fbfa24 Updated compoer 2019-03-20 02:37:44 -07:00
snipe
da015ec4a8 Fixed #6834 and #6402 - use inline QR code generation for 2FA (#6840)
* Fixed  #6834 and #6402 - use inline QR code generation for

* Update auth controllers to use translations

* Updated composer lock

* Added comments

* Moar comments

* Typo
2019-03-20 01:24:31 -07:00
snipe
1451b4f45d Merge branch 'master' of https://github.com/snipe/snipe-it 2019-03-18 20:51:11 -07:00
snipe
b6da68a69c Bumped version 2019-03-18 20:50:47 -07:00
snipe
dee92cfc6c Fixes XSS vulnerabilities (#6831)
* Properly escape log_meta values

* Vue syntax fix to allow npm run dev to work again

* Janky fix for Select2 bug

* Compiled production assets

* Escape user’s last name in API

* Removed duplicate alertClass

* Compiled production assets
2019-03-18 20:49:32 -07:00
snipe
dec77890bd Merge branch 'master' of https://github.com/snipe/snipe-it 2019-03-18 11:59:48 -07:00
snipe
0e1289f12f Fixes #6821 - fixed 2 fa active for users list (#6822)
* Fixed #6821 - confusing UI for 2FA when 2FA is universally enforced

I also updated the language in the user’s listing table to clarify what “activated” means

* Added login enabled info to user view

* Clarified comments

* Added info about 2FA on user profile

Because why not

* Added nowrap to table, and added 2FA reset for superadmins
2019-03-18 11:59:02 -07:00
snipe
7b33f95e83 Fixes/import permissions mask (#6826)
* Check for empty headers in import

* Added import permission

* Fixed model path in docblock

* Added import gate to default blade

* Check if the user is an admin OR idf they have import permissions

* Walked back that admin permission

Since admins are bound by full company support, it makes less sense to let admins have this permission by default, versus having them specifically designated to the import permission
2019-03-18 11:58:08 -07:00
snipe
ab6744dfba Merge branch 'master' of https://github.com/snipe/snipe-it 2019-03-14 15:38:20 -07:00
snipe
0fd940ffa4 Check for empty headers in import 2019-03-14 15:38:07 -07:00
Tim Farmer
5893e25b43 Label bugs in alerts settings (#6808)
* Label bugs in alerts settings

Found another label bug where the alert_email label should be alerts_enabled.

* Update alerts.blade.php
2019-03-14 10:53:05 -07:00
snipe
7c3bbe3097 Fixes #6776 hungary date format (#6823)
* Add @timothyfarmer as a contributor

* Fixed #6776 - added hungary time format
2019-03-13 19:39:38 -07:00
snipe
858d382e26 Changed logging to info level for LDAP 2019-03-13 15:14:03 -07:00
snipe
de16fee00a Change image unlink error log to info from error 2019-03-13 12:22:12 -07:00
Tim Farmer
7deab0f53b Label bugs in general settings (#6801)
* checkbox label bug

The checkbox label in general settings for require_accept_signature was set to full_multiple_companies_support instead so when you click the label for require accept signature it would check the wrong box.

* Update general.blade.php

* Update general.blade.php

Another label that is wrong in general settings
2019-03-08 12:24:24 -08:00
Jonathon Reinhart
e59ec8b27f Run Laravel schedule in docker image using supervisord (#6606)
* docker: Rename /entrypoint.sh to /startup.sh

This script is not configured as the docker image ENTRYPOINT, thus it is
misleading to name it so.

* docker: Terminate supervisord if a process enters the FATAL state

By terminating PID 1, this will also terminate the Docker container.

* docker: Use supervisord to start up apache and cron

Note that this uses `apache2ctl -DFOREGROUND` rather than manually
sourcing /etc/apache2/envvars and running apache2, as recommended at
https://advancedweb.hu/2018/07/03/supervisor_docker/.

* docker: Add artisan schedule:run to crontab

This also switches to executing /var/www/html/artisan directly.

* docker: Run artisan schedule:run directly from supervisor

This has the following benefits over using cron:
- Cron doesn't need to be installed
- Docker-provided environment variables are preserved
- It's easy and explicit to run as the docker user
2019-03-07 12:42:00 -08:00
snipe
6d98bd6846 Fixed error if item requested or request was deleted (#6786)
ch628
2019-03-05 23:47:36 -08:00
snipe
58768e5aee Added ability to search consumables by item number (#6785)
Fixes ch1086
2019-03-05 23:21:22 -08:00
snipe
28a450ea25 Added ability to do full name search in user dropdown selectlist (#6784) 2019-03-05 21:13:39 -08:00
snipe
1393f44070 Create FUNDING.yml 2019-03-04 23:57:17 -08:00
snipe
8016939f31 Merge branch 'master' of https://github.com/snipe/snipe-it 2019-03-01 17:39:55 -08:00
snipe
c1ad2f9376 Added link to Marksman - A Windows agent for Snipe-IT
Per https://github.com/Scope-IT/marksman/issues/9#issuecomment-468275142
2019-03-01 17:39:50 -08:00
snipe
9575cd2651 Add accessories endpoint to user API (#6775) 2019-03-01 17:21:03 -08:00
snipe
cf086b711e Merge branch 'master' of https://github.com/snipe/snipe-it 2019-02-22 13:24:58 -08:00
snipe
53db96edad Bumped minor version 2019-02-22 13:24:45 -08:00
snipe
3b62c4a83a Fixes/integrity constraint (#6754)
* Migration to fix nullables

This should fix an issue introduced in 90cddb7aee where we’re passing null instead of an empty string (necessary to nullify values via the API)

* Removed asset migration - serial was already fixed
2019-02-22 13:20:42 -08:00
snipe
5f3147cf36 Added support for enum that was added :( 2019-02-20 21:04:03 -08:00
snipe
738896bdc2 Make serial nullable
Guessing this is new due to later versions of mysql
2019-02-20 20:47:14 -08:00
snipe
e2834fab90 Bumped minor version 2019-02-16 11:48:20 -08:00
snipe
d687e1d762 Fixed #6725 - revert auro_increment_prefix to string
This was mis-migrated as boolean.

It’s essentially a duplicate migration, as it is fixed in-place on the existing migration, and then a new migratio was created to handle anyone who already upgraded.
2019-02-16 11:37:05 -08:00
snipe
6256abddf2 Bumped point release 2019-02-14 15:07:11 -08:00
snipe
b26fbf986f Fixed issue where offset could be greater than total items, resulting in “No results” confusion 2019-02-14 14:49:08 -08:00
snipe
5c9b1ed43a Fixed #6676 - consumables API not respecting category id 2019-02-14 14:48:43 -08:00
snipe
14eb6b387b Possible fix for #6710 - explicitly make auto_increment_prefix nullable (#6716)
* Possible fix for #6710 - explicitly make auto_increment_prefix nullable

* Added migration to make auto_increment_prefix explicitly nullable
2019-02-14 12:46:09 -08:00
snipe
35ebe33e4e Fixed #6703 - fixes password confirmation (#6711)
* Fixed #6703 - fixes password confirmation

* Removed debugging

* Fixed tests

* I guess we use 10 as the settings for password min in tests

* One more try to fix tests - confirmation won’t validate until password validates
2019-02-13 23:01:19 -08:00
snipe
9035707bd6 Bumped point version 2019-02-13 07:40:52 -08:00
snipe
aa1e06f021 One more time…. Fixed #6704 - don’t apply gate to $arrays collection, just check that they can view assets 2019-02-13 04:46:19 -08:00
snipe
30b1cfabf5 Fixed dumb formatting 2019-02-13 04:45:21 -08:00
snipe
e75d22ab73 Revert "Fixed #6704 - don’t apply gate to $arrays collection, just check that they can view assets"
This reverts commit b1e17743b8.
2019-02-13 04:44:19 -08:00
snipe
b1e17743b8 Fixed #6704 - don’t apply gate to $arrays collection, just check that they can view assets 2019-02-13 04:35:55 -08:00
snipe
e2c0f01a10 Fixed #6367 - pass table name and column_id to scopeCompanyables
Solves error: Integrity constraint violation: 1052 Column 'company_id' in where clause is ambiguous
2019-02-13 01:26:11 -08:00
snipe
f88fee0f21 Make user notes field editable via API 2019-02-12 23:58:30 -08:00
snipe
c0669150fb Bumped point version 2019-02-12 23:48:31 -08:00
snipe
f3c12f38b6 Fixed #6061 - Assigned user group cannot be removed
This bug was a result of attempting to check if the groups field had a value, and only THEN trying to sync the groups. This meant that uf you were removing ALL groups, the  sync wouldn’t be triggered.

This still needs to be updated in the API.
2019-02-12 23:43:38 -08:00
snipe
5e19178a30 Do not count deleted locations in managedLocation check on user delete 2019-02-12 23:32:10 -08:00
snipe
90cddb7aee Fixed #6113 - use $asset->fill vs filled() to allow blanking values via API (#6693)
Need to confirm that re-enabling `\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,` won’t mangle anything. I know we ran into some issues when testing a long time ago, but not sure those issues apply anymore, and I can’t remember what they were.
2019-02-12 22:08:38 -08:00
snipe
6d828964be Merge branch 'master' of https://github.com/snipe/snipe-it 2019-02-04 18:58:32 -08:00
snipe
971fcf5800 Fixed #6633 - return 200 status code 2019-02-04 18:58:28 -08:00
Daniel Ruf
2ad270cf33 ci: fix indentation (#6669) 2019-01-30 15:14:17 -08:00
Daniel Ruf
8ce78c6b31 tests: allow to fail on PHP 7.3 and use the latest PHP 7.1 release (#6666) 2019-01-30 14:32:33 -08:00
Daniel Ruf
af3c8195af tests: fix expected string (#6665) 2019-01-30 14:12:20 -08:00
snipe
117b4c59cc Bumped minor version 2019-01-25 21:06:08 -08:00
snipe
194d0733d4 Fixed #6644 - asset name not linked in Reports > Asset Maintenance Report
This report will likely be deprecated.
2019-01-25 20:57:14 -08:00
snipe
a371e8d53f Added calibration as a maintenance type
Should just make these custmizable options
2019-01-24 15:17:33 -08:00
snipe
8f09cca043 Fixed incorrect group route 2019-01-24 15:17:11 -08:00
snipe
39bca49e8f Specify table name in deleted user display 2019-01-24 14:38:18 -08:00
snipe
b8269020ae Specify table name in deleted user display 2019-01-24 14:37:39 -08:00
snipe
601c129bbf Embed images in emails 2019-01-17 20:45:24 -08:00
snipe
b293d00699 Switch LDAP error to debug, to avoid crapping up the logs 2019-01-17 20:18:03 -08:00
snipe
75a0cf97e2 Return an error if asset maintenance is associated with a non-existant asset 2019-01-16 02:19:57 -08:00
snipe
c055e3af21 Only try to return the asset tag link if a valid asset id has been passed 2019-01-16 02:19:35 -08:00
snipe
a1f93e733c Fixed undefined error when maintenance is associated with a deleted asset 2019-01-16 01:45:51 -08:00
fanta8897
49073742b5 Updating LDAP such that each user is not required to be bindable to LDAP (#6571)
* Update Ldap.php

* Update Ldap.php

* Update Ldap.php

* Update Ldap.php

* Update Ldap.php

Updating LDAP.php such that the admin bind will ONLY occur if the user attempting auth cannot bind. If that is the case, it will attempt to bind as admin and search for that user, prior to failing.
2019-01-15 14:04:21 -08:00
Sxderp
187206cb88 Fix saving of REMOTE_USER setting broken by 1a64879b6 (#6565)
The previous commit made it such that remote user login could only
be enabled if two factor authentication was also enabled. Unnest
the configuration so that the setting can be applied without.
2019-01-15 13:59:36 -08:00
Hubert
8420cb7ec1 Fixed problem with import when using snipeit:import command (#6550) 2019-01-15 13:58:23 -08:00
Andrey Bolonin
75252bce05 add php 7.3 (#6556) 2019-01-10 13:21:04 -08:00
snipe
794824713e Bumped minor version 2019-01-03 11:22:06 -08:00
snipe
8f6ea84fca Fixed #4568 - escaping values in custom report 2018-12-12 19:38:24 -08:00
snipe
ea1b792a93 Fixed #6491 - cleaner return methods for PHP 7.3 compact() 2018-12-12 18:23:39 -08:00
Ben RUBSON
4ffb8f14b8 Improve Memcached settings (#6485)
* Improved memcached settings

* Improved memcached settings
2018-12-06 14:39:14 -08:00
Wes Hulette
d023f61bc4 Fixed missing importer (#6413)
Fixed missing manager_id
Fixed missing department_id
2018-12-04 13:06:12 -08:00
snipe
dd5ca73602 Set support footer and version footer to on when resetting the demo 2018-11-08 12:52:37 -08:00
snipe
2632f730d1 Sets activated to 0 in UserImporter if the activated column isn’t set 2018-11-07 22:36:58 -08:00
snipe
24c158bfe6 Added missing use statement for departments in importer 2018-11-07 18:33:43 -08:00
snipe
3d4a5a8066 More importer tweaks for dept and manager 2018-11-07 18:05:53 -08:00
snipe
db7e0b56f2 Fixed department id on asset import with users 2018-11-07 17:36:34 -08:00
snipe
f2478d813c Fix manager id if no manager is given in importer 2018-11-07 17:33:27 -08:00
snipe
192aa9eb71 Fixed #6386 - licenses not searching on category name 2018-11-02 17:14:08 -07:00
Tim Bishop
0eef0fc1dd Sync with develop branch. (#6377)
Without this change argv[1] is ignored.
2018-10-31 11:11:41 -07:00
snipe
81f8fe34cd Removed debugging line 2018-10-30 18:11:27 -07:00
snipe
f744696043 Fixed #6375 - lowercase keys on findAndBindUser to address LDAP syncing issue 2018-10-30 13:12:10 -07:00
snipe
29b0780c6c Added company info to asset maintenances transforrmer 2018-10-30 00:20:16 -07:00
Wes Hulette
6b3b673daa Changed NULL coalesce from ?? (#6353)
PHP 5 does not have the double question mark null coalesce support.
2018-10-26 15:53:18 -07:00
snipe
a4876e9f3e Updated language files 2018-10-26 15:51:38 -07:00
snipe
925258bfb4 Bumped version 2018-10-26 15:29:10 -07:00
snipe
1a10aa0dda Fixed #6349 - add view permission for print all assigned 2018-10-19 16:43:28 -07:00
snipe
d6f8d1b464 Updated composer lock 2018-10-19 16:40:54 -07:00
snipe
09a102fea8 Only try to return a department if there is a matching field 2018-10-19 01:44:45 -07:00
snipe
304fce73fc Null if blank on user import 2018-10-19 01:38:14 -07:00
snipe
295a68bb7a Try false instead of null 2018-10-19 01:36:15 -07:00
snipe
3aeb521782 Patch PR #6335 to master 2018-10-19 01:30:05 -07:00
snipe
f587d2248b WIP: Better handle activation column in importer (#6290)
* Better handle activation column

* Added comments for clarity on importer methods
2018-10-19 00:23:12 -07:00
snipe
8579c5a68a Allow 0 as a consumable min amt 2018-10-15 17:04:51 -07:00
snipe
835b461d7d Fixed #6323 - typo in link for low inventory 2018-10-11 15:31:10 -07:00
Wes Hulette
b8a37a0c73 Fixed Expiring Assets Email (#6321) 2018-10-11 14:03:00 -07:00
snipe
41b5b1dfd0 Bumped hash 2018-10-09 17:32:46 -07:00
snipe
c7596e7741 Fixed image not uploading on asset create 2018-10-09 17:31:52 -07:00
snipe
d4fa81301d Check if user can see assets in statuslabels gate 2018-10-09 16:34:12 -07:00
snipe
ec7245965f Bumped to rollbar 2.4.1
https://github.com/rollbar/rollbar-php-laravel/issues/65
2018-10-04 17:09:12 -07:00
snipe
de76e8db5f Re-enable rollbar 2018-10-04 12:11:36 -07:00
snipe
a52575c7bf Lock rollbar to v2.3.0
https://github.com/rollbar/rollbar-php-laravel/issues/65
https://github.com/rollbar/rollbar-php-laravel/issues/67
2018-10-04 12:11:36 -07:00
Nenad Ticaric
bf6703c2e8 fixing double word typo (#6292)
Thanks!
2018-10-04 09:41:50 -07:00
snipe
4db1dd8afc Fixed #6291 - send-welcome argument in cli importer 2018-10-04 04:43:06 -07:00
snipe
c39e3acb59 Bumped hash 2018-10-04 02:24:41 -07:00
snipe
7a44da85a0 Fixed issue where admin users could disable activation when editing their own profile 2018-10-04 02:18:20 -07:00
snipe
890b613f71 Temporarily suppress rollbar 2018-10-03 15:47:56 -07:00
snipe
1014bd74e0 Updated rollbar 2018-10-03 14:58:14 -07:00
snipe
db385e024b Possible proxy issue fix 2018-10-03 13:04:25 -07:00
snipe
c8bff3ef38 Features/add manager and dept to importer (#6277)
* Ignore the simlink for public storage

* Added manager and department to user import

* More UI importer tweaks

* Fisxed typos
2018-10-02 15:43:54 -07:00
snipe
10bc35d604 Fixed PHP warning Undefined offset: 1 in upgrade.php 2018-09-28 12:13:35 -07:00
snipe
eea65a3f26 Fixed manufacturers item count 2018-09-28 12:03:27 -07:00
snipe
3b21a19491 Updated language strings 2018-09-28 11:54:52 -07:00
snipe
7a52477294 Added Icelandic and Serbian to locale list 2018-09-28 11:47:59 -07:00
snipe
ef0bd72076 Bumped version 2018-09-28 11:21:39 -07:00
snipe
ff824ec4db Fixes #6252 - activated flag not checked when editing active user 2018-09-28 11:18:33 -07:00
snipe
75032def9e Fixed #4151 - Undefined index: samaccountname on LDAP import 2018-09-27 16:11:09 -07:00
snipe
3a0f738fb0 Added some hepful comments 2018-09-26 19:20:50 -07:00
snipe
55846cc717 Changed LOG:: to Log:: 2018-09-26 19:06:31 -07:00
snipe
1784278a59 Fixed importer email test 2018-09-26 19:06:09 -07:00
snipe
afac0bc441 Removed old isActive model (unused) 2018-09-26 19:05:42 -07:00
snipe
ffbee77f6f Patch for 5965 - multiple email recipients no longer working (#6238) 2018-09-26 15:47:53 -07:00
snipe
b69b5fdf84 Added counts to location show() API method 2018-09-21 15:50:14 -07:00
Brady Wetherington
89e06054bf Merge pull request #6205 from dasjoe/patch-1
Fixes #5630
2018-09-17 21:45:48 -07:00
Hajo Möller
3159e7713a Fixes #5630 2018-09-11 13:07:45 +02:00
snipe
adf6e7d1cd Added group support for user API 2018-09-07 18:25:58 -07:00
snipe
ba3662a9ed Bumped hash 2018-09-07 18:12:29 -07:00
snipe
05ea61421f Added manager_id to fillable for locations API 2018-09-07 18:11:38 -07:00
snipe
22ef2ce0b6 Bumped hash 2018-09-07 03:20:31 -07:00
snipe
51d3d130e4 Fixed Not unique table/alias: 'models' on custom report triggered when category is selected 2018-09-07 03:19:54 -07:00
snipe
d8a8e1cc09 Bumped version 2018-08-28 11:43:56 -07:00
snipe
522dc1db2a Fixed #6124 2018-08-23 21:05:10 -07:00
snipe
db907815ff Removed check for active in password reset form 2018-08-21 18:40:27 -07:00
snipe
ae6abdddad Check the user is active before displaying password reset
This would only come into play if an inactive user already received a password reset email and then the system was upgraded to prevent those emails from being sent to inactive users
2018-08-14 19:04:47 -07:00
snipe
63c9fbe10c Temporarily disbable notifying users on import 2018-08-14 18:27:46 -07:00
snipe
101dfd01f2 Bumped hash 2018-08-14 18:22:53 -07:00
snipe
5db5134ae0 Set activated to default on when new user is created 2018-08-14 18:14:41 -07:00
snipe
5294489b0e Fixed HTML typo 2018-08-14 18:14:29 -07:00
snipe
05b2b8fb59 Tweaked code/language for password reset 2018-08-14 18:09:33 -07:00
snipe
0100c56046 Only allow password reset if user is active 2018-08-14 17:46:29 -07:00
Jordi Boggiano
e81b221fd1 Fix license identifier (#6043) 2018-08-08 20:36:45 -07:00
snipe
f374ac1bf7 Removed duplicate BACKUP_ENV in example env 2018-08-01 15:31:43 -07:00
snipe
524c6c502e Features/restore deleted cmd (#5982)
* Delete content from login attempts table

* Script to restore deleted users and put their asset assignments back

* Uncomment backup
2018-07-27 02:42:55 -07:00
snipe
614e858e44 Restrict users asset listing to just assets checked out to users 2018-07-25 21:38:14 -07:00
snipe
708b1a962c Split out custom_css from custom_header
This makes it so that the custom_css will still be respected even if there is no custom header
2018-07-25 19:01:47 -07:00
snipe
4e55a18a60 Bumped version 2018-07-24 19:46:23 -07:00
snipe
3de1de9dc6 Merge branch 'develop' 2018-07-24 14:04:16 -07:00
snipe
e320d2ba05 Fixed #5944 - added logo option for print-assets page (#5950) 2018-07-24 13:37:02 -07:00
snipe
ed78a4b8a0 Fixed activated issue for strict mode 2018-07-24 13:28:59 -07:00
snipe
376eb52f00 Fixed #5938 - added “self location edit” as permission 2018-07-24 12:42:16 -07:00
snipe
8ecceeacda Fixed weird display for self options 2018-07-24 12:41:58 -07:00
snipe
f4cfb31bf4 Use request object 2018-07-24 12:10:02 -07:00
snipe
227dc7e81d Save model display setting - fix for issue in #5301 2018-07-24 12:10:02 -07:00
snipe
152d985ebc Add @Azerothian as a contributor 2018-07-24 12:10:02 -07:00
Earl Ramirez
ef1e8df001 Disable file browsing (#5922)
* Added cron to list of packages

* Disable file browsing from the public directory
2018-07-23 20:40:04 -07:00
snipe
5c2b1a3b70 Add @Azerothian as a contributor 2018-07-23 20:29:15 -07:00
Azerothian
66c3f5432d implemented specific seat checkout (#5887) 2018-07-23 20:28:45 -07:00
Daniel Meltzer
de413408f5 Port/reenable most unit tests. (#5921)
* Port/reenable most unit tests.

Should probably flesh out notifications tests in the next few days.

* Disable json checkin in ApiAssetsTest@index for now.  It's broken, but hiding other real broken things.

* Re Disable Groups allowDelete
2018-07-23 06:48:21 -07:00
Daniel Meltzer
059126f642 Checkout update locationid (#5919)
* Fix missing punctuation.  Bad merge.

* If we're checking out to an location, use it's id instead of location_id
2018-07-23 06:47:21 -07:00
Daniel Meltzer
3bc43210ab Add ID to the allowed sort fields in api/Users. (#5929) 2018-07-23 06:46:50 -07:00
Daniel Meltzer
82194cef8a bugfix: updating a user when an admin (not a superuser) would remove any groups from the user. (#5914) 2018-07-21 23:02:06 -07:00
snipe
1956a16d1e Merge branch 'develop' 2018-07-20 13:25:03 -07:00
Till Deeke
e1c095adca Removes the typehint for search term string (#5904)
The „string“ typehint only works in PHP >= 7.0.0.
Since we are still supporting versions below that, remove the type hint.
2018-07-20 13:23:44 -07:00
Till Deeke
45a2932f4b Fixes the generation of where conditions (#5902) 2018-07-20 13:23:29 -07:00
Till Deeke
b6e3715cd8 Fix: No Notifications for checking out Consumables (#5898)
* Adds a method to consumables to check if a notification should be sent

Adds the checkin_email method to Consumables, this gets checked in notifications when checking out the consumable.

Without the method, no notifications get sent for checking out consumables.

* Fixes the checkin_email method on the License model

This should allow the License to also send checkout/checkin notifications again.
2018-07-20 13:22:49 -07:00
snipe
aa9c0078a1 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-07-19 14:47:27 -07:00
snipe
9677115055 Bumped version 2018-07-19 14:46:08 -07:00
snipe
d45e90e358 One more fix for #5893 2018-07-19 14:45:28 -07:00
snipe
f692fb5bff Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-07-19 10:50:18 -07:00
snipe
83692696ba Bumped version 2018-07-19 10:49:40 -07:00
snipe
bbb15d610f Merge branch 'develop' 2018-07-19 10:40:38 -07:00
snipe
7ebb7876c4 Partial fix for #5896
Still need to fix the front end on edit, which seems to be defaulting to boolean
2018-07-19 10:40:07 -07:00
snipe
bd581d01a3 Use full row width for image field 2018-07-19 10:40:07 -07:00
snipe
de2ebba577 Category add/edit UI tweaks 2018-07-19 10:40:07 -07:00
Daniel Meltzer
351274c633 Hotfix: the checkin_email does not exist on consumable. (#5891) 2018-07-19 10:38:50 -07:00
snipe
53fab3e9ba Merge branch 'develop' 2018-07-19 10:23:18 -07:00
snipe
a0c0b7b1eb Fixed #5893 - activated typo 2018-07-19 10:22:08 -07:00
snipe
5a34d43a86 Fixed #5895 - wrong date validation in maintenances 2018-07-19 10:19:55 -07:00
snipe
86a0f77e6f Merge branch 'develop' 2018-07-19 10:15:38 -07:00
snipe
9d00ae6e50 Fixed #5894 - lookup by asset tag in top search broken 2018-07-19 10:14:02 -07:00
snipe
8d23398176 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-07-18 20:07:14 -07:00
snipe
5aae930e8c Bumped version 2018-07-18 20:05:48 -07:00
snipe
6db096c336 Add @takuy as a contributor 2018-07-18 20:04:09 -07:00
snipe
78d27e82c8 Updated language strings 2018-07-18 20:03:31 -07:00
Sam
2a4fef6a61 adds select-id as an option for the asset-select partial. allows you to manually override the ID of an element. currently, the bulk-checkout partial has two asset-selects, with conflicting IDs. attempts to resolve #5882 (#5883) 2018-07-18 19:55:13 -07:00
snipe
9daeeeb851 Features/nicer notifications (#5886)
* Improved expiring licenses notification

* Improved expiring assets notification

* Nicee low inventory notification

* Refactored stupid language strings

* Oops

* Use settings variable
2018-07-18 19:15:45 -07:00
Daniel Meltzer
92671823d8 Userimport fixes/improvements (#5884)
* Send notification when user is created.
* Flesh out default user mappings
* Add user importing test.
2018-07-18 19:15:07 -07:00
Daniel Meltzer
19396b2107 Logic Fix (#5877)
If we have a username, we should look that up even if we do not have a
first name.
2018-07-18 19:10:36 -07:00
Brady Wetherington
f0332a7388 Add DB port number to docker
which gets copied to the docker image's `.env` file during installation. This closes #5718
2018-07-18 19:10:06 -07:00
snipe
70cb5ed215 Updated language strings 2018-07-18 08:55:38 -07:00
snipe
d309f67df0 Set activated to zero if no values passed for active user 2018-07-18 08:27:26 -07:00
snipe
7dc070ec2c Add @engrzhou as a contributor 2018-07-18 07:39:46 -07:00
snipe
2ecdf19569 Add @GodUseVPN as a contributor 2018-07-18 07:39:23 -07:00
snipe
c56f2625b7 Add @grayhoax as a contributor 2018-07-18 07:38:57 -07:00
snipe
3dc154991a Add @wira-sandy as a contributor 2018-07-18 07:37:56 -07:00
snipe
a5f57c050f Add @vipsystem as a contributor 2018-07-18 07:37:30 -07:00
snipe
36c4cd98e0 Add @vinzruzell as a contributor 2018-07-18 07:36:31 -07:00
snipe
f6ef6039f4 Add @fraccie as a contributor 2018-07-18 07:35:55 -07:00
snipe
6867563e4a Add @dheche as a contributor 2018-07-18 07:33:19 -07:00
snipe
21820262ce Add @reinvanhaaren as a contributor 2018-07-18 07:29:12 -07:00
snipe
bae1203b64 Add @ragnarcx as a contributor 2018-07-18 07:28:41 -07:00
snipe
0f2ff7aba2 Add @ProfFan as a contributor 2018-07-18 07:28:03 -07:00
snipe
0ce90834f6 Add @priatna as a contributor 2018-07-18 07:27:42 -07:00
snipe
0e182bb2b9 Add @bodrovics as a contributor 2018-07-18 07:26:54 -07:00
snipe
7afff69fab Add @pawel1615 as a contributor 2018-07-18 07:26:12 -07:00
snipe
136d59e4b9 Add @drcryo as a contributor 2018-07-18 07:25:49 -07:00
snipe
6d9cbac928 Add @pooot as a contributor 2018-07-18 07:25:25 -07:00
snipe
efcd8d339b Add @saymd as a contributor 2018-07-18 07:24:51 -07:00
snipe
7e09ce468b Add @omego as a contributor 2018-07-18 07:24:08 -07:00
snipe
baf714b41f Add @MohammedFota as a contributor 2018-07-18 07:22:56 -07:00
snipe
b0808e846d Add @IxFail as a contributor 2018-07-18 07:22:15 -07:00
snipe
0e3e10a707 Add @mikaelssen as a contributor 2018-07-18 07:21:32 -07:00
snipe
cbf3d5d071 Add @micaelrodrigues as a contributor 2018-07-18 07:20:20 -07:00
snipe
68d355a33d Add @meyerf99 as a contributor 2018-07-18 07:19:59 -07:00
snipe
853923013a Add @stubben as a contributor 2018-07-18 07:19:00 -07:00
snipe
737061a983 Add @msjohansen as a contributor 2018-07-18 07:17:34 -07:00
snipe
0438d55284 Add @mariejoyacajes as a contributor 2018-07-18 07:17:08 -07:00
snipe
924962c156 Add @MarcosBL as a contributor 2018-07-18 07:16:30 -07:00
snipe
a8c42843f6 Add @lstrojny as a contributor 2018-07-18 07:15:10 -07:00
snipe
63c4b7b6b6 Add @laopangzi as a contributor 2018-07-18 07:14:47 -07:00
snipe
ff06387d90 Add @joxelito94 as a contributor 2018-07-18 07:11:31 -07:00
snipe
fa46622bad Add @JohnWillker as a contributor 2018-07-18 07:07:13 -07:00
snipe
fd65780287 Add @jarby1211 as a contributor 2018-07-18 07:05:34 -07:00
snipe
6478ec86ac Add @itangiang as a contributor 2018-07-18 07:04:23 -07:00
snipe
748e0eb44f Add @igolman as a contributor 2018-07-18 07:03:30 -07:00
snipe
507c07e9eb Add @abaalkh as a contributor 2018-07-18 07:03:09 -07:00
snipe
5185a9e590 Add @husnulyaqien as a contributor 2018-07-18 07:02:34 -07:00
snipe
1ade088a8f Add @Kentsson as a contributor 2018-07-18 07:00:50 -07:00
snipe
5c087181be Add @fofwisdom as a contributor 2018-07-18 07:00:21 -07:00
snipe
08bb314bbc Add @Hafidzi as a contributor 2018-07-18 06:59:43 -07:00
snipe
7ddb9becd4 Add @AdnanAbuShahad as a contributor 2018-07-18 06:59:03 -07:00
snipe
7d96709ee2 Add @mrgluek as a contributor 2018-07-18 06:58:28 -07:00
snipe
4413a9f0b6 Add @jgroblesr85 as a contributor 2018-07-18 06:58:10 -07:00
snipe
9611b55fb9 Add @georgwallisch as a contributor 2018-07-18 06:57:39 -07:00
snipe
dffd535f68 Add @gdraque as a contributor 2018-07-18 06:57:17 -07:00
snipe
4aaf2da651 Add @possebon as a contributor 2018-07-18 06:55:47 -07:00
snipe
80ac46af78 Add @fgbs as a contributor 2018-07-18 06:55:12 -07:00
snipe
13c58b54fa Add @frapposelli as a contributor 2018-07-18 06:54:39 -07:00
snipe
355119187a Add @Erlpil as a contributor 2018-07-18 06:54:03 -07:00
snipe
0d96587b04 Add @EpixFr as a contributor 2018-07-18 06:53:30 -07:00
snipe
be806def91 Add @dominiksenti as a contributor 2018-07-18 06:50:59 -07:00
snipe
8acf8027e9 Add @danielcb as a contributor 2018-07-18 06:49:25 -07:00
snipe
893a1fec57 Add @danielheene as a contributor 2018-07-18 06:48:58 -07:00
snipe
d182e40aef Add @da-friedl as a contributor 2018-07-18 06:48:34 -07:00
snipe
6118aca949 Add @dpyroc as a contributor 2018-07-18 06:48:11 -07:00
snipe
f0ac83179f Add @Wxcafe as a contributor 2018-07-18 06:47:18 -07:00
snipe
8969ffc14b Update @CronKz as a contributor 2018-07-18 06:46:45 -07:00
snipe
9fb130146a Add @kopi-item as a contributor 2018-07-18 06:46:24 -07:00
snipe
d285ff673a Add @Againstreality as a contributor 2018-07-18 06:46:00 -07:00
snipe
7f586da856 Add @cwlin0416 as a contributor 2018-07-18 06:45:24 -07:00
snipe
c35f5b3116 Add @chibacityblues as a contributor 2018-07-18 06:44:40 -07:00
snipe
12927d2d54 Add @rudashi as a contributor 2018-07-18 06:43:43 -07:00
snipe
6b06b87547 Add @benunter as a contributor 2018-07-18 06:42:30 -07:00
snipe
058f8ae3b8 Add @aschiavon91 as a contributor 2018-07-18 06:40:14 -07:00
snipe
1faedac04b Add @angeldeejay as a contributor 2018-07-18 06:39:39 -07:00
snipe
666c7321af Add @xelan as a contributor 2018-07-18 06:39:05 -07:00
snipe
6cbaf7396a Add @sirrus as a contributor 2018-07-18 06:38:25 -07:00
snipe
b706acaa9f Add @RealEnder as a contributor 2018-07-18 06:36:42 -07:00
snipe
5870acb193 Add @albertoaldrigo as a contributor 2018-07-18 06:36:04 -07:00
snipe
c00b633312 Add @a-royal as a contributor 2018-07-18 06:33:05 -07:00
snipe
1c0ee7c4c5 Fixed license checkout gate 2018-07-18 05:33:14 -07:00
snipe
961741b809 Fixed license checkout gate 2018-07-18 05:31:59 -07:00
snipe
5a66f0f373 Include show_in_list option in select 2018-07-18 04:00:04 -07:00
snipe
5a1e1c73c9 Include show_in_list option in select 2018-07-18 03:59:02 -07:00
snipe
3be68ec721 Fix location edit permissions 2018-07-18 03:43:45 -07:00
snipe
0f81e494ee Fix location update permission 2018-07-18 03:42:43 -07:00
snipe
d88f66b019 Updated to stale.yml 2018-07-18 02:57:05 -07:00
snipe
027edbdb21 Fixed #5872 - asset maintenances listing showing created_at instead of start_date 2018-07-18 02:24:53 -07:00
Daniel Meltzer
cf03d25934 Fix importer emailformat (#5871)
* Fix Importer emailformat

Str::slug() strips periods from the string, which caused our existing
logic to misbehave when generating a user's email on an import.  Adjust
logic to use generateEmail() helper on user instead.  Also clean up some
of the logic in this method.

* Remove dead code.

* More refactor/cleanup of the user create method.  I think it is almost readable now.
2018-07-17 16:46:08 -07:00
snipe
75232d2a70 Added link to jamf2snipe 2018-07-17 14:17:13 -07:00
snipe
bcb966af12 Updated composer 2018-07-17 14:17:02 -07:00
snipe
0272e58868 Add @DeusMaximus as a contributor 2018-07-17 01:11:39 -07:00
snipe
bcd988bb81 Merge branch 'develop' of https://github.com/DeusMaximus/snipe-it into develop
# Conflicts:
#	app/Http/Controllers/Auth/LoginController.php
2018-07-17 01:11:15 -07:00
snipe
aa6c21f38d Fixed typo 2018-07-17 01:03:32 -07:00
snipe
3fb5f6f5be Revert 2018-07-17 01:03:25 -07:00
snipe
5dc2ac9e22 Try to fix test
This is a listing, not a single asset
2018-07-17 00:20:06 -07:00
snipe
0f85d6810b Added login log 2018-07-16 23:49:08 -07:00
snipe
bf761946da Fix activated check for login 2018-07-16 23:48:46 -07:00
snipe
d9fa2f0e91 Fixed #5842 - added components to location detail view 2018-07-16 21:50:14 -07:00
snipe
8a25677a8d Bumped hash 2018-07-16 21:35:21 -07:00
DeusMaximus
7c2da81700 Fix REMOTE_USER Header with IIS and AD
Remove DOMAIN\ portion of DOMAIN\user when using Windows Authentication and IIS with REMOTE_USER.
2018-07-17 14:03:19 +10:00
snipe
a4799a495a Fixes #5859 - add file name/size to file upload UI (#5861)
* Fixes #5859 - add file name/size to file upload UI

* Reverting assetcontroller

Not sure exactly what happened here…

* Production assets
2018-07-16 20:09:53 -07:00
Till Deeke
b5de5ac19c Fix: Searching for multiple terms on assets (#5860)
* Give advancedTextSearch all search terms at one

The additional conditions for assets had some problems, since they were joining tables for the additional attributes. The method was called once for every search term, so the join was added multiple times if the user entered multiple search terms.

* Allows search to handle multiple search terms better

The search now better handles multiple search terms, adding additional orWhere clauses, instead of duplicating all queries.

* Fixing typo
2018-07-16 17:44:31 -07:00
Daniel Meltzer
638a7b2d91 Assetcontroller cleanup (#5858)
* Extract method/cleanup

* Remove apiStore method that is unusued since api controllers.

* Use proper model exception

* Remove old user importer.  This is now supported by the general importer framework.

* Refactor AssetsController methods.

This is a giant diff without many functional changes, mostly cosmetic.
I've pulled a number of methods out of assetscontroller, preferring
instead to create some more targetted controllers for related actions.
I think this cleans up the file some, and suggests some places for
future targetted improvement.

Fix weird missing things.

* Fix Unit test failing after date changes.

* Pass valid string to be translated.

* Some method cleanup for codacy.

* Extract trait for common checkout uses and codacy fixes.
2018-07-16 17:44:03 -07:00
Daniel Meltzer
dcf72ce2da Strip timestamps from comparison for api tests, in an attempt to fix the test failures. (#5847) 2018-07-16 14:41:51 -07:00
Daniel Meltzer
8f6e0ad5be Only error if checking out to asset with same id (#5845)
A user with an id of 2 is perfectly fine as a checkout_target of an asset with an id of 2.
2018-07-16 14:25:36 -07:00
Daniel Meltzer
50e0b9b84e category_id not category_i (#5844) 2018-07-16 14:23:24 -07:00
Daniel Meltzer
b6b93550fe Remove old helpers (#5843)
* Cleanup model bulk-edit

Use the general partials where appropriate, as well as display a list of
what models we are editing in the bulk edit.

* Use new api based fetch/display for modal select2.

This is just copy/pasting the code currently because I'm not entirely
sure how the two pieces of code interact.

* Remove old helper functions that are no longer necessary with our populating of select2 dropdowns via ajax.
2018-07-16 14:22:25 -07:00
Till Deeke
baa3be728d Refactoring: A nicer and easier syntax for searching models (#5841)
* Adds the ability to search by dates

Adding extra „where“-conditions to the „TextSearch“ queries, allowing the users to search by dates

* Adds missing dates to $dates in models

* Removes duplicated „where“ conditions

* Adds the Searchable trait to models, defining the searchable attributes and relations

* Removes the old text search methods

* Adds back additional conditions to the search

These conditions could not be modeled in the „attributes“ or „relations“, so we include them here

* Removes unnecessary check for the deleted_at attribute

* Fixes typo in comments

* suppresses errors from Codacy

We can safely ignore the error codacy is throwing here, since this method is a standin/noop for models who need to implement more advanced searches
2018-07-16 14:13:07 -07:00
Till Deeke
240e642fe9 Removes the unused bulk operations for components (#5840) 2018-07-16 14:11:38 -07:00
Till Deeke
07a92d20d7 Fixing #5773: Refactoring the "clearing" of select2 lists (#5839)
* adds select2 placeholders to select lists

To allow us to clear the selection on „select2“ selects, we need a placeholder attribute

See: https://select2.org/placeholders

* Removes empty option from multiple select

select2 requires an empty option value on singular selects, but not on multiple selects.

When selecting multiple options, this empty option would be shown as selectable otherwise, not clearing the selection.

* Adds the option to clear select2 instances

Sets the correct options to allow clearing of out select2 instances. The empty placeholder is required, since clearing only works when a placeholder ist set (event an empty one).

See: https://select2.org/placeholders

* Removes the „Clear selection“ option from select lists

Since we can clear the select2 lists with their native clearing method, we can remove this hack

* Updates generated assets (css/js)
2018-07-16 14:10:54 -07:00
Till Deeke
3f334406d1 Fixing #5470: Checkin emails not working (#5838)
* Always send checkin notifications to users

This fixes the routing of the notifications, to only send „checkin“ emails if the „mail on checkin“ flag on the category was set. (and we checkout to a user with a non-empty email)

* Fixes checkout notification routing

Notifications to users should be send if the category of the resource (accessory/asset/consumable/license):

a) requires the user to confirm acceptance
b) should send notifications on checkin/checkout

* adds a check for EULAs

Adds back a check for the EULA, since the user should receive the EULA if it was set (regardless of other setings on the category, etc)
2018-07-16 14:09:04 -07:00
Till Deeke
dbd177576e Brings back the „pending“ state when checking out (#5837)
When a user would get an asset checked out for them, and the assets category required acceptance of the asset, the „pending“ state would not get set.
2018-07-16 14:07:24 -07:00
Till Deeke
0fb9f42ba4 Removes setting the encryption status on update (#5833)
When we are updating a custom field, we don’t want to change the „field_encrypted“-setting on it.
2018-07-13 04:04:30 -07:00
snipe
b4542d4d42 Fix migration for models on labels 2018-07-12 21:49:44 -07:00
snipe
4bfd7a7e4e Add @chemfy as a contributor 2018-07-12 18:28:43 -07:00
snipe
3fe1562b92 Add @jasonlshelton as a contributor 2018-07-12 18:28:43 -07:00
Till Deeke
27699aa99c Adds permission checks for custom fields and custom fieldsets (#5645) (#5795)
* adds permission checks to custom fields

* adds permission checks to custom fieldsets

* adds separate permissions for custom fieldsets

* check for permissions in views

* Removes custom fieldsets from permissions config

* Proxy the authorization for custom fieldsets down to custom fields.

This allows us to use the existing permissions in use and have more semantically correct authorization checks for custom fieldsets.

* simplifies the authorization check for the custom fields overview

* removes special handling of custom fieldsets in base policy

I just realised that this code duplicates the logic from the custom fieldset policy.
Since we are checking for the authorization of custom fields anyway, we can just use the columnName for the fields.

* cleanup of unused imports
2018-07-12 18:28:20 -07:00
Till Deeke
48bbbe0f40 Fixing authorization issues (#5807)
* adds permission checks for companies

* adds permission checks for depreciations

* adds permission check for all reports

* fixes permissions for departments

* fixes permission naming (edit -> update)

* fixes authorization checking wrong permission in API

The authorization was checking for the non-existent „edit“ method where it should have checked for the „update“ method.

* adds authorization checks for select2 lists

* adds missing authorization checks for api

* fixes user authorization check for creating users

* adds additional check viewing assets on showing a users assets

* Removes authorization checks for select2 lists

Reference: https://github.com/snipe/snipe-it/pull/5807#pullrequestreview-136018755
2018-07-12 18:28:02 -07:00
Antti
9dc226e3d6 Feature: PostgreSQL support (#5642)
* Made migrations work with pgsql and changing empty integers to null

* Fixed the last functional test
2018-07-12 18:24:43 -07:00
Jason
98b20fc1cd Added option to include model information on asset labels. (#5301)
* Added option to include model information on asset labels.

Cleaned up label page to fix skewed label alignment on last row per page.

* Changes made per Snipe's direction

changed type from tinyint to boolean in DB
changed labels back to initials
2018-07-12 18:23:12 -07:00
snipe
980dccf31c Add @5quirrel as a contributor 2018-07-12 18:19:35 -07:00
snipe
bb2193d481 Add @tilldeeke as a contributor 2018-07-12 18:19:14 -07:00
5quirrel
bf8fe316df Fix for #4901 (#5829) 2018-07-12 16:45:12 -07:00
snipe
96716626c6 Fixed #5828 - typo 2018-07-12 14:07:07 -07:00
snipe
e20338ff9e Merge branch 'develop' 2018-07-09 22:38:04 -07:00
snipe
78530ae123 Fix tests 2018-07-09 21:57:45 -07:00
snipe
52d605d13e Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-07-09 19:21:23 -07:00
snipe
0182615e7e Bumped version 2018-07-09 19:18:38 -07:00
snipe
fad84d4437 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-07-09 19:07:49 -07:00
snipe
c162e9a4de Bumped hash 2018-07-09 19:06:56 -07:00
snipe
bf1e742df6 Merge branch 'develop' 2018-07-09 19:05:19 -07:00
snipe
0e88a6b268 Fixed bug in branding image upload size text 2018-07-09 19:04:18 -07:00
Till Deeke
c1e870528e Fixes the label association (#5510) (#5790) 2018-07-09 14:51:17 -07:00
snipe
35fc001c58 Fixed #5742 - create_function() is deprecated 2018-07-05 20:49:01 -07:00
snipe
339263a295 Fixed #5751 - added option for unique constraint on serial 2018-07-05 19:30:36 -07:00
snipe
a44bd9abe0 Disallow deleting category if there are licenses 2018-07-05 18:02:25 -07:00
snipe
b850d47282 Merge branch 'develop' 2018-07-05 15:40:07 -07:00
snipe
4099c06b27 fix middleware priority: handle trusted proxies prior setup check
From @plexorama
2018-07-05 15:36:59 -07:00
snipe
e559879f91 Add @plexorama as a contributor 2018-07-05 15:35:37 -07:00
snipe
abb95e7872 Tweaked custom field default value layout
This still needs work. It’s ugly.
2018-07-05 15:31:27 -07:00
snipe
869de3d251 Fixed broken pagination on status labels API 2018-07-05 14:42:39 -07:00
snipe
f3526eccb9 Merge branch 'features/textarea-custom-field' into develop
# Conflicts:
#	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
2018-07-05 12:37:07 -07:00
Daniel Meltzer
880faa83a6 Importer2 checkout (#5771)
* Importer: checkout to location, backend changes+tests.

* Import location checkout. Frontend changes.

* Allow importing of item number/model number for consumables.
2018-07-05 12:22:24 -07:00
Juan Font
311f9fcefb Implemented method to get info on the current user of the API (#5722)
* Implemented method to get info on the current user of the API

* Move userinfo method to UsersController

* Added missing files
2018-07-02 20:35:10 -07:00
snipe
8732f299e6 Added logo class for logo override in custom CSS 2018-07-02 18:47:30 -07:00
Arunas Skirius
b30aac536a fixed the alignment of a couple navbar icons (#5764) 2018-07-02 18:10:25 -07:00
snipe
d7dc4ae0c0 Added manager to custom report 2018-06-27 00:45:09 -07:00
snipe
5bb4c85ccb Update twitter handle 2018-06-27 00:44:54 -07:00
snipe
80dda198c5 Parse line breaks in the detail view 2018-06-21 09:44:10 -07:00
snipe
9442736518 Adds textarea as a custom field type 2018-06-21 09:35:04 -07:00
snipe
2fbad52c71 Merge branch 'develop' 2018-06-21 07:54:01 -07:00
snipe
5975c9fac7 Add @ParadoxGuitarist as a contributor 2018-06-21 07:52:24 -07:00
snipe
7b0e392ecd Add @thelamer as a contributor 2018-06-21 07:52:04 -07:00
snipe
b51a10b46b Add @RichardRay as a contributor 2018-06-21 07:51:39 -07:00
snipe
6c58f59d72 Add @EarlRamirez as a contributor 2018-06-21 07:51:24 -07:00
snipe
cd9caa24ad Add @SjamonDaal as a contributor 2018-06-21 07:51:13 -07:00
snipe
cbb4b4d846 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-06-21 07:46:27 -07:00
snipe
ea4cdadc6e Bumped version 2018-06-21 07:44:58 -07:00
snipe
6638d64d68 Merge remote-tracking branch 'origin/master' into develop 2018-06-21 07:43:32 -07:00
snipe
eb412c2bcb Missed one 2018-06-21 07:43:12 -07:00
snipe
fde4a59510 Bumped version 2018-06-21 07:40:37 -07:00
snipe
1ee394aa69 Added Select2 to class for dropdowns 2018-06-21 07:40:30 -07:00
snipe
707f90573c Merge branch 'thelamer-docker-fixes' into develop 2018-06-21 07:31:55 -07:00
snipe
f8429ad357 Merge branch 'docker-fixes' of https://github.com/thelamer/snipe-it into thelamer-docker-fixes 2018-06-21 07:31:15 -07:00
snipe
aa5003d297 Merge branch 'develop' 2018-06-21 07:21:24 -07:00
snipe
e9901f5e58 Set composer timeout to 3000 2018-06-21 07:17:51 -07:00
Djamon Staal
f0d04a4a57 End help text with a period consistently. (#5731) 2018-06-21 07:13:29 -07:00
Djamon Staal
32e3f748d8 Make version footer configurable. (#5730) 2018-06-21 07:12:16 -07:00
Earl Ramirez
fa465a84df Added cron to list of packages (#5729) 2018-06-20 23:28:35 -07:00
Earl Ramirez
82cf1a4467 Updated SELinux label (#5728) 2018-06-20 23:28:20 -07:00
tiagom62
3bbd49dbad Don't run composer as root (#5689)
* dont run composer as root

* better naming
2018-06-20 19:59:44 -07:00
Daniel Meltzer
ad21857cae Update my email address across files. (#5716) 2018-06-20 01:59:59 -07:00
Daniel Meltzer
2d18b73138 Fix #5408. (#5715)
The temporary password cannot be added to the users data until after do
any update-related logic, otherwise their password will be overwritten.
2018-06-20 01:59:04 -07:00
Richard Ray Thomas
e7bc18dad4 Fixed inconsistent or incorrect comment labels (#5691)
Accessories table was labeled 'Checked out License table' likely just a duplicate of the above comment for the actual licenses table. Very minor.
2018-06-11 20:11:58 -07:00
snipe
62e4eabab0 Fixed #5693 - don’t truncate license key 2018-06-11 18:47:02 -07:00
tiagom62
d204eebab9 Ubuntu Bionic Beaver 18.04 support (#5687)
* bionic box

* bionic beaver support
2018-06-09 11:21:28 -07:00
thelamer
ed5823151b in order to avoid manual user intervention of running "php artisan migrate" before installation on a clean database pulling in migration scripts on startup if needed, also ownership of keys needs to be the docker user 2018-06-07 22:29:00 -07:00
snipe
968d7d1f11 Fixed #5354 - adds dd/mm/yyyy to localization 2018-06-05 15:30:50 -07:00
snipe
086683319a Fixed #5172 - autosum on assets detail view for components tab 2018-06-02 06:21:51 -07:00
snipe
e5c1f4847d Added emoji to exempt list to match current labels 2018-06-01 13:55:39 -07:00
snipe
087e114d34 Merge branch 'develop' 2018-05-31 16:34:25 -07:00
snipe
5a6b8bb856 Make default custom field value null 2018-05-31 14:21:18 -07:00
snipe
102f567cb5 Fixed typo 2018-05-31 10:55:48 -07:00
snipe
1a64879b65 Only allow remote user settings to be saved if the app is not in demo mode 2018-05-31 10:55:48 -07:00
Brady Wetherington
4a0e5e4b88 Possible fix for oauth issue in Docker 2018-05-31 01:36:51 -07:00
snipe
01857fb056 Added created_at to file upload UI, added header/footer to files modal, fixed string for actions 2018-05-29 16:38:23 -07:00
snipe
3afe4938f9 Fixed #5616 - removed duplicate call to ekkoLightbox on asset view
It’s already in the default blade
2018-05-29 16:36:42 -07:00
snipe
9e0544e735 Fixed #5617 - incorrect url to fieldset editing 2018-05-29 10:41:46 -07:00
snipe
3993c6ad6b Use custom header color in emails 2018-05-29 10:23:39 -07:00
Brian Monroe
649563457d Added notes field to acessories and consumable checkout pages. Resolves request #5607. (#5608) 2018-05-28 14:30:18 -07:00
snipe
6ec75714f0 Force default srtorage engine 2018-05-22 14:57:05 -07:00
snipe
15916e6668 Allow checkout to non-user objects even if the object requires checkout 2018-05-21 17:35:04 -07:00
snipe
76d0562716 Added last name to expected checkin notification
For non-US countries like Germany where it’s considered rude not to include last name
2018-05-21 17:34:27 -07:00
snipe
2ecc4aead7 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-21 15:43:48 -07:00
snipe
d8210847a4 Bumped version 2018-05-21 15:43:04 -07:00
snipe
ece916e12f Merge branch 'develop' 2018-05-21 15:41:40 -07:00
snipe
1a29d4f60f Check for > 0 expected assets 2018-05-21 15:41:19 -07:00
snipe
11832daf5c Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-21 15:17:40 -07:00
snipe
79555f5647 Bumped version 2018-05-21 15:16:56 -07:00
snipe
1868013704 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-21 14:59:08 -07:00
snipe
39cc13f155 Bumped version 2018-05-21 14:58:16 -07:00
snipe
e636875797 Merge branch 'develop' 2018-05-21 14:56:57 -07:00
snipe
260749337e Updated languages 2018-05-21 14:54:36 -07:00
snipe
20a3b556bb Removed log 2018-05-21 14:33:27 -07:00
Tim Bishop
fa0c58e42a Don't install require-dev packages. (#5549)
This is already set for a local composer install, but not a global
one. They should be consistent.
2018-05-18 16:06:05 -07:00
snipe
abbb94239d Add @doekman as a contributor 2018-05-18 16:05:12 -07:00
snipe
d89ef43834 Make category counters ints 2018-05-18 16:05:12 -07:00
Doeke Zanstra
f84ab2beda Fixed bug #5540 (#5550)
Error message with button "Test LDAP" is empty #5540
2018-05-18 16:04:34 -07:00
snipe
86d398abda Fixed category test 2018-05-18 14:58:06 -07:00
snipe
883c65981b More test fixes 2018-05-16 20:09:18 -07:00
snipe
8eb96efa13 Merge branch 'develop' 2018-05-16 19:38:23 -07:00
snipe
e9973670ea Could should not be equal to 1 2018-05-16 19:38:02 -07:00
snipe
ef8d2d06df Fixes #5519 - count() for php 7.2 2018-05-16 19:35:14 -07:00
snipe
0b5bb520a7 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-16 19:24:31 -07:00
lea-mink
233fb23cb8 Create asset maintenance - Added orange bar for required asset to edit view (#5520)
* Added orange bar for required asset to edit view

* disable redirection to asset maintenances view

* Update - disable redirection to asset maintenances view
2018-05-16 19:23:23 -07:00
snipe
fa89f45cb8 Bumped hash 2018-05-16 19:22:59 -07:00
snipe
4c656c0321 De-normalize new counters from 4.3.0 (#5547)
* Added de-norm counter migration for assets

* Renaming counter columns, since Eloquent has a magical *_count helper

* Added artisan command to sync counters (one-off)

* Update API to use de-normed fields

* Increment counters for checkin;/checkout

* Derp.

* Added request increment/decrementer

* Move increment for checkout to the Asset::checkout method

* Added “could take a while” message
2018-05-16 19:20:43 -07:00
snipe
87c6ee2035 Importer test fixes 2018-05-16 19:05:00 -07:00
snipe
aab190423f Partial fix for license+category tests 2018-05-16 18:35:11 -07:00
snipe
05e3e6bda6 Added indexes for speed 2018-05-14 20:27:56 -07:00
Tim Bishop
1f299ed73e Fix padding on next page with Chrome. (#5509)
It looks like Chrome (and probably other browsers) is optimising the
next-padding div out since it has no content. This means the margin
doesn't apply. Adding the nbsp, and making sure it takes up no space
itself, is enough to make the margin do the right thing.

I also tried using padding instead of margin, but this results in an
extra page at the end of the output (there'd need to be a way to stop
page-break and next-padding appearing at the end of the document). I also
tried setting a min-height on next-padding, but this had the same issue.
2018-05-09 17:00:36 -07:00
snipe
4ba9792fbe Merge branch 'develop' 2018-05-09 15:29:59 -07:00
snipe
f405511b6b Fixed #5501 - regression disallowing license files to be downloaded 2018-05-08 14:24:51 -07:00
snipe
8ad5eb3e59 Fixed #5500 - present() on correct location value 2018-05-08 09:21:43 -07:00
snipe
3df8fa99f0 Merge branch 'develop' 2018-05-08 07:37:44 -07:00
snipe
cca97341e9 More flexible date range in datepicker for expected checkin filter on custom reports 2018-05-08 07:37:18 -07:00
snipe
13195d06fd Fixed #5491 - added default location filter for custom report 2018-05-08 07:34:14 -07:00
snipe
9b6e86b55c Merge branch 'develop' 2018-05-08 05:59:54 -07:00
snipe
65cf7527b0 Added model name to expected checkin reminders 2018-05-08 05:59:34 -07:00
snipe
f74d50439c Merge branch 'develop' 2018-05-08 05:39:28 -07:00
snipe
28e7ca5a84 Formatting tweaks to email notifications 2018-05-08 05:39:11 -07:00
snipe
8f64da5bc7 Added admin alert on expiring notifications 2018-05-08 05:27:03 -07:00
snipe
25f537e730 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-08 03:49:28 -07:00
snipe
6d8af3d9c0 Bumped hash 2018-05-08 03:48:35 -07:00
snipe
e56a46882d Include EULA/acceptance in license interfaces 2018-05-08 03:47:28 -07:00
snipe
0476ffecdb Removed debugging comments 2018-05-08 03:46:48 -07:00
snipe
47a0400a72 Fixed comment typo 2018-05-08 03:14:25 -07:00
snipe
de3417d557 Added link to SnipeitPS powershell wrapper 2018-05-08 01:34:53 -07:00
snipe
83b546c1c5 Disable privacy policy link in footer if in demo mode 2018-05-08 01:07:15 -07:00
snipe
04709dc1df Fixed #5477 - added GDPR privacy policy link in email and webpage 2018-05-08 00:50:13 -07:00
snipe
f48171dcab Add category to licenses 2018-05-08 00:14:38 -07:00
snipe
7b8362b64c Added license categories 2018-05-04 21:01:38 -07:00
snipe
188538651a Fixed slack notification error if location is not set on checkin 2018-05-04 21:01:25 -07:00
snipe
a9fc7e04e9 Fixed php7.2 count issue 2018-05-04 21:00:58 -07:00
snipe
4812285512 Fixed #5482 - typo in custom fields info 2018-05-04 14:52:19 -07:00
snipe
ec1fa8e90a Merge branch 'develop' 2018-05-03 08:06:58 -07:00
snipe
3a1b432234 Fixed #5472 - show_in_email for custom fields missing in edit field UI 2018-05-03 08:06:28 -07:00
snipe
98f853128a Merge branch 'develop' 2018-05-03 05:43:49 -07:00
snipe
276d2bc866 Fixed advanced search on model number
(RB:347)
2018-05-03 05:43:25 -07:00
snipe
0472e3a3e5 Merge branch 'develop' 2018-05-02 14:41:10 -07:00
lea-mink
a0afa9f2e8 Modified the affectation of the value of the password in credential mail sent for the first user sign up (#5446)
* Modified the affectation of the value of the password

* Remove e()
2018-05-02 14:40:41 -07:00
snipe
0116fa9b95 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-05-02 14:35:16 -07:00
snipe
42f0eebf8f Bumped version 2018-05-02 14:33:01 -07:00
snipe
8194660d16 Updated languages 2018-05-02 14:32:50 -07:00
snipe
532a9ef9fe Additional languages in language selector 2018-05-02 14:20:42 -07:00
snipe
0be69f57ac Improved files display 2018-05-02 14:13:06 -07:00
snipe
97f748d58e Removed old reports methods and routes
We only use the custom asset report now
2018-05-02 03:44:31 -07:00
snipe
3662a58ad8 Removed unusued parameters in BS table formmatters 2018-05-01 21:35:07 -07:00
snipe
f5bf6a0beb Added larger icon size 2018-05-01 21:34:31 -07:00
snipe
0fc0aa39b1 Use flexible language string for confirm delete 2018-05-01 21:33:51 -07:00
snipe
c8cf46f62b Fixed #5431 - category widget error on dashboard 2018-05-01 21:33:03 -07:00
tiagom62
28a1960fda PHP 7.1 for all (#5456)
* PHP 7.1 for all

* fedora support. strict versioning.

* Add fedora 26 and 27
2018-05-01 21:02:14 -07:00
Brady Wetherington
44bb79c90e Fix to lockfile, update to package.json to non-vuln versions (#5460) 2018-05-01 21:01:45 -07:00
snipe
2f01bb2e63 Update issue templates 2018-05-01 21:01:27 -07:00
snipe
71708e349c PHP7.2 count fixes (#5427)
* PHP 7.2 count() fixes

* Re-enable php travis 7.2
2018-04-29 06:10:49 -07:00
tiagom62
6ad3d40216 installer automation? (#5433)
* cli

* less spaces!

* rename functions

* move progress spinner variables into progress ()

* $hosts was used in one place

* missed this one

* make testing snipeit.sh easier
2018-04-29 06:09:46 -07:00
snipe
d6d498bc8f Fixes incorrect gate for “new” button for status labels on asset create/edit form 2018-04-26 16:35:08 -07:00
snipe
6df7f6d6ec Fixes wrong users index route name 2018-04-26 16:31:02 -07:00
snipe
5365182c86 Fixed advanced search on supplier, count for PHP7.2 2018-04-25 20:25:03 -07:00
snipe
e5121b33e6 Shorter syntax for demo disabled 2018-04-25 19:23:03 -07:00
snipe
5fb4eacf5b Disable history importer on the demo 2018-04-25 19:18:05 -07:00
Hannah Tinkler
c4c520c1a3 Fixes #4445: prevents assigned assets from being checked out in bulk checkout (#5421)
* Fixes #4445: prevents assigned assets from being checked out in bulk checkout

* Updates data attribute to more versatile 'data-asset-status-type'

* Fixes broken unit test
2018-04-25 02:39:23 -07:00
snipe
9eab9ad40d Merge branch 'develop' 2018-04-24 16:25:42 -07:00
snipe
a2fef11016 Use Bootstrap Tables on custom fields screens for column selector 2018-04-24 16:25:10 -07:00
snipe
088eb3da14 Merge branch 'develop' 2018-04-24 13:24:24 -07:00
snipe
a0706b8780 Added gitignore 2018-04-24 13:24:15 -07:00
snipe
0e1dfcf408 Changed directory for audits image dir 2018-04-24 13:20:15 -07:00
snipe
3ca9f5f389 Merge branch 'develop' 2018-04-24 12:49:28 -07:00
snipe
5acd225f0f Fixed #5423 - removed required text on preflight 2018-04-24 12:48:58 -07:00
snipe
1708bb5cdf Fixes #5422 - remove extension ending from uploaded file name 2018-04-24 12:47:09 -07:00
snipe
8127484081 Better error checking for private file display method 2018-04-24 03:12:30 -07:00
snipe
103c75e78c Removed max cap in image validation 2018-04-24 03:12:17 -07:00
snipe
d886dcc7c3 Reset skin for demo 2018-04-24 03:00:56 -07:00
snipe
ea54d73911 Merge branch 'develop' 2018-04-24 02:59:19 -07:00
snipe
1ef4cc9fc2 Fixed #4301 - added image upload to audit 2018-04-24 02:54:54 -07:00
snipe
4785db4471 Update factory format for custom fields 2018-04-23 21:24:59 -07:00
lea-mink
c8cbc55b59 Bulk Checkout to Assets and Location (#5385) 2018-04-23 21:24:49 -07:00
Hannah Tinkler
8d501e1c24 Feature/custom fields default values (#5389)
* Fixes CustomFieldsetsController::fields() which I think is not used anywhere else and don't think ever worked as you can't call get() on a Collection.
Have tested extensively and doesn't seem to affect anywhere else?

* Adds default value functionality

* Adds built assets

* Fixes assignment to asset_model_id which should have been evaluation and alters route so it sits more in line with existing work

* Updates built assets

* Remove silly docker.env file; fix Dockerfile to preserve Oauth keys (#5377)

* Added department to custom asset export
Updates build assets

* Adds translation support for 'add default values' checkbox label
2018-04-23 21:16:55 -07:00
snipe
132a5d424d Check for valid accessory category 2018-04-23 16:04:01 -07:00
snipe
4c5f20fde4 Merge branch 'develop'
# Conflicts:
#	app/Importer/Importer.php
#	config/version.php
2018-04-23 13:33:34 -07:00
snipe
35a20fb197 Fixes #5410 - missing subject string for license delivery 2018-04-23 13:30:32 -07:00
snipe
63848e8fc2 Additional rules for non-green skins 2018-04-23 13:30:32 -07:00
lea-mink
16a7409ce1 Matched Asset currency and Asset Maintenance currency in Asset Maintenance view (#5386)
* Matched Asset currency and Asset Maintenance currency in Asset Maintenance editing view

* Cleaning code & add condition on currency of an asset location
2018-04-20 14:03:31 -07:00
Stephen
c23955d0b5 Allow setting of "ldap_import" through the API (#5218)
* Allow setting of "ldap_import" through the API, this will allow cusom scripts to be made to import data from Active directory using the API, this would allow any field to be filled such as the manager (based on the ID), department etc.

* Password fix for LDAP through API
2018-04-20 14:02:52 -07:00
Tim F
18ef355d2a Updated rules (#5325)
Added .modal-content, as I either forgot it previously or an update started calling it for popups on the New Asset pages.

Brought .main-header, .navbar, .main-header, and .logo into the fold with the variables -- good addition there :)
2018-04-20 13:56:45 -07:00
snipe
def1eb0b5f Bumped hash 2018-04-20 13:52:32 -07:00
snipe
bdbc189a4f Bumped hash 2018-04-20 13:51:54 -07:00
snipe
6efe9efab8 Fixes #5393 - added notes to suppliers API (#5400) 2018-04-19 18:28:22 -07:00
Daniel Meltzer
7b72dde222 Another importer fix. (#5383)
* Fix condition where matching user fails when providing a username but no full name.  Also shortcircuit username matching if a user exists.

* Simplify Logic

If the user provided is numeric, but doesn't exist in the database, assume that the user's name is a number and go through all relevant generation.  of email/first+last names.  Alternatively we may want to abort or remove the is_numeric bits.. it seems a little counterintuitive
2018-04-18 07:58:26 -07:00
snipe
5948a0b235 Added department to custom asset export 2018-04-16 20:10:38 -07:00
Brady Wetherington
a6fc7ba07a Remove silly docker.env file; fix Dockerfile to preserve Oauth keys (#5377) 2018-04-16 16:29:42 -07:00
snipe
a326adc863 Add @hannahtinkler as a contributor 2018-04-13 14:55:11 -07:00
snipe
ab31c633d0 Possible fix for #5211 2018-04-13 14:55:11 -07:00
Hannah Tinkler
48254a93f0 Fixes #5338 - mark required setup fields during setup (#5359) 2018-04-13 14:52:32 -07:00
snipe
365c8c18d7 Fixed #5319 - signature pad too small on mobile 2018-04-06 19:19:31 -07:00
snipe
bbc0695a8f Added count of checkins, checkouts, requests (#5314)
* Added count of checkins, checkouts, requests

* Removed old commented items

* Use actionlog instead of redefining the relationship
2018-04-06 16:23:39 -07:00
snipe
1d0f8f01f2 Fixed #5266 - small scroll window for EULA accept screen 2018-04-06 15:50:45 -07:00
snipe
2253439940 Added default location/address to custom report 2018-04-05 17:33:25 -07:00
snipe
c1838a60df Bumped hash 2018-04-04 17:36:47 -07:00
snipe
8a6713d5c0 WIP - Improved requested assets (#5289)
* WIP - beginning of improved requested assets

- Use Ajax tables for faster loading
- Use new notifications for requesting an asset

TODO:
- Use ajax tables for requestable asset models
- Use new notifications for canceling an asset request
- Expire requests once the asset has been checked out to the requesting user

* Only show asset name in email if it has one

* Refactor requested method to only include non-canceled requests

* Refactored requestable assets to log request and cancelation

* Added softdeletes on checkout requests

* Differentiate between canceling and deleting requests

* Added asset request cancelation notification

* Added timestamps and corrected unique key on requests table

* Improved requests view

* Re-use blade for cancel/request email

* Refactored BS table formatter for requested assets

* Location name min reduced to 2

* Added PAT test as maintenance option

This needs to be refactored into database-driven options with a UI

* Better slack message

* Added getImageUrl method for assets

* Include qty in request notifications

TODO:
- Try to pull requested info from original request for cancelation, otherwise it will default to 1

* Removed old asset request/cancel emails

* Added user profile asset request routes

* Added profile controller requested assets method

* Added blade link to requested assets for profile view

* Sort user history desc

* Added requested assets blade

* Added canceled at to checkoutRequest method

* Include qty in request

* Fixed comment, removed allowed_columns

* Removed Queable methods, since we don’t use a queue

* Fixed return type in method doc

* Fixed version number

* Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
snipe
201efecafa Fixed #5293 - component category drilldown 2018-04-02 16:12:19 -07:00
Daniel Meltzer
79f061be93 Move first_name and last_name to only be displayed for user importer. Also sort items alphabetically regardless of their source. (#5292) 2018-03-30 19:32:43 -07:00
snipe
4786c1c59f Check for custom fields in Importer 2018-03-30 18:50:09 -07:00
snipe
116cad88a0 Fixed #5279 - [regression] edit button not appearing in asset view 2018-03-29 11:28:37 -07:00
snipe
7d1200c434 Add @lea-mink as a contributor 2018-03-29 05:32:52 -07:00
lea-mink
99a9707a34 Add title field in Asset Maintenances list/filter/export (#5287) 2018-03-29 05:32:09 -07:00
Daniel Meltzer
787f2390fb Add location_id to fillable (#5286)
Should fix #5268
2018-03-29 05:11:07 -07:00
snipe
a510ac4052 Fixed #5272 - make city min length 2 instead of 3 2018-03-29 04:36:18 -07:00
snipe
9f414baa99 Remove notifiable from asset 2018-03-28 19:08:08 -07:00
snipe
086711e467 Fixed checkout to checkin in loggable checkin trait 2018-03-28 19:01:51 -07:00
Daniel Meltzer
bf2d6dd0fa Avoid populating db manually. (#5255)
* Avoid populating db manually.  Instead rely on a seeded database existing and use api/fucntional tests based on that.

* Seed the Setting object with default values.

* Update Setting seeder to match web default.  Also only generate one Setting instance.
2018-03-28 19:01:51 -07:00
Daniel Meltzer
4c43226b99 Fix #4798. (#5271)
Filter down the custom fields we are iterating over to only custom fields in the header row.
This prevents nulling out fields when performing an import-update that doesn't include
all custom fields as well as properly nulls out values between items being imported.
2018-03-28 19:01:51 -07:00
snipe
dfe8aac10b Fixed checkout to checkin in loggable checkin trait 2018-03-28 19:01:10 -07:00
snipe
983786c29f Fixed component checkout bug 2018-03-28 18:53:18 -07:00
snipe
edb81425cf Fixed component checkout bug (#5282) 2018-03-28 18:53:03 -07:00
Daniel Meltzer
69478aea58 Avoid populating db manually. (#5255)
* Avoid populating db manually.  Instead rely on a seeded database existing and use api/fucntional tests based on that.

* Seed the Setting object with default values.

* Update Setting seeder to match web default.  Also only generate one Setting instance.
2018-03-27 17:43:28 -07:00
Daniel Meltzer
e9d9c0c42d Fix #4798. (#5271)
Filter down the custom fields we are iterating over to only custom fields in the header row.
This prevents nulling out fields when performing an import-update that doesn't include
all custom fields as well as properly nulls out values between items being imported.
2018-03-27 16:52:44 -07:00
snipe
b41adc2eee Merge branch 'develop' 2018-03-26 16:05:50 -07:00
snipe
115d6e29df Added location address to custom asset report export 2018-03-26 15:59:09 -07:00
snipe
83781f3a70 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-03-26 15:20:40 -07:00
snipe
5f1ec550ec Fixed hash 2018-03-26 15:16:35 -07:00
snipe
95f8dccc14 Bumped hash 2018-03-26 15:15:43 -07:00
snipe
0134ec7b04 Added asset presenter, fixed asset maintenances button in asset view 2018-03-26 14:49:49 -07:00
snipe
590938fa33 Added asset maintenances presenter, fixed broken action buttons on asset view 2018-03-26 14:46:37 -07:00
snipe
46f5f21368 Notification improvements (#5254)
* Added “show fields in email” to custom fields

* Added “show images in email” to settings

* Added nicer HTML emails

* Break notifications out into their own, instead of trying to mash them all together

* Remove old notification for accessory checkout

* Janky fix for #5076 - “The asset you have attempted to accept was not checked out to you”

* Add method for image url for accessories

* Added accessory checkout email blade

* Make accessory email notification on checkout screen consistent with assets

* Added native consumables notifications

* Fixes for asset notification

* Updated notification blades with correct-er fields

* Updated notifications

* License checkin notification - does not work yet

Need to figure out whether the license seat is assigned to a person or an asset before we can pass the target

* Added alternate “cc” email for admins

* Only try to trigger notifications if the target is a user

* Fix tests

* Fixed consumable URL

* Removed unused notification

* Pass target type in params

* Show slack status

* Pass additional parameters

There is a logic bug in this :( Will send to slack twice, since the admin CC and the user are both using the same notification. Fuckity fuck fuck fuck.

* Pass a variable to the notification to supress the duplicate slack message

* Slack is broken :( Trying to fix

Will try a git bisect

* Put preview back into checkout

* Pulled old archaic mail

* Removed debugging

* Fixed wrong email title

* Fixed slack endpoint not firing

* Poobot, we hardly knew ye.

* Removed old, manual mail from API

* Typo :-/

* Code cleanup

* Use defined formatted date in JSON

* Use static properties for checkin/checkout notifiers for cleaner code

* Removed debugging

* Use date formatter

* Fixed target_type

* Fixed language in consumable email
2018-03-25 13:46:57 -07:00
snipe
b6bf3800c7 Merge branch 'develop' 2018-03-23 16:20:09 -07:00
snipe
6043d37b05 Added the backup env flag to the example env 2018-03-23 16:19:26 -07:00
snipe
34919b0396 Added API calls to look up assets by tag and serial 2018-03-23 14:50:11 -07:00
snipe
846613c244 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-03-22 20:48:02 -07:00
snipe
c4c137dc08 Bumped to 4.2.0 2018-03-22 20:47:09 -07:00
snipe
130bb19a11 Merge branch 'develop' 2018-03-22 20:31:04 -07:00
snipe
d0f14d7a3c Fixed #5186 - email being sent to admin as well as user on checkout
This should be introduced as a setting, but not arbitrarily send
2018-03-22 20:30:45 -07:00
snipe
795c2cf540 Merge branch 'develop' 2018-03-22 20:27:40 -07:00
snipe
bcd02ce718 Inelegantly fixed #5186 - placeholder notification being sent
This needs work
2018-03-22 20:27:14 -07:00
snipe
469efc923b Merge branch 'develop'
# Conflicts:
#	README.md
#	config/version.php
2018-03-22 20:08:55 -07:00
snipe
dcd74f6922 Bumped version 2018-03-22 20:07:06 -07:00
snipe
4b7eaf6dae Updated translations 2018-03-22 20:01:45 -07:00
Brady Wetherington
085f909f35 Change duplicate header check to return 1-based header indices 2018-03-22 19:40:12 -07:00
snipe
4f394b683d Added test CSV with blank column headers 2018-03-22 19:35:43 -07:00
snipe
7096ebedbc Sample CSV with duplicated headers 2018-03-22 19:22:28 -07:00
Brady Wetherington
0b0243a5e0 Hoist file-reading before file-renaming, catch duplicate headers (#5244) 2018-03-22 19:16:15 -07:00
snipe
5c9f9b1685 Add @cepacs as a contributor 2018-03-22 15:49:46 -07:00
snipe
bf74bb196d Merge branch 'develop' 2018-03-22 14:57:15 -07:00
snipe
c6e14caa31 Removed e() from array_smart_custom_field_fetch 2018-03-22 14:36:36 -07:00
snipe
506602b257 Remove escaping on import value checker 2018-03-22 14:25:52 -07:00
snipe
a79ec0d408 Merge branch 'develop' 2018-03-22 14:18:30 -07:00
snipe
140724be2e Fixed #5217 - permissions error with importer for non-admins 2018-03-22 14:17:44 -07:00
snipe
04af1f3c97 Moved remote user stuff below password security stuff 2018-03-22 14:00:35 -07:00
snipe
cb4f1daac1 Fix for a bad merge - the migration was changed between master and develop :( 2018-03-22 13:45:55 -07:00
snipe
96bab5697d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
#	database/migrations/2018_02_22_160436_add_remote_user_settings.php
2018-03-22 09:46:50 -07:00
snipe
abac3e7758 Bumped hash 2018-03-22 09:44:54 -07:00
snipe
8557cb5305 Added - make accessory manufacturer field searchable 2018-03-22 09:43:53 -07:00
tiagom62
735840aafd Revert removing setting hostname (#5208) 2018-03-21 19:09:37 -07:00
David Kaatz
1c777888d5 Authentication via REMOTE_USER (clean repull) (#5204)
- Implementation in Login
- Configuration
- Database migration
2018-03-21 19:09:37 -07:00
Brady Wetherington
625810cd8a Support \r-terminated files better from the Importer (#5184)
* Hoist the ini_get/ini_set auto-detect line endings code higher

* Placate the irascible Codacy
2018-03-21 19:08:06 -07:00
Anh DAO-DUY
cd63abd72e Add default_label field missing in status_labels table (Travis CI) (#5180) 2018-03-21 19:08:06 -07:00
snipe
d911b776bf Check for associated maintenance asset
This should probably never happen, but triggers on the demo sometimes because of fluctuating data seeding
2018-03-21 19:08:06 -07:00
tiagom62
9e7d1b3ed8 Revert removing setting hostname (#5208) 2018-03-15 10:20:42 -07:00
David Kaatz
53735f2026 Authentication via REMOTE_USER (clean repull) (#5204)
- Implementation in Login
- Configuration
- Database migration
2018-03-14 12:48:07 -07:00
David Kaatz
a43b31400f Authentication via REMOTE_USER (#5142)
* Added authentication via Remote User

* - Removed nullable from remote_user settings fileds and used just default values instead
- Removed german translations
- Removed 401 error page and replaced usage with 403 error page as 401 was actual a duplicate of 403
- Replaced usage of $_SERVER['REMOTE_USER'] with Laravels API Request::server('REMOVE_USER')

* - Fixed request usage
2018-03-13 20:07:52 -07:00
Brady Wetherington
e15f2ac8ab Support \r-terminated files better from the Importer (#5184)
* Hoist the ini_get/ini_set auto-detect line endings code higher

* Placate the irascible Codacy
2018-03-13 20:06:53 -07:00
Anh DAO-DUY
06e760081c Add default_label field missing in status_labels table (Travis CI) (#5180) 2018-03-09 13:05:14 -08:00
snipe
fc8637c81a Check for associated maintenance asset
This should probably never happen, but triggers on the demo sometimes because of fluctuating data seeding
2018-03-07 22:22:36 -08:00
snipe
01eaf48471 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-03-07 18:25:12 -08:00
snipe
238a075c6a Bumped hash 2018-03-07 18:24:09 -08:00
snipe
1d130b4a89 Fixed asset model permission not granted for edit 2018-03-07 18:22:49 -08:00
snipe
95d935d917 Added warning to not edit config files manually 2018-03-07 17:39:13 -08:00
snipe
c4db8d37c2 Fixed #5168 - users without superadmin could not see custom fields UI even if granted 2018-03-07 13:37:37 -08:00
snipe
17e0154995 Fixed #5160 - make field_values readable via custom fields API 2018-03-06 13:11:45 -08:00
snipe
d60c9800c2 Check that the id key exists to prevent any weird edge cases for location 2018-03-05 22:44:05 -08:00
snipe
04d2542b81 Fixed #5078 - check for object or array as location in LDAP sync 2018-03-05 22:42:40 -08:00
snipe
9a25cb3ee7 Set default labels in seeders 2018-03-05 22:16:36 -08:00
snipe
1e22b8e567 Fixed #5138 - added default_label flag to status labels 2018-03-05 22:04:16 -08:00
snipe
d05dfb18a7 Fixed #5150 - added lastname first initial as username format 2018-03-05 21:39:05 -08:00
snipe
a5c6ddb8ac Change gate for updating assets via the API to edit 2018-03-05 21:27:17 -08:00
snipe
90bff709a4 Merge branch 'develop' 2018-03-05 20:58:08 -08:00
snipe
052132ec37 Remove pyrchase cost from accessory search 2018-03-05 20:57:50 -08:00
snipe
d42361bac1 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-03-05 20:27:50 -08:00
snipe
2df19bcbb4 Only pull category matches for accessory category 2018-03-05 20:22:44 -08:00
snipe
0ef4251462 Include new icons in stalebot 2018-03-05 19:06:39 -08:00
snipe
a4af81563a Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2018-03-05 16:26:49 -08:00
snipe
36cd63836e Fixed #5151 - added asset tag to maintenances 2018-03-05 16:26:40 -08:00
tiagom62
48f9959fcd snipeit.sh improvements (#4708)
* snipeit.sh improvements

* Missing dependency for upgrade.php #4726

* Upgrade noninteractively
2018-03-04 21:48:44 -08:00
snipe
9effa3d2ab Fixed manufacturer restore 2018-03-03 19:14:27 -08:00
snipe
0782222c6b Removed commenting
This restore() is still not working, though not sure why. Seems like it should be pretty straghtforward, and yet…

Additonally, manually setting the deleted)at date to null or blank isn’t working either. I’m sure I’m just missing something obvious.
2018-03-03 18:49:02 -08:00
snipe
f7784b6543 Fixed #2402 - add ability to restore manufacturers 2018-03-03 18:46:19 -08:00
snipe
fabc9e5d1c Bumped hash 2018-03-03 17:15:59 -08:00
snipe
06805d70fe Fixed incorrect settings HTML 2018-03-03 17:07:28 -08:00
snipe
123e317e52 Check for an array (for PHP7.2 support) 2018-03-03 17:07:07 -08:00
fordster78
68a9855506 New First Admin Notification (#5147)
* New First Admin Notification

* Include Last name in Welcome and First admin Notifications
2018-03-03 14:37:42 -08:00
fordster78
688a3251a9 New Welcome Notification (#5146)
* New Test Notification

Created Test Notification.
Updated Vendor Mail message.blade files.
Updated api settings controller to use Notification Façade.

* Add show URL in Emails condition

* New Welcome Notification
2018-03-03 12:44:41 -08:00
snipe
30c5cc1dc4 Additional dark themes 2018-03-02 20:01:41 -08:00
snipe
4ab1d5ca7f Fixed #5110 - crash on accessory checkin missing last_name 2018-03-02 19:26:41 -08:00
snipe
5a808a0f91 Fix for header color in green skin 2018-03-02 19:17:12 -08:00
snipe
a6cc31944a Added package-lock 2018-03-02 19:17:01 -08:00
snipe
41a9e5f710 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2018-03-02 18:01:41 -08:00
snipe
31790e0bb7 Tweaks to theme settings 2018-03-02 18:01:36 -08:00
fordster78
4e0c8e218d New Test Notification (#5137)
Created Test Notification.
Updated Vendor Mail message.blade files.
Updated api settings controller to use Notification Façade.
2018-03-02 18:01:20 -08:00
Geoff Young
4fe4c0c72a Add viewKeys permission check on Asset page (#5141) 2018-03-02 18:00:22 -08:00
snipe
f171357e36 Removed unused skin files, added skin setting option 2018-03-02 17:50:40 -08:00
snipe
ef91cc992e Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2018-03-02 16:52:00 -08:00
snipe
04b92f2ad2 Added info on third-party libraries 2018-03-02 16:51:56 -08:00
Tim F
4f049112de Patch for dark-green.css to include more in template. (#5122)
* Update dark-green.css

Added several new rules, to include little bits that weren't previously. Mostly centered around the new 'form' page and the various settings pages.

* Update dark-green.css

Fixed buttons for "View All" on main page. Missed those previously. Well, the buttons were included, but not the hover effects.
2018-03-01 20:45:22 -08:00
Tim F
86242b322c Create dark-green.css (#5118) 2018-02-27 14:10:08 -08:00
Tim F
14af95001e Create dark-green.css (#5117) 2018-02-27 13:37:12 -08:00
snipe
2dd56f5bda Backup the .env if BACKUP_ENV is set to true 2018-02-26 18:12:48 -08:00
snipe
c250c632f3 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-26 15:46:49 -08:00
snipe
e3fb4f8799 Bumped hash 2018-02-26 15:44:36 -08:00
snipe
b4f704d7f1 Fixed 2FA reset button 2018-02-26 15:43:49 -08:00
Daniel Meltzer
9ee2c6be57 Api tests2 (#5098)
* Cleanup

* API tests for asset models and related cleanup/improvements

* Api license test.  Tests incomplete because create/update/destroy are not implemented yet in the controller

* API Category tests.

* Manufacturers API Test.

* Implement License Create/Update/Delete Methods for API and enable test.

* Add missing gate for api.  Fixes only superadmins being able to generate Personal Access Toekns
2018-02-25 12:10:02 -08:00
Daniel Meltzer
7de8f71f58 Api tests (#5096)
* Use the formated date helper to clean up verifications.

* Add Checkin/Checkout api tests.

* Accessories api test

* Add Companies API Test.

* Return ModelNotFound as a 404.

* Cleanups/simplficiations/updates.

* Locations api test.

* currency and image should be fillable on location.

* Update components api test.

* Use findOrFail so we return a 404 instead of a 200.  Matches other item types.

* order_number should be fillable in component.

* Add updated_at and permissions to information returned from api for a user.

* Add users test and flesh out factory and fillable fields.

* Add test for assets method

* API status label test.

* Disable php7.2 for now on travis until the count(null) issues are remedied

* Add serial to update.

* API model not found should return a 200
2018-02-24 19:01:34 -08:00
snipe
b6a75093b7 Removed duplicate location_id assignment 2018-02-24 14:06:10 -08:00
Daniel Meltzer
d54dda40d3 Functional Tests Improvements (#5095)
* Rely on laravel transactions instead of refreshing the database dump between functional test runs.  Cuts functional test runtime by 75%.  Also use mysql to seed directly.

* Split functional tests into two groups on travis to reduce overall memory usage.  Any new tests will need to be added to one of these two files before they are run on travis.  running all functional tests simultaneously still works locally.

* Fix name of test in group.
2018-02-24 12:03:33 -08:00
snipe
a705c714ab Merge branch 'develop' 2018-02-23 05:53:44 -08:00
snipe
0e48837eec Fixed assets checked out to assets listing table 2018-02-23 05:53:00 -08:00
snipe
5e5ba54c3e Disallow checkout asset to itself 2018-02-23 05:52:19 -08:00
snipe
4403b139d5 Merge branch 'develop' 2018-02-23 05:12:49 -08:00
snipe
103974cae4 Fixed statuslabel detail view table 2018-02-23 05:09:14 -08:00
snipe
aea37467d8 Attempt to add codeclimate test coverage 2018-02-22 22:34:08 -08:00
snipe
9d2ed7bc5f Merge branch 'develop' 2018-02-22 21:49:37 -08:00
snipe
bfb11d249e Tweaked slack test UI 2018-02-22 21:47:48 -08:00
Daniel Meltzer
f7dbda4ed3 Disable broken tests (#5073)
* Work towards a functional travis.  Step 1: Disable broken unit tests.

* Fix functional tests

This updates the login information and model factories to work with
changes to source.

* Importer name/full name fixes.

Fix a bug where "name" was used ambigously and mapping "item name" to
"name" would confuse the importer into thinking it should also be a user
name.  Now we default to "full name" for the users name, and "item name"
for the item name.  These are still both configurable through the custom
mapping.

Also update sample csvs and remove an outdated sample.

* Max length of supplier notes is 191, not 255, as per default laravel string length.  Might make sense to change this to a text field in the future to match other places.

* Use sqlite/different db setup for unit tests.

* Fix assets api test.

* Fix Components API test.

* increase travis memory limit for functional tests.

* Use travis config for api tests as well.

* Fix memory limit file.

* Disable ApiComponentsAssetsCest until it's fixed.
2018-02-22 21:46:58 -08:00
snipe
7c9c6ea3df Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-22 16:40:19 -08:00
snipe
7d49c4de87 Bumped hash 2018-02-22 16:39:12 -08:00
snipe
e49a36c9fd Fixed #5084 - min supplier name now 1 2018-02-22 16:37:05 -08:00
snipe
236e773438 Fixed incorrect component not found string 2018-02-22 16:36:01 -08:00
snipe
e3144c3093 Added Slack test button 2018-02-22 16:35:34 -08:00
snipe
cbd8409611 Fixed #5067 - account for only one name in generateFormattedNameFromFullName 2018-02-22 14:10:58 -08:00
snipe
a85b38850c Added roave security-advisories to composer
https://packagist.org/packages/roave/security-advisories
2018-02-22 13:22:13 -08:00
snipe
e01f1ae8ed Partial fix for #2892 - Added export for license seat assignments 2018-02-21 17:28:55 -08:00
snipe
d60a4c5f87 Merge branch 'develop' 2018-02-21 16:47:03 -08:00
snipe
9c1fe2c922 Reverting previous change - qty already existed in allowed columns. Derp. 2018-02-21 16:46:37 -08:00
snipe
3131c0d0ac Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 16:45:21 -08:00
snipe
364e9cf3de Bumped version 2018-02-21 16:43:00 -08:00
Daniel Meltzer
5a84863873 Importer name/full name fixes. (#5072)
Fix a bug where "name" was used ambigously and mapping "item name" to
"name" would confuse the importer into thinking it should also be a user
name.  Now we default to "full name" for the users name, and "item name"
for the item name.  These are still both configurable through the custom
mapping.

Also update sample csvs and remove an outdated sample.
2018-02-21 16:42:36 -08:00
snipe
006b2b18b0 Added qty to sorting 2018-02-21 16:21:31 -08:00
snipe
fb262e38a7 Added - make total sortable in components 2018-02-21 16:21:21 -08:00
snipe
f8151284ee Fixed - removed extra components table 2018-02-21 16:20:46 -08:00
snipe
5d8c91b687 Fixed regression - Added sum footer back into accessories 2018-02-21 15:54:16 -08:00
snipe
0f23462607 Merge branch 'develop' 2018-02-21 15:51:33 -08:00
snipe
ca50ea190f Applied master changes to develop
Wrong branch :(
2018-02-21 15:51:04 -08:00
snipe
31192abd2c Fixed ambniguous query in asset maintenance search 2018-02-21 10:28:05 -08:00
snipe
42c7f41d24 Added some alerting in custom fields if it looks like the db_column and real column do not match 2018-02-21 10:06:23 -08:00
snipe
fade03e337 Added nicer gates for auditing 2018-02-21 08:13:18 -08:00
snipe
d511d90a2f Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 06:50:42 -08:00
snipe
54d6cafda9 Bumped hash 2018-02-21 06:49:36 -08:00
snipe
a730cd1c51 Fixed - better handle admin routes when user is not logged in
Redirect to login instead of just showing forbidden
2018-02-21 05:51:49 -08:00
snipe
cccd75fc42 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 05:48:02 -08:00
snipe
dbc16a6c9b Bumped hash 2018-02-21 05:47:12 -08:00
snipe
6ffad6043f Fixed issue where bootstrap tables didn’t use a default page size 2018-02-21 05:44:41 -08:00
snipe
4e398950a0 Merge branch 'develop' 2018-02-21 05:34:02 -08:00
snipe
f5e51897e3 Added - sort by color on status labels 2018-02-21 05:29:50 -08:00
snipe
698ea36cc2 Added - Order location by manager 2018-02-21 05:09:40 -08:00
snipe
0cf9cdd3b1 Make department columns sortable 2018-02-21 05:04:20 -08:00
snipe
42c8de5620 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 04:44:48 -08:00
snipe
ba4f63bca3 Bumped version 2018-02-21 04:43:52 -08:00
snipe
4ed2ef6298 Merge branch 'develop' 2018-02-21 04:42:03 -08:00
snipe
d28c2af7de Fixed bootstrap table issue if per page was set to 10 2018-02-21 04:41:44 -08:00
snipe
1e8c32fbdb Fixed - missing table prefix for location parent search 2018-02-21 04:33:47 -08:00
snipe
97bd87773c Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 02:40:23 -08:00
snipe
b560828173 Bumped hash 2018-02-21 02:18:14 -08:00
snipe
0d7a43ab38 Fixed orderby parent location name 2018-02-21 02:17:18 -08:00
snipe
7865fbea7e Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-21 01:27:11 -08:00
snipe
5fcbf9091e Bumped version 2018-02-21 01:23:19 -08:00
snipe
fedd2b638c Merge branch 'develop' 2018-02-21 01:20:48 -08:00
snipe
22f44e342f Fixed incorrect next audit date if asset override 2018-02-21 01:18:21 -08:00
snipe
f7eb013935 Reverse order on activity report 2018-02-20 15:42:44 -08:00
snipe
1c28f893e7 Fixed #5027 - remove link to non-existant reports page 2018-02-20 14:44:29 -08:00
snipe
195dfd752d Updated translations 2018-02-20 14:41:12 -08:00
snipe
cc788d3b62 Added Slovenian 2018-02-20 14:37:22 -08:00
snipe
e54021e7be Added Venzuelan spanish 2018-02-20 14:37:12 -08:00
snipe
918db39eba Merge branch 'develop' 2018-02-19 18:06:58 -08:00
snipe
213fe2ecea Fixed #5044 - incorrect env values for SSL DB paths 2018-02-19 18:03:50 -08:00
snipe
6e70352d0b Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-17 01:05:20 -08:00
snipe
d2403cdadb Added image preview for uploads 2018-02-17 00:51:22 -08:00
snipe
9a4f5e0ba8 Bumped hash 2018-02-16 22:10:06 -08:00
snipe
502e4d672a Use image upload partial in asset edit/create 2018-02-16 22:08:49 -08:00
snipe
99205bf72e Merge branch 'develop' 2018-02-16 21:39:15 -08:00
snipe
b3d673b0aa Fixed #5037 - use line breaks in notes in table view 2018-02-16 21:38:56 -08:00
snipe
baf51eb430 Merge branch 'develop' 2018-02-16 21:19:03 -08:00
snipe
d8fc50c351 Fixed #5033 - use PHP’s max filesize for image uploads 2018-02-16 21:17:41 -08:00
snipe
ba5057181e Check for model in asset view 2018-02-16 18:04:11 -08:00
snipe
4c4dee0bf1 Use dropifexists for rollback 2018-02-16 17:45:40 -08:00
snipe
589c3a2be3 Fixed #4668 - wrong gate on manufacturer save 2018-02-16 17:45:27 -08:00
snipe
991f182f89 Added drop table to migration 2018-02-16 15:36:33 -08:00
snipe
f44b03f5a9 Updated sticky table header extenion 2018-02-16 15:36:21 -08:00
snipe
5fd50040d3 Fixed weird migration 2018-02-16 15:31:47 -08:00
snipe
5302108556 Update to master 2018-02-16 13:27:03 -08:00
snipe
592c62de75 Merge branch 'develop'
# Conflicts:
#	README.md
#	config/version.php
2018-02-16 13:26:53 -08:00
snipe
27c1bc0271 Bumped hash 2018-02-16 13:24:47 -08:00
snipe
3744a68daf Features/better table options (#5018)
* Added CSS for table toolbar

* Use maintenances API for table listings

* NIcer layout for allowed_columns in maintenances API

* Fixed #5014 - bootstrap cookie issues

* Fixed #5015 - bug when saving settings

* Refactored datatable code to use data attributes

* Updated dashboard with new table code

* Added - Order by group user count

* Updated groups to use new table attributes

* New license listing table code

* More bootstrap table implementations

* More BS table refactoring

* Improved bootstrap assigned assets

* New bootstrap for reports

* Misc BS fixes

* FIxed small issue with asset history display

* Removed multisort option

* JS refactor
2018-02-16 13:22:55 -08:00
dl1l
c209b7bb5d Update edit.blade.php (#5019) 2018-02-15 14:59:54 -08:00
snipe
f04493c1ab Added firstname_lastname as possible username format 2018-02-13 20:31:51 -08:00
snipe
62edf14893 Refactored method to generate usernames from full names 2018-02-13 20:31:11 -08:00
snipe
0d11e32523 Fixed issue where group with no permissions would crash view page 2018-02-13 17:14:30 -08:00
snipe
5484b06df8 Fixed #4923 - invalid check for location object in Ldap Sync 2018-02-13 17:06:42 -08:00
snipe
9f3116e4e3 Added - Deep linking in Bootstrap tabs 2018-02-13 17:04:50 -08:00
snipe
f144d671ff Fixed #4988 - incorrect variable name in asset checkout API method 2018-02-12 19:13:33 -08:00
snipe
b294635e17 Updated packages 2018-02-08 09:29:12 -08:00
snipe
36234cc0e8 Add @seanmcilvenna as a contributor 2018-02-08 09:29:12 -08:00
snipe
5c87003196 Added code triage badge 2018-02-08 09:29:12 -08:00
Sean McIlvenna
cc5d36f8be Addng "Category" column to "view assets" and re-organizing columns (#4970) 2018-02-05 17:36:35 -06:00
README Bot
1359a4f88a Add CodeTriage badge to snipe/snipe-it (#4968)
Adds a badge showing the number of people helping this repo on CodeTriage.

[![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)

## What is CodeTriage?

CodeTriage is an Open Source app that is designed to make contributing to Open Source projects easier. It works by sending subscribers a few open issues in their inbox. If subscribers get busy, there is an algorithm that backs off issue load so they do not get overwhelmed

[Read more about the CodeTriage project](https://www.codetriage.com/what).

## Why am I getting this PR?

Your project was picked by the human, @schneems. They selected it from the projects submitted to https://www.codetriage.com and hand edited the PR. How did your project get added to [CodeTriage](https://www.codetriage.com/what)? Roughly 4 months ago, [@imjennyli](https://github.com/imjennyli) added this project to CodeTriage in order to start contributing. Since then, 8 people have subscribed to help this repo.

## What does adding a badge accomplish?

Adding a badge invites people to help contribute to your project. It also lets developers know that others are invested in the longterm success and maintainability of the project.

You can see an example of a CodeTriage badge on these popular OSS READMEs:

- [![](https://www.codetriage.com/rails/rails/badges/users.svg)](https://www.codetriage.com/rails/rails) https://github.com/rails/rails
- [![](https://www.codetriage.com/crystal-lang/crystal/badges/users.svg)](https://www.codetriage.com/crystal-lang/crystal) https://github.com/crystal-lang/crystal

## Have a question or comment?

While I am a bot, this PR was manually reviewed and monitored by a human - @schneems. My job is writing commit messages and handling PR logistics.

If you have any questions, you can reply back to this PR and they will be answered by @schneems. If you do not want a badge right now, no worries, close the PR, you will not hear from me again.

Thanks for making your project Open Source! Any feedback is greatly appreciated.
2018-02-05 15:22:52 -06:00
snipe
1e1362bdbc Merge branch 'develop' 2018-02-02 09:42:50 -06:00
snipe
813106efd3 Default to only changing default location in bulk edit 2018-02-02 09:42:34 -06:00
snipe
08d129c2ea Fixed #4951 - updating asset location in bulk edit 2018-02-02 09:36:40 -06:00
snipe
8491e57258 Merge branch 'develop' 2018-02-02 09:32:00 -06:00
snipe
d86fb098a8 Fixed sort ordering on dashboard 2018-02-02 09:31:43 -06:00
snipe
e21f8e46d4 Merge branch 'develop' 2018-02-01 17:18:16 -06:00
snipe
b3a6ec2804 Fixed #4938 - default sort order 2018-02-01 17:17:56 -06:00
snipe
0034938c04 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-02-01 16:32:13 -06:00
snipe
d2587337e4 Fixed #4952 - ignore show_archived setting when drilling down into specific ID 2018-02-01 16:31:32 -06:00
snipe
8b2045523d Bumped hash 2018-02-01 15:55:33 -06:00
snipe
e7e10c24be Add departments to usr export 2018-02-01 15:54:49 -06:00
snipe
f06852f48d Merge branch 'develop' 2018-01-25 13:17:11 -06:00
snipe
2f9fff7130 Do not try to show depreciation report if no depreciations are set up 2018-01-25 13:16:49 -06:00
snipe
0b788f64f7 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-25 11:07:18 -08:00
snipe
facf1d42f7 Sigh. 2018-01-25 11:06:37 -08:00
snipe
e77aae703b Bumped hash 2018-01-25 11:03:45 -08:00
snipe
76f52dd89b Fixed #4872 - Check for assigned in depreciation table 2018-01-25 11:02:46 -08:00
snipe
0e980f7229 Hotfix: Disabling the event dispatcher broke model validation. (#4912) (#4915)
* Hotfix: Disabling the event dispatcher broke model validation.

This resulted in invalid data being imported.  Reenable the event dispatcher for now--this causes double logs, but at least validates properly.

* Actually disable the event dispatcher.

* Sign in where necessary to fix the importer unit test.  This catches the issues found manually in 4912
2018-01-25 10:29:06 -08:00
Daniel Meltzer
2f5d550df6 Hotfix: Disabling the event dispatcher broke model validation. (#4912)
* Hotfix: Disabling the event dispatcher broke model validation.

This resulted in invalid data being imported.  Reenable the event dispatcher for now--this causes double logs, but at least validates properly.

* Actually disable the event dispatcher.

* Sign in where necessary to fix the importer unit test.  This catches the issues found manually in 4912
2018-01-25 10:10:56 -08:00
snipe
3acf5f76fe Merge branch 'develop' 2018-01-25 03:35:30 -08:00
snipe
4be8ae7f3d Fuckery. 2018-01-25 03:34:52 -08:00
snipe
029dd43ecd Merge branch 'develop' 2018-01-25 03:01:35 -08:00
snipe
cf8feb37e1 Fixed crash if no model associated
This should never be triggered. Bad data is getting in somehow.
2018-01-25 03:00:54 -08:00
snipe
648531f4cc Merge branch 'develop' 2018-01-25 00:26:17 -08:00
snipe
11505d5d06 Fix crash if no asset model
(This should never happen, but shouldn’t crash if/when it does)
2018-01-25 00:25:43 -08:00
snipe
1d04897b32 Order by default location 2018-01-24 14:27:12 -08:00
snipe
eddcc0fbbd Fixed cookie issue on hardware 2018-01-24 14:23:19 -08:00
snipe
bd80a77b78 Added default location to asset listing 2018-01-24 14:23:12 -08:00
snipe
edc30a31c1 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-24 11:02:46 -08:00
snipe
f5636f325d Bumped hash 2018-01-24 11:01:51 -08:00
snipe
426bf3803b Merge branch 'develop' 2018-01-24 11:00:40 -08:00
snipe
739d3c72b7 Fixed #4902 - include last name in checkin email 2018-01-24 10:59:34 -08:00
snipe
60c38a0c47 Added setting to choose what appears in model select list - Fixes #4879 2018-01-24 10:43:46 -08:00
snipe
9566fbd3cd Fixed borked down() migrations 2018-01-24 10:41:49 -08:00
Anh DAO-DUY
e41ee1bcf8 Fix upload images problem with Docker (#4906)
Some folders were missing in this script file (Docker installation).
Missing folders caused errors while uploading some images.
2018-01-24 09:13:03 -08:00
Tim Bishop
bc14f225af Only use location currency if it's actually set. (#4904)
This changes the depreciation report to only use the currency on a
location if it's actually set to something. Previously, if an asset had
a location it would use the currency even if it was blank rather than
falling back to the default.
2018-01-24 08:55:51 -08:00
snipe
07148c1503 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-24 08:29:25 -08:00
snipe
fcb5eea951 Bumped hash 2018-01-24 08:28:53 -08:00
snipe
7a4bf26733 Merge branch 'develop' 2018-01-24 08:25:36 -08:00
snipe
2c2910d1dd Handle shitty edge cases where an asset has no model without crashing 2018-01-24 08:16:05 -08:00
snipe
1dae7a2fe4 Fixed #3636 - removed thread_id (we don’t use this anymore) 2018-01-24 08:00:28 -08:00
snipe
e19ab329bd Add @tdb as a contributor 2018-01-24 07:13:24 -08:00
snipe
922cb90cb3 Add @CronKz as a contributor 2018-01-24 07:12:59 -08:00
snipe
765295136a Added - ability to add custom footer text, hide user’s manual/support links 2018-01-24 07:02:30 -08:00
snipe
7579333ad3 Disallow LDAP settings if in demo mode 2018-01-24 05:50:31 -08:00
snipe
543ea28b72 Added qty to asset components - fixed #4876 2018-01-24 05:25:01 -08:00
snipe
fefd6d60f6 Fixed #4899 - sorting in user history 2018-01-24 04:48:00 -08:00
snipe
a7b8b4bf55 Added: ability to change actual location from bulk edit assets 2018-01-24 04:38:25 -08:00
snipe
7cafa194c1 Fix for counts 2018-01-24 04:26:15 -08:00
snipe
b37f78dbbf Fixed #4858 - wrong params for id count 2018-01-24 04:24:45 -08:00
snipe
e20cd42cc2 More parse error fixes 2018-01-24 03:42:49 -08:00
snipe
7b99f81f72 Fixed parse error on hardware blade 2018-01-24 03:38:29 -08:00
snipe
f1bbdc9e59 Revert PR #4845 2018-01-24 03:32:45 -08:00
Richard Hofman
5219fb63a1 Add --base_dn option to LdapSync command. (#4888) 2018-01-23 18:15:36 -08:00
CronKz
dcc379c3fa Adding missing translations. (#4896) 2018-01-23 18:13:55 -08:00
vcordes79
0fb8dc3418 check if user is allowed to view assets (#4845) 2018-01-23 18:10:05 -08:00
vcordes79
f4e9d245d0 Status labels (#4895)
* fix statuslabels

* fix statuslabels
2018-01-23 18:08:54 -08:00
snipe
c75a2051ff Hide mfg button if no permissions to create 2018-01-22 14:02:23 -08:00
CronKz
691ec164b1 Added Translations (#4880) 2018-01-22 13:14:52 -08:00
vcordes79
7b596c750d API for (dis)associating fields with fieldsets (#4881)
* start work on fields in fieldset api

* revert CustomFieldsetsController

* fieldset associate / disassociate api

* fix variable names and payload

* fix variable name
2018-01-22 13:14:04 -08:00
Tim Bishop
b346556caa Allow manager_id to be fillable. (#4882)
The API UsersController accepts manager_id, but calls the following:

$user->fill($request->all());

This results in manager_id being ignored. I can't see any problem with
allowing a user's manager to be modified using the API, so this change
allows it.
2018-01-22 13:12:02 -08:00
snipe
93ec7068df Removed a few additional unused badges 2018-01-20 08:54:49 -08:00
snipe
5f65f993a0 Small refactor for AssetPresenter method 2018-01-20 08:52:50 -08:00
snipe
a9441521a4 Small refactor for Setting method 2018-01-20 08:52:50 -08:00
snipe
2dede67ae9 Removed unusued badges 2018-01-20 08:52:49 -08:00
Daniel Meltzer
6bf17e7d47 Reset the item between rows of import to avoid stale data. (#4868) 2018-01-20 08:40:01 -08:00
snipe
a3240da707 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-20 07:47:54 -08:00
snipe
a28fee1ff4 Bumped hash 2018-01-20 07:47:07 -08:00
snipe
57e5af2f69 Removed console logging from JS 2018-01-20 07:42:48 -08:00
snipe
8d3a214475 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-20 04:56:36 -08:00
snipe
58eedce6fe Updated language files 2018-01-20 04:53:04 -08:00
snipe
f91704a372 Added Ukrianian to language dropdown 2018-01-20 04:49:40 -08:00
snipe
f53c68e040 Bumped hash 2018-01-20 04:40:26 -08:00
snipe
99e55f84f0 Fixed misc UI permissions elements 2018-01-20 04:39:31 -08:00
snipe
798ea75f3d Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-20 04:00:26 -08:00
snipe
913e6a5709 Bumped hash 2018-01-20 03:59:42 -08:00
snipe
34f6d5ab45 Fixed #4671 - corrected component checkin gate 2018-01-20 03:58:59 -08:00
snipe
86c0194e9a Fixed #4628 and #4590 - Illegal mix of collations for operation error when searching on some languages 2018-01-20 03:08:27 -08:00
Brady Wetherington
dfb2b9b569 Major overhaul for modals, and a fix for #4820 (#4866)
Changes how the various modals work, and allows for specifying
category_type when inline-creating categories.
2018-01-20 00:42:29 -08:00
snipe
7fae380ab6 Fixed #4300 - Fix for illegal character type in action log 2018-01-20 00:27:45 -08:00
snipe
4ca8272efc Merge branch 'develop' 2018-01-20 00:21:03 -08:00
snipe
5d4bbc393e Fixed #4837 - link phone numbers 2018-01-20 00:20:45 -08:00
snipe
984275ff05 Merge branch 'develop' 2018-01-19 23:47:54 -08:00
snipe
e6f70f2ab7 Fixed #4821 - add username to user selection dropdown 2018-01-19 23:47:37 -08:00
snipe
f7de252f67 Wrong branch. :( 2018-01-19 23:46:42 -08:00
snipe
532b299c40 Fixed #4821 - add username to user selection dropdown 2018-01-19 23:46:12 -08:00
snipe
5dcc63dc74 Merge branch 'develop' 2018-01-19 20:49:20 -08:00
snipe
6eb8acf319 Swich to panel box for dashboard message 2018-01-19 20:49:02 -08:00
snipe
862251ab36 Change label to stale 2018-01-19 20:45:25 -08:00
snipe
95a9742571 Fixed #4768 - adds dashboard message option to settings 2018-01-19 20:43:55 -08:00
snipe
81d0921782 Merge branch 'develop' 2018-01-19 19:18:43 -08:00
snipe
0fa556da1d Exempt ready for dev and bounty tags from stale collection 2018-01-19 19:16:35 -08:00
snipe
05bc238c9a Merge branch 'develop' 2018-01-19 18:17:38 -08:00
snipe
782813ab49 Check for valid category before checking acceptance 2018-01-19 18:17:23 -08:00
snipe
cbca126d9d Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-19 17:53:15 -08:00
snipe
a9ffe8d210 Bumped hash 2018-01-19 17:52:40 -08:00
snipe
c242abb42e Added Company policy to fix company deletion issue 2018-01-19 17:51:28 -08:00
snipe
2c722ffc4f Fixed issue template with correct path to storage logs 2018-01-19 16:31:01 -08:00
snipe
484c47cef8 Added stale.yml to auto-close stale issues 2018-01-19 16:23:28 -08:00
snipe
f38dc37152 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-17 19:31:09 -08:00
snipe
6133f09056 Bumped hash 2018-01-17 19:30:31 -08:00
snipe
2a959edeba Added - Setting to allow archived assets in List All 2018-01-17 19:18:48 -08:00
snipe
8995d689b8 Fixed #4817 - [BREAKING API CHANGE] - changed user API response from firstname, lastname to first_name, last_name 2018-01-17 13:25:11 -08:00
snipe
f08bec6c78 Merge branch 'develop' 2018-01-17 13:12:58 -08:00
snipe
d920d91786 Fixed #4593 - do not require model_id to update asset custom fields via API 2018-01-17 13:12:32 -08:00
snipe
e35b3745eb Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-17 12:41:51 -08:00
snipe
e8252d9468 Bumped hash 2018-01-17 12:41:11 -08:00
snipe
8fff6a1a06 Update asset location based on user checkout in bulk checkout 2018-01-17 12:39:49 -08:00
snipe
0eff821928 Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-17 12:12:33 -08:00
snipe
9e605e0f79 Bumped hash 2018-01-17 12:11:39 -08:00
snipe
8fb991110e Fixed #4652 - asset not correctly checking out to user on creation 2018-01-17 12:10:28 -08:00
snipe
b383ebee48 Add @fordster78 as a contributor 2018-01-17 10:59:00 -08:00
fordster78
138313dcb9 Setup changes (#4813)
* Add Asset Tag settings to Setup

* Add Locale Settings to Setup

* Add multiple company support to setup

* Changed the locale label name from 'site_name' to locale
Added default value for language to 'en' and multiple companies support
to 0 (false)
Switched out the old Input facade to the preferred $request->input
method for the setup page.
2018-01-17 10:58:03 -08:00
snipe
f254958db9 Check if db_column foield already exists 2018-01-17 06:48:14 -08:00
snipe
09eff88679 Merge branch 'develop' 2018-01-17 06:28:11 -08:00
snipe
24b356dba4 Fixed typos in default blade that cause components to not show up for non-superadmins 2018-01-17 06:27:23 -08:00
snipe
91bca5fcba Fixed #4844 - use input value for ids in user bulk edit 2018-01-17 06:15:52 -08:00
vcordes79
96a469db36 allow changing user activated field via api (#4843) 2018-01-17 05:38:10 -08:00
snipe
9ab05e7037 Add @vcordes79 as a contributor 2018-01-17 05:36:15 -08:00
vcordes79
52a99faf68 fix phone number update in api (#4842) 2018-01-17 05:33:55 -08:00
vcordes79
94cf1f8741 Fixes #4829 - add fields api (#4840)
* add fields api

* change route names
2018-01-17 05:31:57 -08:00
snipe
e2a8f9b790 Merge branch 'develop' 2018-01-17 03:27:03 -08:00
snipe
409f5cc4fd Added - display asset model category on hardware view page 2018-01-16 16:26:24 -08:00
snipe
3b5e4c44eb Fixed depreciation report not showing assigned to 2018-01-16 07:46:34 -08:00
snipe
69a7ea63e2 Pass nopages to dashboard stuff 2018-01-16 07:37:20 -08:00
snipe
aac379daeb If nopages is passed, hide page numbers
This seems weird since we’re checking for a negative, but there are only a few spots where we wouldn’t want page numbers, namely the dashboard sruff
2018-01-16 07:37:06 -08:00
snipe
1d3472b5c4 Truncate the checkout_requests field on seed 2018-01-16 07:36:14 -08:00
snipe
66a590b774 Account for user’s that have been deleted in the requested list 2018-01-16 07:36:03 -08:00
snipe
dc4472e9e9 Misc export table fixes 2018-01-15 21:03:46 -08:00
snipe
6bfd428c2e Use real status label names, even if deployed 2018-01-15 21:03:26 -08:00
snipe
f4623bd277 Add user group membership to user view page 2018-01-15 20:31:10 -08:00
snipe
1f04d7aafd Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-11 15:27:17 -08:00
snipe
78efeec368 Bumped hash 2018-01-11 15:24:37 -08:00
snipe
5b15a2fc0a Merge branch 'develop' 2018-01-11 15:23:27 -08:00
snipe
68f1fa0340 Added - ability to sort by asset count in models 2018-01-11 15:21:51 -08:00
snipe
9293f17707 Moved search scope lower to fix weird (possible Laravel) bug w/prepared statements 2018-01-11 15:17:34 -08:00
snipe
a4b32e2328 Improved - use presenters on model detail 2018-01-11 15:17:10 -08:00
snipe
d109ca30e2 Improved - allow searching on most detail views with additional tables 2018-01-11 15:16:53 -08:00
snipe
3b4a651dd9 Allow counts in mfg sorting 2018-01-11 15:16:23 -08:00
snipe
a4ac53e2e9 Improved - Using presenters for column headers in location detail 2018-01-11 15:16:09 -08:00
snipe
cf1b5a7685 Added- sort by count in manufacturer listing 2018-01-11 15:13:32 -08:00
snipe
0789eb8b07 Improved - using presenters for column headers in manufacturer detail 2018-01-11 15:12:48 -08:00
snipe
7f674fdd35 Require implicit search 2018-01-11 13:18:06 -08:00
snipe
500aa37e3c Fixed switchable attributes on table headers 2018-01-11 13:17:55 -08:00
snipe
efe668d26d Merge branch 'develop'
# Conflicts:
#	config/version.php
2018-01-10 22:59:02 -08:00
snipe
90db97b980 Merge branch 'features/fixes_4792_ajax_tables_on_license_view' into develop 2018-01-10 22:57:44 -08:00
snipe
bc3d27fac6 Bumped hash 2018-01-10 22:57:22 -08:00
snipe
55b9f1207d Updated bootstrap tables 2018-01-10 22:53:54 -08:00
snipe
2bbb6001a8 Added location to seats transformer 2018-01-10 20:36:27 -08:00
snipe
af7b7664c5 Added license seat location method 2018-01-10 20:34:44 -08:00
snipe
c31362655c Refactored BS tables include for clearer separation of simple v not simple 2018-01-10 20:34:36 -08:00
snipe
c6a956382f Fixed #4784 - cookie not always being set correctly for ajax tables 2018-01-10 20:04:41 -08:00
snipe
bab0bda174 Added custom formatter for license seats (WIP) 2018-01-10 18:58:55 -08:00
snipe
bb52a8417c Switch view code to AJAX table 2018-01-10 18:58:41 -08:00
snipe
e3d7be23cb Added new seats controller method 2018-01-10 18:47:57 -08:00
snipe
3d5545494e Added new seats transformer 2018-01-10 18:47:40 -08:00
snipe
7f1a535b30 Added new seats API route 2018-01-10 18:47:27 -08:00
snipe
254234b0dc Fixed #4787 - don’t try to display category if it is invalid
This shouldn’t be needed, but in case data got weird (manual editing, etc)
2018-01-10 06:33:59 -08:00
snipe
3566f37981 Merge branch 'develop' 2018-01-10 06:25:06 -08:00
snipe
e6259eb6e1 Fixed #4774 - show assets assigned to assets in asset view
Todo: Fix text search on the asset to asset tab. It’s currently broken so I’ve disabled it.
2018-01-10 05:44:11 -08:00
snipe
eeb3d1eb42 Use language strings for tab text 2018-01-10 05:42:34 -08:00
snipe
79295f6434 Use dataTableLayout for table 2018-01-10 05:33:45 -08:00
snipe
285e4e2e52 Allow bulk language update in user edit 2018-01-10 05:33:26 -08:00
snipe
5587b64d64 Fixed #4770 - broken licenses, etc on company view, added users and components 2018-01-10 03:52:21 -08:00
snipe
f0f2a5aa67 Workaround for #4784 - make changes tab always visible
Not sure why the cookie isn’t cookie-ing
2018-01-10 01:56:24 -08:00
Brady Wetherington
061317866b Changes to Models in the asset-edit screen maintain chosen values (#4781)
Fixed for #2195
2018-01-09 23:45:20 -08:00
snipe
65ffc01583 Merge branch 'develop' 2018-01-09 20:19:50 -08:00
snipe
f5f7a63a23 Production assets 2018-01-09 20:19:04 -08:00
snipe
b6a7fd1cec Run production assets - because modern web development. Sigh. 2018-01-09 20:18:49 -08:00
snipe
6245f92e16 Merge branch 'develop' 2018-01-09 20:12:44 -08:00
snipe
0abab2107c Fixed #4779 - show selected filename on filepicker 2018-01-09 20:12:07 -08:00
snipe
afc7116260 Fixed #4778 - added notes to asset model view 2018-01-09 20:00:06 -08:00
Geoff Young
0b3d2b46e0 Add attributes to delete asset file button (#4336)
This adds attributes to the delete file buttons on the View Asset page.
The attributes will fill the confirmation modal that prompts users
before deleting. They also activate a tooltip on the button.
2018-01-03 17:25:50 -08:00
Brandon Daniel Bailey
f786e07179 Allow auto increment through the API (#4690)
* Allow auto incrementing asset_id from the API when the setting is enabled

* Cleaned up the if else statement

* Added prefix to the orWhereRaw which causes a database error if the configuration uses a prefix

* Auto incrementing through the API
2018-01-03 17:24:32 -08:00
Daniel Meltzer
b2469bb34f Fix double create log on import. (#4706)
* Fix double create log on import.

* Fix code error causing component importer to implode.

* More component importer oldities
2018-01-03 17:22:02 -08:00
Brandon Daniel Bailey
b721bfcc84 Added prefix to the orWhereRaw which causes a database error if the configuration uses a prefix (#4703) 2017-12-28 20:09:36 -08:00
Daniel Meltzer
f16ce09a7a Importer again (#4702)
* If a user id is provided in the name column of an import, we should assume that it is a user id and check out to it.

* Fix build of vue files.  The location is public/js/build, not public/build

* Ensure a status type is set before allowing submission of an import.

Also expand the status text label to change color based on success/failure.

Fixes #4658

* Use right key to lookup emails when importing users.  Fixes 4619.

* Import serial for components, and make unique matches based on the serial as well as the name.  Fixes #4569

* Set the location_id when importing an item properly.

This moves as well to using the Asset::checkout() method, which should consolidate the logic into a useful spot.
Fixes #4563 (I think)

* Production assets.

* Case insensitive field map guessing and repopulate when changingin import type.
2017-12-28 20:08:45 -08:00
snipe
bbe9df306b Merge branch 'develop' 2017-12-26 17:33:06 -08:00
snipe
1bd7392531 Account for audits on deleted assets 2017-12-26 17:32:45 -08:00
snipe
2002dfca17 Merge branch 'develop' 2017-12-26 17:13:49 -08:00
snipe
38c2ecbd0c Fixed migration use 2017-12-26 17:13:26 -08:00
snipe
ce97faa8d4 Merge branch 'develop' 2017-12-26 17:11:40 -08:00
snipe
31ca4bff8c Break audit date denorm migrations into two separate migrations 2017-12-26 17:11:08 -08:00
snipe
6f9033b2fd Merge branch 'develop' 2017-12-26 16:49:42 -08:00
snipe
8864f81402 Fixed manufacturer error on printable user page
Manufacturer is not required for accessories, so need to account for that
2017-12-26 16:49:01 -08:00
snipe
71ba1af647 Fixed button class on asset view 2017-12-26 16:48:28 -08:00
snipe
b894a4c19a Merge branch 'develop' 2017-12-20 12:36:45 -08:00
snipe
c3a44f25fd Fixed #4663 - duplicate manufacturer name in selectlist 2017-12-20 12:32:55 -08:00
snipe
1f36e7997f Bumped version 2017-12-20 03:31:43 -08:00
snipe
33b5c26da5 Bumped hashcount 2017-12-19 22:16:31 -08:00
snipe
e5a7e6619f Merge branch 'develop' 2017-12-19 22:15:51 -08:00
snipe
d2e2c1c05f Stub and 404 registration routes 2017-12-19 22:14:51 -08:00
snipe
557b8b0ded Merge branch 'develop' 2017-12-19 20:57:46 -08:00
snipe
37d4cf3afb Fixed #4647 - requestable model button not clickable 2017-12-19 20:48:26 -08:00
snipe
b716db225f Added “new” buttons for manufacturer and category in asset model creation 2017-12-19 20:30:46 -08:00
snipe
fbe093705d Fixed #4640 - add username to user detail 2017-12-19 13:42:34 -08:00
snipe
db278e9109 Merge branch 'develop' 2017-12-19 00:48:44 -08:00
snipe
88798435f6 Fixed inefficient query for inventory alerts 2017-12-19 00:32:39 -08:00
snipe
6220f0d8a5 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-15 18:57:21 -08:00
snipe
30716b349b Bumped dev hash 2017-12-15 18:55:56 -08:00
snipe
7a8c8233a2 Fixes #4639 2017-12-15 18:54:38 -08:00
snipe
7a2abcca33 Merge branch 'develop' 2017-12-14 13:00:46 -08:00
Ryan
9a40e5e651 return an error from ldap_search (#4623)
This will return the error from the ldap_search ran in Models/Ldap.php instead of throwing an exception. Users seem to commonly be getting an Exception because of invalid search filters. This will better inform them of that issue without requiring them to enable DEBUG.
2017-12-14 12:57:43 -08:00
snipe
893bcd0533 Merge branch 'develop' 2017-12-12 21:14:41 -08:00
snipe
f1a911d305 Fixed ambiguous query on non-super admins with FCS 2017-12-12 21:14:12 -08:00
snipe
8e8d9118e9 Merge branch 'develop' 2017-12-12 16:35:45 -08:00
snipe
9c108873e9 Trying 5.4.35 2017-12-12 16:32:45 -08:00
snipe
6fe5d00e9b Testing laravel 5.4.3 for PHP7.2 bug 2017-12-12 16:23:30 -08:00
snipe
a85d5cfe92 Merge branch 'develop' 2017-12-12 12:58:52 -08:00
snipe
6b257cc287 Concat search for full name 2017-12-12 12:52:10 -08:00
snipe
2952497a60 Removed bower components directory
We use npm/webpack now
2017-12-12 11:42:26 -08:00
snipe
9018af4f1d Bumped hash 2017-12-12 10:45:20 -08:00
snipe
5204e84703 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-12 09:25:45 -08:00
snipe
55eaea0110 Derp. 2017-12-12 09:25:06 -08:00
snipe
671a125c62 Bumped hash 2017-12-12 09:24:40 -08:00
snipe
eb827cdbe6 Merge branch 'develop' 2017-12-12 09:21:17 -08:00
snipe
5504dd435f Updated transslations 2017-12-12 09:20:57 -08:00
snipe
608bb1b5b1 Fixed #3416 - bulk delete asset models 2017-12-12 09:10:05 -08:00
snipe
88b64034e8 Fixed #3416 - bulk delete asset models 2017-12-12 09:03:37 -08:00
snipe
694c6f3ec6 Merge branch 'develop' 2017-12-12 07:22:30 -08:00
snipe
1d82f80e73 Improved - used “checked out to” string in asset model listing to reflect new polymorphic options 2017-12-12 07:20:06 -08:00
snipe
e21fa37254 Added - bulk actions to model asset listings 2017-12-12 07:15:51 -08:00
snipe
1ef44721f5 Improved - disallow delete if not elgible in UI 2017-12-12 07:03:31 -08:00
snipe
528630a8d3 Improvement - make asset, etc totals in company listing sortable 2017-12-12 07:03:09 -08:00
snipe
856a760d89 Improved - disallow deleting manufactureres if there are associated items
This is enforced on the backend - UI imorovement only
2017-12-12 06:52:50 -08:00
snipe
9179b6d9c4 Improved - disallow delete through the GUI if asset model has assets associated
(We prevent this on the backend anyway, but this makes for nicer UI)
2017-12-12 06:49:04 -08:00
snipe
4ce91a4f5d Fixed namespace gate for components - related to #4282 2017-12-12 06:26:37 -08:00
snipe
aff93d8f2b Fixed #4589 - Licence deployed to asset issue 2017-12-12 05:43:06 -08:00
snipe
e4ab4024c5 Added - changelog searchable in history tab 2017-12-12 05:10:13 -08:00
snipe
612f23f6e0 Added #2893 - track changes on asset edits 2017-12-12 04:59:28 -08:00
snipe
1d543f83d4 Added line spacing between unstyled lists 2017-12-12 04:31:53 -08:00
snipe
88ca852dcf Cleaned up asset observer 2017-12-12 03:40:04 -08:00
snipe
9fe2a7f81d Removed duplicate log entry on asset creation 2017-12-12 03:39:55 -08:00
snipe
a988011f0c Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-12 03:07:43 -08:00
snipe
c816870083 De-norm last audit date so we can display it in the asset listing 2017-12-12 03:03:43 -08:00
snipe
c5c46b7283 Removed duplicate deleted logaction
This is handled via observers now
2017-12-12 02:32:45 -08:00
snipe
8bb8306f80 Added restored string 2017-12-12 02:31:46 -08:00
snipe
d91e8bfee5 Fixed select2 on setup 2017-12-12 02:31:36 -08:00
snipe
2b3e5c8800 Moved “deleted” alert banner higher on page 2017-12-12 02:31:26 -08:00
snipe
5b00a8ae33 Use specific company_id column name in user search 2017-12-11 22:50:55 -08:00
snipe
5ee6e7f94b Fixed #4613 - Added table prefix to user search DB raw 2017-12-11 22:31:07 -08:00
snipe
f90271dae5 Aaaaand one more for #2810. Sigh. 2017-12-08 14:33:12 -08:00
snipe
e1423bd9d9 One more fix for #2810 2017-12-08 14:02:27 -08:00
snipe
557714e7b7 Fixed #2810 - checkin fix for licenses 2017-12-08 13:19:10 -08:00
snipe
3df62a200f Fixed manufacturer gates 2017-12-08 13:16:37 -08:00
snipe
a65ea639ed Added comments to SnipePermissionsPolicy for clarity 2017-12-07 21:00:09 -08:00
snipe
defed52caa Fixed #4596 - manufacturer gate 2017-12-07 20:59:55 -08:00
snipe
035f7a5292 Formatted version 2017-12-07 19:24:03 -08:00
snipe
0ea3100896 Bumped hash 2017-12-07 19:23:34 -08:00
snipe
2e0c2bd190 Add @Gelob as a contributor 2017-12-07 19:20:08 -08:00
snipe
ceca76b344 Merge branch 'develop' 2017-12-07 13:21:45 -08:00
snipe
a062769672 Check for array key before checking selected state
This is necessary for pre-created groups that don’t have all keys present
2017-12-07 13:21:30 -08:00
Ryan
af6a794ae9 fix typo (#4585) 2017-12-06 20:51:37 -08:00
snipe
f4ac087c83 Fixed policy namespace 2017-12-06 14:49:52 -08:00
snipe
4898dd8e23 Use ImageUploadRequest $request on user profile update 2017-12-06 14:42:14 -08:00
snipe
a090b6a9d2 Use ImageUploadRequest 2017-12-06 14:38:01 -08:00
snipe
023910472c Fixes #2415 - only allow gif, png jpg, svg image uploads 2017-12-06 14:33:52 -08:00
snipe
25bf10b125 Merge branch 'develop' 2017-12-06 11:19:45 -08:00
snipe
dfb0c09c51 Fixed #4581 - renamed print method 2017-12-06 11:17:42 -08:00
snipe
41c8d6302a Merge branch 'develop' 2017-12-05 18:17:32 -08:00
snipe
83547e3fed Prepopulate ther groups array on create 2017-12-05 18:17:11 -08:00
snipe
0d92ffc7ed Merge branch 'develop' 2017-12-05 18:13:08 -08:00
snipe
3d0525bc51 Removed debugging info 2017-12-05 18:11:59 -08:00
snipe
808cd0f728 Nicer groups permission edit UI 2017-12-05 18:11:06 -08:00
snipe
f5b3df697c Better select/unselect all javascript for permissions 2017-12-05 18:10:35 -08:00
snipe
29a36b5d1c Do not allow null id on group edit 2017-12-05 16:49:26 -08:00
snipe
ec131a7416 Updated account licenses 2017-12-05 16:27:02 -08:00
snipe
3da49ceb60 Standardize hidden key format 2017-12-05 16:26:39 -08:00
snipe
a19cef07bf Check for gate on license key display 2017-12-05 16:26:23 -08:00
snipe
c39e6d7006 Fixed #4576 - licence checkout gate 2017-12-05 15:04:10 -08:00
snipe
60f6895919 Remove version from asset table to persist column selections 2017-12-05 14:50:38 -08:00
snipe
bfa4812482 Exclude company on filter 2017-12-05 14:23:46 -08:00
snipe
a083cdba18 Merge branch 'develop' 2017-12-05 14:04:55 -08:00
snipe
438f484d68 Removed duplicate purchase cost column 2017-12-05 14:03:08 -08:00
snipe
f95d780fcf Include purchase cost column if depreciation is selected 2017-12-05 14:02:43 -08:00
snipe
79fe092c81 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-05 13:20:39 -08:00
snipe
027f6a7c12 Bumped hash 2017-12-05 13:18:52 -08:00
snipe
76fe2af0af Fixed #4444 - Added EOL back into asset lsiting and custom report 2017-12-05 13:03:11 -08:00
snipe
e490185533 Fixed EOL in custom report 2017-12-05 12:54:19 -08:00
snipe
deba4d2b81 Added consumables tio printable output 2017-12-05 12:37:42 -08:00
snipe
a8cc29f062 Added #2562 - print view of all assigned inventory 2017-12-05 12:34:16 -08:00
snipe
c9e6a75ea8 Make sure there is a valid status label associated withthe asset when checking if deployable 2017-12-05 11:28:48 -08:00
snipe
7efa7ec03f Starter print template for all assets assigned to a user 2017-12-05 01:55:24 -08:00
snipe
9eef7a94ab Fixed #4557 - added missing fields in components/consumables list view 2017-12-05 00:40:30 -08:00
snipe
244eceadec Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-04 23:51:53 -08:00
snipe
28fdbdd5d8 Bumped version 2017-12-04 23:50:45 -08:00
Brady Wetherington
4584990cc3 Cleaner re-implementation of loop-detection for asset-assigned-assets (#4553)
* Cleaner re-implementation of loop-detection for asset-assigned-assets

* Get rid of the other static and pass it along recursively too.
2017-12-04 23:00:55 -08:00
snipe
4f3c932bb1 Merge branch 'develop' 2017-12-04 20:45:46 -08:00
snipe
d7f2bceea2 Fixed incorrect property on supplier 2017-12-04 20:45:20 -08:00
snipe
18c1b2b477 Merge branch 'develop' 2017-12-04 20:43:24 -08:00
snipe
174e3e720a Fixed #4521 - profile location not selected
This is kind of a janky way to handle this - I created a new dropdown select, instead of adding even more convoluted code to the partial. The reasoon for this is because there may be situations where $user is passed in *addition* to $item, and we don’t want to erroneously use that location if we’re not supposed to be.
2017-12-04 20:30:57 -08:00
snipe
fdaa279930 Fixed #4548 - add cateory to model dropdown 2017-12-04 20:19:30 -08:00
snipe
a4323a0308 Fixed #4559 - min width for checkin/checkout buttons 2017-12-04 19:57:15 -08:00
snipe
ec4bed436c Fixed #4565 - show manufacturer in models selectlist 2017-12-04 19:52:18 -08:00
snipe
636c558fe6 Fixed #4566 - search by purchase_date 2017-12-04 15:26:40 -08:00
snipe
29873f9c22 Merge branch 'develop' 2017-12-01 21:58:23 -08:00
snipe
912ee20f3c Better supplier/location flow for suppliers/locations without data 2017-12-01 21:58:00 -08:00
snipe
9f10080243 Fixed parsed error 2017-12-01 21:13:18 -08:00
snipe
b3c386663f Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-12-01 21:11:33 -08:00
snipe
f2d25ff777 Added device image to supplier 2017-12-01 21:06:36 -08:00
snipe
dec9d959db Added additional user/asset fields to location view 2017-12-01 21:06:16 -08:00
snipe
2aafdb1400 Bumped version 2017-12-01 20:49:04 -08:00
snipe
c6b02cdc02 Location layout tweaks 2017-12-01 20:35:15 -08:00
snipe
f0c825a9b3 Improved suppliers view (use ajax tables) 2017-12-01 20:29:51 -08:00
snipe
79c035da11 Added map alt tag 2017-12-01 20:29:04 -08:00
snipe
9deafd771e Fixed bad suppliers error route 2017-12-01 20:28:37 -08:00
snipe
cbc09f3a12 Fixes #4508 - adds image to location view (and google map, if address and API key exist) 2017-12-01 19:12:35 -08:00
snipe
22c4d79cfb Fixed #4345 - Disallow future date for checkin date, disallow past date for expected checkin 2017-12-01 17:52:56 -08:00
snipe
71b9a15c9c Moved User menu down 2017-12-01 17:50:01 -08:00
snipe
db328e1ce5 Fixed granular hash resetting cookie 2017-12-01 17:49:44 -08:00
snipe
8b4c85d69a Removed verbose logging 2017-12-01 16:56:50 -08:00
snipe
78a51d3675 Additional fixes, improvements for custom report 2017-12-01 16:51:38 -08:00
snipe
a5bcf53146 Pass $required variable to display requiredness 2017-12-01 16:51:22 -08:00
snipe
9f97b4aefd Added created_at status, and manufacturer filter to custom report 2017-12-01 16:50:56 -08:00
snipe
62468199af Wider column for checkboxes 2017-12-01 16:50:16 -08:00
snipe
c220315cb0 Check if item is set before trying to look up selected 2017-12-01 16:49:51 -08:00
snipe
5a3233da37 Grr. 2017-12-01 14:54:18 -08:00
snipe
80109071a2 Fixed version number 2017-12-01 14:54:02 -08:00
snipe
c43bb670d2 Bumped hash 2017-12-01 14:50:46 -08:00
snipe
14874d8e8a Fixes #4495 - missing field in Download CSV, added filter options on custom report 2017-12-01 14:47:52 -08:00
snipe
ff793f1cb5 Added variable to determine if “new” modal button should be displayed 2017-12-01 14:37:11 -08:00
snipe
500f6d7baf Set requiredness of select2 ajax fields 2017-12-01 14:36:27 -08:00
snipe
ee9a229c0e Added datepicker range CSS 2017-12-01 14:26:01 -08:00
snipe
8140bdaa88 Fixed repeated header issue in asset export 2017-12-01 14:25:32 -08:00
snipe
5979a18852 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-30 16:43:59 -08:00
snipe
5efb803b60 Auto-bumped hash/version 2017-11-30 16:41:06 -08:00
snipe
fd4a8edae9 Fixed incorrect glyphs for users/locations 2017-11-30 16:40:58 -08:00
snipe
e9fdf06bf6 Improved display of asset status and meta status 2017-11-30 16:40:37 -08:00
snipe
0a5b72e71e Fixed #4517 - order number not visible if no purchase cost given on asset view 2017-11-30 16:39:59 -08:00
snipe
51168e8e10 Auto-bumped hash/version 2017-11-30 13:40:36 -08:00
snipe
cefdca3d22 Fixed incorrect has vs input 2017-11-30 13:40:26 -08:00
snipe
cc5eee1890 Auto-bumped hash/version 2017-11-30 13:32:12 -08:00
snipe
d3864db5e1 Switched to use $request from Input:: facade 2017-11-30 13:32:00 -08:00
snipe
ece8ae3adc Fixed #4542 and #4482 - default asset location not updating on biulk edit 2017-11-30 13:31:46 -08:00
snipe
e25829c759 Removed extra debug logging 2017-11-30 13:30:20 -08:00
snipe
92afd5f232 Removed debugging console code 2017-11-30 11:48:59 -08:00
snipe
b934d2e504 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-27 22:19:24 -08:00
snipe
8cf70e7e20 Auto-bumped hash/version 2017-11-27 22:10:54 -08:00
snipe
cdfd720c65 Fixed custom fields edit behavior with cutom format always selected 2017-11-27 22:10:46 -08:00
snipe
c0f791cf13 Sigh. 2017-11-27 21:35:06 -08:00
snipe
290cf79778 Auto-bumped hash/version 2017-11-27 21:25:21 -08:00
snipe
8af1481749 Auto-bumped hash/version 2017-11-27 21:20:38 -08:00
snipe
d8f404096c Added spacing in js 2017-11-27 21:20:31 -08:00
snipe
ea2f7617df Validate checkout_to_type on asset checkout 2017-11-27 21:20:12 -08:00
snipe
b6c258bb12 Redirect to back on bad checkout 2017-11-27 21:19:50 -08:00
snipe
804b49cefb Make sure the asset is available for checkout before displaying the checkout screen 2017-11-27 21:18:29 -08:00
snipe
305b0d8edb Fixed #4522 - properly check for valid target, throw error otherwise 2017-11-27 21:17:58 -08:00
snipe
05996019e5 Use asset checkout request in API 2017-11-27 21:17:16 -08:00
snipe
31a967e072 Use general order_number string to mean order number 2017-11-27 21:16:51 -08:00
snipe
0b56ebf291 Fixed radio button selector to provide correct default value in checkout 2017-11-27 21:16:25 -08:00
snipe
fcc87b3219 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-24 11:27:54 -08:00
snipe
82fca0c72d Auto-bumped hash/version 2017-11-24 11:25:57 -08:00
snipe
51661b0a21 Fixed #4190 - Added artisan command to regenerate asset tags 2017-11-24 11:25:51 -08:00
snipe
e8670fe591 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-11-24 10:53:20 -08:00
Daniel Meltzer
bee1dfc4a6 More importer fixes (#4516)
* The default locale of en does not include dollar sign in default currency.  Assume if there is no currency symbol set that the dollar sign is a good thing to look for in parsefloat.

* Fix for 4485.  Serial not serial_number

Also fix bug where updating with a csv that does not include custom field columns should not overwrite current values.

* Rename serial_number to serial in default imports to avoid needing to map weirdly.

* Add Test for 4359.  Not reproducable at current though
2017-11-24 10:42:11 -08:00
snipe
83c8449aca Auto-bumped hash/version 2017-11-22 18:43:18 -08:00
snipe
9dba1bb3e5 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-22 18:43:15 -08:00
snipe
76e3398d44 Auto-bumped hash/version 2017-11-22 18:42:33 -08:00
snipe
a7e12931fa Check that assigned is an object in reports controller 2017-11-22 18:42:17 -08:00
snipe
ba04c64567 Auto-bumped hash/version 2017-11-22 18:34:03 -08:00
snipe
8c8352ecc6 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-22 18:33:56 -08:00
snipe
64b670033d Auto-bumped hash/version 2017-11-22 18:30:45 -08:00
snipe
b9d102a5fb Truncate the fieldset table and pivot table on seed as well 2017-11-22 15:57:05 -08:00
snipe
77076e02e8 Fixed - mark supplier as requried in maintenance 2017-11-22 15:22:44 -08:00
snipe
c6a761a5ad Fixed - supplier is not required on asset creation/edit 2017-11-22 15:20:46 -08:00
snipe
6f3a90c48b Check that the assigned data is valid 2017-11-22 15:07:34 -08:00
snipe
8f160a8590 Fixed CSV asset export missing checked out to info 2017-11-22 13:05:48 -08:00
snipe
2278d5bfd8 Fixed restore permission to assets API disable delete/checkin/checkout on deleted assets 2017-11-22 10:35:24 -08:00
snipe
66ac147c9e Auto-bumped hash/version 2017-11-22 07:32:40 -08:00
snipe
05e0d15ae4 Auto-bumped hash/version 2017-11-22 07:31:50 -08:00
snipe
8562f018ed Fixed - conflicting error when a user and asset were both checked out 2017-11-22 07:31:38 -08:00
snipe
0cbdcce3ea Production asset manifest 2017-11-22 07:07:33 -08:00
snipe
980be65193 Added ability to turn items on/off in checkout-selector 2017-11-22 07:07:18 -08:00
snipe
3aaaea37e4 Added - sync locations artican call after seed 2017-11-22 06:21:56 -08:00
snipe
964c594c4c Added ItemFormatter for acrtivity report 2017-11-22 06:21:06 -08:00
snipe
9430c4bf43 Added sanity checks in BS tables formatter 2017-11-22 06:20:51 -08:00
snipe
538757317b Fixed #4411 - broken activity report if purge didn’t remove relationship entries 2017-11-22 06:20:28 -08:00
snipe
fe986f7c51 Auto-bumped hash/version 2017-11-21 22:45:10 -08:00
snipe
09105871d9 Auto-bumped hash/version 2017-11-21 22:43:33 -08:00
snipe
e84a6059f4 Merge branch 'develop' 2017-11-21 22:43:00 -08:00
snipe
09f20873df Auto-bumped hash/version 2017-11-21 22:35:05 -08:00
snipe
2adc1e8ba9 Auto-bumped hash/version 2017-11-21 22:35:04 -08:00
snipe
4a6c18532b Auto-bumped hash/version 2017-11-21 22:35:02 -08:00
snipe
921f882680 Auto-bumped hash/version 2017-11-21 22:35:01 -08:00
snipe
f79e5add58 Auto-bumped hash/version 2017-11-21 22:34:59 -08:00
snipe
d98d06377e Fixed #4098 - autolink URL and email addresses in listing 2017-11-21 22:34:53 -08:00
snipe
22fdd05314 Add UTF charset 2017-11-21 22:34:07 -08:00
snipe
8c15a4e0c6 Auto-bumped hash/version 2017-11-21 21:00:07 -08:00
snipe
9250b45e6e Auto-bumped hash/version 2017-11-21 21:00:05 -08:00
snipe
3a1de3d2a5 Auto-bumped hash/version 2017-11-21 21:00:02 -08:00
snipe
b59dd11304 Auto-bumped hash/version 2017-11-21 21:00:00 -08:00
snipe
b6222abb7c Auto-bumped hash/version 2017-11-21 20:59:58 -08:00
snipe
dcf8e4f5ef Auto-bumped hash/version 2017-11-21 20:59:54 -08:00
snipe
f195073ac3 Auto-bumped hash/version 2017-11-21 20:59:52 -08:00
snipe
452a9d6725 Auto-bumped hash/version 2017-11-21 20:59:50 -08:00
snipe
1d74ddc547 Auto-bumped hash/version 2017-11-21 20:59:45 -08:00
snipe
932b589a14 Auto-bumped hash/version 2017-11-21 20:59:43 -08:00
snipe
671e514785 Auto-bumped hash/version 2017-11-21 20:59:41 -08:00
snipe
1e2ebdb69c Auto-bumped hash/version 2017-11-21 20:59:39 -08:00
snipe
148751d927 Auto-bumped hash/version 2017-11-21 20:59:37 -08:00
snipe
efecdfaea0 Auto-bumped hash/version 2017-11-21 20:59:35 -08:00
snipe
ab9c84a6b6 Auto-bumped hash/version 2017-11-21 20:59:33 -08:00
snipe
8711bc0dbd Added comments for toggle checkout-to JS 2017-11-21 20:55:57 -08:00
snipe
0adebd1ec8 Add sorting and additional category types to dashboard categories
BREAKING CHANGE: Category type now reports as `category_type`, instead of `type`
2017-11-21 20:33:30 -08:00
snipe
43c1e893c0 Fixed #4494 - use audit settings for asset audit pre-populaton 2017-11-21 20:13:51 -08:00
snipe
7ce63e653b Auto-bumped hash/version 2017-11-21 19:39:53 -08:00
snipe
e5129a8b98 Removed next_version 2017-11-21 19:39:33 -08:00
snipe
a922c1a298 Auto-bumped hash/version 2017-11-21 19:38:29 -08:00
snipe
f4aa812d96 Changed twitter handle to @snipeitapp 2017-11-21 19:02:15 -08:00
snipe
0c9e41e1fa Updated versioning script to handle githooks better 2017-11-21 19:01:47 -08:00
snipe
1d6320a88f Merge branch 'develop' 2017-11-21 16:26:41 -08:00
snipe
4696e799ed Fixes #4491 and #4483 - handle pre-selected asset on checkout, better checkout-to selection UI (#4501)
* Added form checkout selector partial

* Stupid stash

* Added radio button checkout selector javascript

* New compiled production assets

* Added $style override in form partials for select2 ajax lists

* Added checkout-to radio button selector

TODO: Fix for accessibility - currently cannot tab-select this radio button

* Added new checkout-to selector to hardware edit

* Added new checkout-to selector to asset checkout form

* Refactored postCheckout to use radio button submission

This defaults to user checkout if nothing is passed for some reason

* Better visual feedback on whether or not an asset is deployable in edit screen
2017-11-21 15:58:31 -08:00
snipe
2e08a91c53 Merge branch 'develop' 2017-11-20 21:48:57 -08:00
snipe
4fd35573ad Bumped version 2017-11-20 21:20:34 -08:00
snipe
caf2cdbf6f Merge branch 'develop' 2017-11-20 20:51:22 -08:00
snipe
c0293a7c1c Add @uknzaeinozpas as a contributor 2017-11-20 19:39:04 -08:00
snipe
17405f5de1 Fixed #4413 - Next license seat not bering assigned correctly 2017-11-20 19:21:05 -08:00
snipe
bfefa10462 Add @TheVakman as a contributor 2017-11-20 18:54:43 -08:00
snipe
55a140045b Fixed upgrade language 2017-11-16 16:50:16 -08:00
snipe
897bd2c56e Fixed location sorting location instead of rtd 2017-11-16 16:49:48 -08:00
snipe
7321c5937f Fixed #4440 - allow username search in asset dropdown 2017-11-16 16:49:16 -08:00
snipe
98700cab8b Merge branch 'develop' 2017-11-16 14:14:49 -08:00
snipe
3d07635820 Fixed sort by model in asset listing 2017-11-16 14:14:30 -08:00
snipe
59b18a6ada Merge branch 'develop' 2017-11-16 13:49:05 -08:00
snipe
ce525c1985 Fixed #4471 - removed gate for categories selectlist 2017-11-16 13:48:38 -08:00
snipe
a0d163a3c4 Merge branch 'develop' 2017-11-16 11:41:18 -08:00
snipe
faab971931 Improved - removed 500 pixel width on 2FA dropdown 2017-11-16 11:09:42 -08:00
snipe
38eb16dfea Fixed #4408 - requestable assets not working 2017-11-16 11:09:24 -08:00
snipe
4494456cad Merge branch 'develop' 2017-11-15 15:51:50 -08:00
snipe
876dde1280 Fixed #4428 - updated translations 2017-11-15 15:31:34 -08:00
snipe
17ee904828 Fixed #4450 - added MAIL_REPLYTO_ADDR to docker.env 2017-11-15 14:48:45 -08:00
snipe
4f1b58cd35 Merge branch 'develop' 2017-11-15 14:27:47 -08:00
snipe
edcd3afc3e Fixes #4457 - use un-escaped CSS for custom CSS styles
We are already escaping the CSS in the show_custom_css() method
2017-11-15 14:27:21 -08:00
snipe
e433b0f9d8 Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-14 16:50:05 -08:00
snipe
f137e516a6 Bumped version 2017-11-14 16:49:18 -08:00
snipe
ba38b841cb Constrain accessory by category ID if one is passed 2017-11-14 16:47:21 -08:00
snipe
f7c6697a69 Merge branch 'develop' 2017-11-14 01:32:43 -08:00
snipe
498fc3762d Fixed #4437 - pagination for maintenances 2017-11-14 01:32:25 -08:00
snipe
eb24eb1fff Merge branch 'develop' 2017-11-14 00:04:27 -08:00
snipe
3e5e6ba99a Use table alias for models join in search/order by models 2017-11-14 00:04:03 -08:00
snipe
0396267388 Fixed #4412 - use select2 ajax list for asset maintenances 2017-11-12 17:22:16 -08:00
snipe
4577ba39d7 Merge branch 'develop' 2017-11-10 15:56:34 -08:00
snipe
7eef1b4bcf Fixed #4418 - order models by name asc in selectlist 2017-11-10 15:56:14 -08:00
snipe
578da128e9 Merge branch 'develop' 2017-11-09 18:59:25 -08:00
snipe
bb4d49690f Add purchase date to dates array so it’s treated as a Carbon date 2017-11-09 18:59:05 -08:00
snipe
7eb94d16a1 Merge branch 'develop' 2017-11-09 14:42:19 -08:00
snipe
65bd33c274 Make sure the user isn’t deleted before trying to display the name 2017-11-09 14:42:03 -08:00
snipe
0f7aa21a59 Merge branch 'develop' 2017-11-09 13:41:17 -08:00
snipe
ee0814716a Only format ecpected checkin date if one exists 2017-11-09 13:40:55 -08:00
snipe
f56e1768b6 Merge branch 'develop' 2017-11-09 13:28:12 -08:00
snipe
012afe99e2 Set purchase date to date type 2017-11-09 13:27:58 -08:00
snipe
0c02b6d24e Use —no-dev flag in upgrade 2017-11-09 13:25:44 -08:00
snipe
2f34336f2b Merge branch 'develop' 2017-11-09 11:18:21 -08:00
snipe
afe6f43a1b Exclude manufacturer on filter sort 2017-11-09 11:18:04 -08:00
snipe
738e33b165 Merge branch 'develop' 2017-11-09 11:06:50 -08:00
snipe
566fd4d2e1 Only format purchase date if one exists 2017-11-09 11:06:37 -08:00
snipe
c61887da21 Merge branch 'develop'
# Conflicts:
#	upgrade.php
2017-11-09 10:52:46 -08:00
snipe
6852b74317 Removed gates from selectlist method 2017-11-09 10:51:55 -08:00
snipe
4c266fdcf4 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-11-08 20:28:23 -08:00
snipe
c26a2f8291 Patching #4402 into develop 2017-11-08 20:28:18 -08:00
uknzaeinozpas
a994e54726 Update upgrade.php (#4402)
posix_* are not supported on Windows platform
2017-11-08 20:13:35 -08:00
madd15
172c7c75a8 Fixed #4374 - Add option to display company names to labels (#4405)
* Migration - Labels Display Company Name setting

* Add Company Name to Labels

Add company name if it is turned on in settings and asset has a valid company

* Add Company Name checkbox to Label settings

* Add Company Name Lang

* Add display company name to postLabels

* Revert Add Company Name Lang

* Fix display company name in postLabels

* Change tinyInt to boolean for display company name

* Simplify checking for company and getting the name

* Change to square brackets for array notation

* Move divs inside if statements on optional fields
2017-11-08 20:05:39 -08:00
snipe
487fd17ce3 Fixed ambiguous query when selecting by model_id 2017-11-08 20:03:26 -08:00
snipe
65353fa422 Nicer styling for purchase cost in edit form
Made it more consistent with the warranty months foeld below it
2017-11-08 18:06:51 -08:00
snipe
4f4920615c Format expected checkin as Y-m-d in form 2017-11-08 18:06:14 -08:00
snipe
c162c02304 Force expected checkin to be formatted as a date (not datetime) since it’s a date field in the DB 2017-11-08 18:03:47 -08:00
snipe
1bb1480f67 Added a comment around protected dates so we know wtf 2017-11-08 18:03:27 -08:00
snipe
8dceacc2b4 Merge branch 'develop' 2017-11-08 17:09:19 -08:00
snipe
ffae537400 Check that the models are valid being trying to return a value in the ajax partials 2017-11-08 17:07:57 -08:00
snipe
5ce7882bf5 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-11-08 17:01:50 -08:00
snipe
b36594f508 Check that the company is valid in select ajax 2017-11-08 17:01:46 -08:00
madd15
59c9c22a59 Small UI Changes (#4404)
* Small ui change to settings nav

* Remove min-height

Removed min-height from Assets by Status

* Add min-height to box-body
2017-11-08 13:12:03 -08:00
snipe
9097681c13 Changed log fomrat in example env to single 2017-11-08 11:08:11 -08:00
snipe
567f741d95 Merge branch 'develop' 2017-11-08 10:59:29 -08:00
snipe
9b1fb90519 Change default log type back to single 2017-11-08 10:58:56 -08:00
snipe
b567ffdcfe Fixed #2855 - checkin for components 2017-11-08 06:06:05 -08:00
snipe
9d44607b8f Added UI fade out when bootstrap alerts are dismissed 2017-11-08 03:13:18 -08:00
snipe
35ee52212f Added ability to disable the alert icon in the top menu 2017-11-08 03:08:17 -08:00
snipe
e9e32fdb00 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-11-08 02:46:40 -08:00
snipe
6c130ce8ac Apply patch #4354 2017-11-08 02:46:33 -08:00
Sorvani
dd7db0de93 Moved firewall commands from CentOS 7 to a routine and called form both CentOS 7 and Fedora. (#4366) 2017-11-08 02:37:34 -08:00
snipe
70efac8fa7 Production js 2017-11-08 02:24:34 -08:00
snipe
6e2556eefd Production assets 2017-11-08 02:24:06 -08:00
snipe
fb6a545cc6 Slightly nicer styling 2017-11-08 02:11:33 -08:00
snipe
e5c1e41966 Nicer icheck for user menu 2017-11-08 01:52:35 -08:00
snipe
61617a2629 Slightly less fugly groups permission styling 2017-11-08 01:19:20 -08:00
snipe
a3e80882c1 Better error handling for qr codes on invalid assets 2017-11-08 01:04:14 -08:00
snipe
31980c55de Merge branch 'develop'
# Conflicts:
#	config/version.php
2017-11-08 00:58:16 -08:00
snipe
d1022e8ff7 Fixed #4390 - results couldn’t be loaded error
The baseUrl in the javascript routes is already appending a trailing slash, so don’t prepend a slash in front of api call
2017-11-08 00:57:43 -08:00
snipe
e6ff447ee8 Bumped hash 2017-11-07 23:12:59 -08:00
snipe
1123272ae8 Bumped hash 2017-11-07 23:12:10 -08:00
snipe
74aa562a3b Merge branch 'develop' 2017-11-07 23:10:49 -08:00
snipe
74773ac912 Fixed incorrect policy reference in consumables listing 2017-11-07 23:05:29 -08:00
snipe
9764d2ad24 Removed commented code 2017-11-07 22:25:32 -08:00
snipe
d03b8c6528 Error handling for when log ID has no match on asset accept 2017-11-07 22:25:24 -08:00
snipe
99a355145e Removed empty comments 2017-11-07 22:24:57 -08:00
snipe
7233e2dded Merge branch 'develop' 2017-11-07 18:21:47 -08:00
snipe
eed50112d5 Better fallback for local that won’t break migrations 2017-11-07 18:18:27 -08:00
snipe
5aee5a3f3d Update location on checkout, error if bad target 2017-11-07 17:37:08 -08:00
snipe
f21c7ba312 Merge branch 'develop' 2017-11-07 11:28:41 -08:00
snipe
9d7455f022 Fixed handling deleting old images better 2017-11-07 11:28:13 -08:00
snipe
b748e7ed5e Fixed transformers to use new singleton upload urls 2017-11-07 11:11:47 -08:00
snipe
84a717c6ad Fixed deleted ordering scope 2017-11-07 11:06:38 -08:00
snipe
a202e1657c Fixed path for singletons 2017-11-06 21:58:28 -08:00
snipe
30ec919048 Remove AWS package
We unfortunately can’t use it right now because it requires a symlink from the storage/app directory. Until we have a better way of checking for configuration issues and/or automatically handling that symlink creation, we’re pulling it for now, since it’s not used anywhere.
2017-11-06 21:45:31 -08:00
snipe
4ae4083b7b Removed unused $matches variable 2017-11-06 21:26:30 -08:00
snipe
dec9ac1ac8 Fixed lowercase false 2017-11-06 21:25:40 -08:00
snipe
8776d28d3b Remove fastclick from package.json
We’re not using it anymore because of conflicts with select2. Select2 is more important for our UX than fastclick is.
2017-11-06 21:22:18 -08:00
snipe
231dea0ebc Break out service providers by responsibility 2017-11-06 21:17:17 -08:00
snipe
cceeb5c8a2 Disable Fastclick - conflicts with Select2, per #4392 2017-11-06 21:16:20 -08:00
snipe
579334b5fc Fixed - name should always be required in custom fields 2017-11-06 20:05:40 -08:00
snipe
3a82fbe714 Switch to rollbar’s official package 2017-11-06 20:04:50 -08:00
snipe
143071fa0c Merge branch 'develop' 2017-11-06 17:18:18 -08:00
snipe
0589652edb Fixed #4392 - select2 + fastclick incompatibility
This is a workaround fix - should upgrade both to latest after testing extensively
2017-11-06 17:17:48 -08:00
snipe
b772d8e527 Fix for IIS+ Chrome not showing webfonts
Thanks, @BrettFagerlund!
2017-11-06 16:51:46 -08:00
snipe
b96d4dcf1f Don’t show ANY in custom regex field if empty 2017-11-06 16:43:27 -08:00
snipe
dc32e4bdb0 Fixed form request ffor custom fields 2017-11-06 16:42:37 -08:00
snipe
43f2a530f5 Merge branch 'develop' 2017-11-06 12:39:00 -08:00
snipe
6ece593629 Fixed #4381 - asset uploads 2017-11-06 12:38:31 -08:00
snipe
33de0ec8a9 Merge branch 'develop' 2017-11-06 12:19:48 -08:00
snipe
8e17714c12 Fixed #4384 - bulk checkout of assets 2017-11-06 12:19:22 -08:00
snipe
32fc052c3b Fixed #4391 - companies now listing correct assets 2017-11-06 12:01:54 -08:00
snipe
17febca466 Merge branch 'develop' 2017-11-06 10:55:42 -08:00
snipe
8572a9771b Only return location name if find is valid 2017-11-06 10:55:20 -08:00
snipe
3ea294c0e6 Merge branch 'develop' 2017-11-06 10:45:14 -08:00
snipe
2b3b2e3197 Removed presenter references in custom report 2017-11-06 10:44:57 -08:00
snipe
a9d9234fb3 One more fix for custom reports 2017-11-06 10:44:18 -08:00
snipe
a11d9b84a4 Merge branch 'develop' 2017-11-06 10:42:48 -08:00
snipe
936ff707c7 Fixed custom report bug if assignedTo has no value 2017-11-06 10:42:34 -08:00
snipe
def69b1aaa Merge branch 'develop' 2017-11-06 10:30:42 -08:00
snipe
4818e1b8ca Fixed sorting for filtered items by location 2017-11-06 10:26:09 -08:00
snipe
623f21e51f Merge branch 'develop' 2017-11-04 18:21:09 -07:00
snipe
bd524ff2f3 Removed buggy migration code switched to artisan command 2017-11-04 18:20:41 -07:00
snipe
04ab522ee3 Fixes #4236 - validate the regex custom validation (#4380)
* More helpful text on how the custom validator works

* Clarified language of custom format, fixed regex example

* Fixed regex example in placeholder

* Added comments to custom fields

* Added regex validation string

* Added valid_regex validator in format requirements

* Removed useles comments

* Fixes #4236 - validate the regex custom validation
2017-11-04 17:06:14 -07:00
snipe
f672b14468 Ignore deleted assets and check for valid location in artisan command 2017-11-04 16:20:31 -07:00
snipe
b0973de1a6 Merge branch 'develop' 2017-11-04 01:24:23 -07:00
snipe
fe06ef10f1 Set the items array variable for no results 2017-11-04 01:23:50 -07:00
snipe
14ca690441 Merge branch 'develop' 2017-11-04 01:11:41 -07:00
snipe
2de0a3669e Check that there is a valid array before unshifting 2017-11-04 01:11:11 -07:00
snipe
c7c63e8432 Merge branch 'develop' 2017-11-04 00:49:19 -07:00
snipe
adf6afbb43 Moved delay back to 250 on ajax menus 2017-11-04 00:48:54 -07:00
snipe
69b90fb65e Merge branch 'develop' 2017-11-04 00:46:22 -07:00
snipe
97ea68b15c Updated translations 2017-11-04 00:45:50 -07:00
snipe
badc763c06 Bumped version 2017-11-04 00:32:33 -07:00
snipe
b6a14d2c9c Production assets 2017-11-04 00:20:06 -07:00
snipe
d59dd0f636 Reduced rate limit from 250 to 100 2017-11-04 00:20:00 -07:00
snipe
d68d95a915 Fixed - Added a “clear selection” option to select2 ajax lists 2017-11-04 00:19:16 -07:00
snipe
15d4344efb Comments in the JS 2017-11-04 00:15:23 -07:00
snipe
dc10f18188 Merge branch 'develop' 2017-11-03 21:53:13 -07:00
snipe
2522bfee9c Only return mnon-archived assets by location 2017-11-03 21:46:11 -07:00
snipe
d7f8615964 Fixed query for location ID 2017-11-03 20:10:36 -07:00
snipe
88dff754b1 Fixed swapped asset total headings 2017-11-03 20:10:16 -07:00
snipe
ecd21074fb Commented out erroneous fixme 2017-11-03 20:10:05 -07:00
snipe
12caa48390 Uncommented info output 2017-11-03 19:42:54 -07:00
snipe
cc7be5f947 Added location to checkout in API 2017-11-03 19:42:45 -07:00
snipe
5b489e003d Set the location_id on save/update to rtd if one is given 2017-11-03 19:41:26 -07:00
snipe
8c1f4b006e Removed comment 2017-11-03 19:40:55 -07:00
snipe
3a52c19428 Updated fieldname in Locations transformer and API 2017-11-03 19:40:40 -07:00
snipe
279ad6d80a Updasted fieldname in locations table 2017-11-03 19:40:04 -07:00
snipe
b786791401 Added location to checkout method 2017-11-03 19:39:48 -07:00
snipe
1c12b6e13b Added artisan command to sync locations 2017-11-03 19:00:36 -07:00
snipe
c06539dee3 Update the location ID if there is an rtd_location given 2017-11-03 17:36:18 -07:00
snipe
3b9544d1f3 Remove commented code 2017-11-03 17:35:46 -07:00
snipe
da9bb07041 Add a default legacy cipher of rijndael-256 2017-11-03 16:27:03 -07:00
snipe
5d9b9ad590 Merge branch 'develop' 2017-11-03 15:05:07 -07:00
snipe
f95502ae35 Fixed search by model on adsvanced search for assets 2017-11-03 15:04:21 -07:00
snipe
877daba096 Merge branch 'develop' 2017-11-03 14:59:38 -07:00
snipe
b3b8ab493e Switch to the reply_to address 2017-11-03 14:58:49 -07:00
snipe
1859c8f5ab Reply to seems to be overwritten 2017-11-03 14:55:03 -07:00
snipe
87ba042b2d Fixed manager name subquery on user search
Self-joins in Laravel make baby jesus cry :(
2017-11-03 14:47:31 -07:00
snipe
6fb0ef908d Fixed issue sorting when viewing users by department 2017-11-03 14:29:04 -07:00
snipe
c4a30cc646 Merge branch 'develop' 2017-11-03 13:29:33 -07:00
snipe
cf56f70b3a Added table alias for sorting 2017-11-03 13:28:57 -07:00
snipe
f84e6a34cc Merge branch 'develop' 2017-11-03 13:04:18 -07:00
snipe
85360a7c7f Removed gates on selectlists
They can’t access the API directly unless they have been granted API accesses anyway
2017-11-03 13:03:57 -07:00
snipe
9e0b3afa91 Merge branch 'develop' 2017-11-03 12:48:17 -07:00
snipe
416455fe01 Fixes weird manager_id validation
This is a shit fix - need to find out what’s happening here.
2017-11-03 12:48:00 -07:00
snipe
53a1511cac No idea why this is needed, but… 2017-11-03 12:20:11 -07:00
snipe
733921f1f9 Added optional required parameter 2017-11-03 12:17:41 -07:00
snipe
3ecaa99990 Fixed only undeployed assets in checkout to list 2017-11-03 11:33:36 -07:00
Kasey
ab9729c39a fix to availCount() (licenseSeatRelation) (#4378)
`license_seats`.`user_id` represents an overall "owner" of the license
2017-11-03 09:51:15 -07:00
snipe
104cc2bf11 Make sure the seat hasn’t been deleted 2017-11-02 21:07:59 -07:00
snipe
1659c3f1a6 Fixed inconsistent color type on checkin/checkout 2017-11-02 20:42:07 -07:00
snipe
caa8ec3178 Fixed checkout on license view page 2017-11-02 20:23:17 -07:00
snipe
0c794c103b Return an integer value for free seats if null 2017-11-02 20:23:04 -07:00
snipe
53175d5035 Fixed sorting issue on company/manufacturer/supplier 2017-11-02 20:01:39 -07:00
snipe
0bd09f9c46 Added sorting on available and total seats 2017-11-02 19:37:30 -07:00
snipe
27d795508d Fixed n+1 query, changed checkout behavior to just ask for a license ID
We’re offloading the freeSeat() to the checkout page now
2017-11-02 19:16:09 -07:00
snipe
368ac5b85d First stab at handling the n+1 issue on licenses 2017-11-02 18:20:42 -07:00
snipe
d5635f32e5 Merge branch 'develop' 2017-11-02 17:23:14 -07:00
snipe
f47075c180 Removed depreciation from Licenses transformer 2017-11-02 17:18:53 -07:00
snipe
0a5e4b9b7b Fixed inconsistent required field indicator 2017-11-02 17:13:06 -07:00
snipe
85624205b4 Updated Lang::get to trans 2017-11-02 17:12:51 -07:00
snipe
2330e5ee57 Merge branch 'develop' 2017-11-02 16:20:48 -07:00
snipe
c9c5ce6ee0 Return Deleted User if the user is invalid for some reason 2017-11-02 16:20:01 -07:00
snipe
fc3a59d193 Merge branch 'develop' 2017-11-02 13:47:52 -07:00
snipe
950519be5d Added a few more table aliases for ordering complex results 2017-11-02 13:47:37 -07:00
snipe
e64cf8b320 Merge branch 'develop' 2017-11-02 13:01:28 -07:00
snipe
87affa40ed Fixes filtering on status label 2017-11-02 13:01:09 -07:00
snipe
513a1b1e3b Bumped version 2017-11-02 12:52:44 -07:00
snipe
cf09908c60 Merge branch 'develop' 2017-11-02 12:50:56 -07:00
snipe
d21c92f91b Changed assetloc to location 2017-11-02 12:50:34 -07:00
snipe
ffd93c59d6 Merge branch 'develop' 2017-11-02 11:26:13 -07:00
snipe
01bb4bf64a Bumped version 2017-11-02 11:24:01 -07:00
snipe
28a4293a0b Fixed #4370 - user’s listing flashing then no results 2017-11-02 11:19:34 -07:00
snipe
f095f1807c Eager load userloc 2017-11-02 11:06:15 -07:00
snipe
e08911ab8f Removed nonce for now
There is a dependency in a package where we can’t edit the script tags to add the nonce
2017-11-02 10:57:05 -07:00
snipe
ca6dc5c2b5 Eager load user location 2017-11-02 10:56:36 -07:00
snipe
27875c2dac Improved to use trans() facade instead of lang 2017-11-02 10:27:53 -07:00
snipe
6f886d3d6e Merge branch 'develop' 2017-11-02 08:44:40 -07:00
snipe
fd74e4308b Don’t show request if the user or the asset/model has been deleted 2017-11-02 08:11:04 -07:00
snipe
3695e118f4 Fixed erroneous call to assets.category in filter scope 2017-11-02 08:06:10 -07:00
snipe
f43692938b Fixed slug call in asset models image 2017-11-02 08:02:51 -07:00
snipe
6008eec205 Merge branch 'develop' 2017-11-02 04:37:27 -07:00
snipe
3343cf16dd Bumped version 2017-11-02 04:37:08 -07:00
snipe
48207fc695 Added model number in box header 2017-11-02 04:36:04 -07:00
snipe
3dae464c34 Added nicer formatting for model details 2017-11-02 04:33:53 -07:00
snipe
0c524e0830 Use model image if it’s a requestable model 2017-11-02 04:29:05 -07:00
snipe
da56a253bc Added checkout requests method 2017-11-02 04:21:57 -07:00
snipe
a844d5b018 Added pagination, nicer formatting for requested assets 2017-11-02 04:17:14 -07:00
snipe
ba9bb470eb Added imageSrc presenter to assets 2017-11-02 04:15:24 -07:00
snipe
41452450b3 Added imageSrc presenter 2017-11-02 04:15:09 -07:00
snipe
a9e5ad0df1 Added link to requested assets in sidenav 2017-11-02 03:12:12 -07:00
snipe
0e2f4f3cfb Added requested route back in 2017-11-02 03:11:09 -07:00
snipe
3bc9d3f3f1 Merge branch 'develop' 2017-11-01 23:47:36 -07:00
snipe
81ca0ac91d Added better styling for user upload 2017-11-01 23:46:21 -07:00
snipe
3ca5d39c66 Hide upload button if app is locked 2017-11-01 23:44:31 -07:00
snipe
4f008e118f Fixed search string on suppliers selectlist 2017-11-01 21:55:17 -07:00
snipe
e11f9313f0 Fixed #4360 - better output if backup fails 2017-11-01 14:12:18 -07:00
snipe
d379c6b61f Merge branch 'develop' 2017-11-01 13:28:19 -07:00
snipe
d36e8cfbd2 Dashbpoard pie fixes 2017-11-01 13:27:59 -07:00
snipe
4cdcbc97ee Fixed varname issue on old image delete for manufacturers 2017-11-01 13:10:56 -07:00
snipe
ba516ac9af Merge branch 'develop' 2017-11-01 11:11:32 -07:00
snipe
feb2f5b076 Fixed #4356 - removed reference to old assetloc 2017-10-31 18:20:03 -07:00
snipe
b5f1e10b45 Merge branch 'develop' 2017-10-31 11:50:47 -07:00
snipe
a1eac967a7 Few more migration fixes 2017-10-31 08:57:57 -07:00
snipe
6186c324b5 Misc assetLoc error checks 2017-10-31 08:47:40 -07:00
snipe
772785f9b5 Few more fixes for big ugly location migration 2017-10-31 08:39:53 -07:00
snipe
2f6c0cee59 Merge branch 'develop' 2017-10-31 07:46:36 -07:00
snipe
e56f1ee6fd Fix for anomolies where asigned_type is not null but asigned_to is 2017-10-31 07:33:43 -07:00
snipe
37868cd70e Added warranty and expiration to list view 2017-10-31 07:05:15 -07:00
snipe
32b2f77ad9 Fixed issue where we tried to call the audit log even if the asset wasn’t valid 2017-10-31 05:41:06 -07:00
snipe
472a5b9f69 Removed extra log on API asset create 2017-10-31 05:38:52 -07:00
snipe
121e158f39 Change method name from userloc to location
This needs to be changed in more places though
2017-10-31 05:22:57 -07:00
snipe
f4e7bfc28d Null custom field if field exists but is empty 2017-10-31 05:22:21 -07:00
snipe
0089f73686 Moved trait to single line 2017-10-31 05:21:55 -07:00
snipe
0f4c05c5d0 Remove commented code 2017-10-31 05:02:46 -07:00
snipe
379274deff Check for multiple variable (for bulk checkout) 2017-10-31 04:48:40 -07:00
snipe
dbf5fec7b0 Fixed language string 2017-10-31 04:48:09 -07:00
snipe
4bb546a882 Pull assigned asets preview into its own blade for re-use 2017-10-31 04:47:59 -07:00
snipe
7f1b7be416 Rolling back prepending models to sort 2017-10-30 21:26:25 -07:00
snipe
02720f225c Fixed sorting issue in asset models when ordering by manufacturer 2017-10-30 20:51:01 -07:00
snipe
e44e573a3c Fixed requestable assets reference to assetloc 2017-10-30 19:40:35 -07:00
snipe
546c3e50fa Fixed updating the assigned assets location if user’s location changes 2017-10-30 19:33:52 -07:00
snipe
7f1b962e56 Improved use of de-normed locations fields 2017-10-30 19:21:35 -07:00
snipe
4eee7f8d97 Added address for users - fixes #4323 2017-10-30 18:57:00 -07:00
snipe
0fd0e3a8b4 Returns null for order number if it’s blank 2017-10-28 15:53:22 -07:00
snipe
1076ec96be Bumped version 2017-10-28 15:46:02 -07:00
snipe
3b498efee1 Fixes indefined item in user select blade 2017-10-28 15:42:55 -07:00
snipe
9687a78981 Fixed a few inconsistencies in the API results (null vs empty string) 2017-10-28 15:17:36 -07:00
snipe
2244a4b3cf Fix peresenter for assigned 2017-10-28 15:17:09 -07:00
snipe
f3f84f1a8c Production assets 2017-10-28 11:22:38 -07:00
snipe
41994c95e0 Bumped version 2017-10-28 11:22:27 -07:00
snipe
39c68214e9 More ajax menu fixes 2017-10-28 11:17:52 -07:00
snipe
9c94e70917 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-28 09:21:49 -07:00
snipe
6a3716a06d Added new ajax dropdown menus for components, consumables, etc editing/creating 2017-10-28 09:21:39 -07:00
Sorvani
feccc55c54 Added support for Fedora to the installer. (#4332) 2017-10-28 09:17:19 -07:00
snipe
fe70792cbd Bumped hash 2017-10-28 08:41:34 -07:00
snipe
95b6e0d2d8 Fixed assetloc to location 2017-10-28 08:40:27 -07:00
snipe
5d890fb139 Added more defaults for selected values 2017-10-28 08:38:19 -07:00
snipe
cd2816b1c7 Suppliers ajax menu API route 2017-10-28 08:38:00 -07:00
snipe
2172e6cc25 Added suppliers ajax list 2017-10-28 08:37:47 -07:00
snipe
04130a568c Fixes check for help_text 2017-10-28 07:41:13 -07:00
snipe
108ac79442 Added update to asset location id on checkout/checkin 2017-10-28 07:38:36 -07:00
snipe
3d7fd5cf04 Fixed references to assetLoc in hardware view 2017-10-28 07:29:32 -07:00
snipe
5737de2e22 Added help text to location partial 2017-10-28 07:29:14 -07:00
snipe
1e21cef218 Set max page size to 500 2017-10-28 07:29:03 -07:00
snipe
ad7a2da9bd Add help text that explains location override 2017-10-28 07:28:49 -07:00
snipe
bd48ae96c2 Update location on checkin if one is given 2017-10-28 07:28:35 -07:00
snipe
0f5e0dcd4f Added nobr for nicer formatting of bs tables status and deployed to 2017-10-28 07:12:47 -07:00
snipe
c37fa44f72 Use ther morphto assigned 2017-10-28 07:12:22 -07:00
snipe
daaf98783f Fixes status display in listing 2017-10-28 07:11:06 -07:00
snipe
1399ebb133 Nicer formatting on 503 2017-10-28 07:01:45 -07:00
snipe
03f6211582 Make notes null if empty 2017-10-28 07:01:06 -07:00
snipe
20bcc73000 Don’t emit to logs on this migration, otherwise we’ll flood the actionlogs 2017-10-28 06:33:15 -07:00
snipe
0058f02e82 Closure wasn’t working, go traditional 2017-10-28 06:23:04 -07:00
snipe
25b8c4438e Check for asset before attempting to cite mismatch 2017-10-28 06:20:44 -07:00
snipe
4f1747023a Seed with demo images 2017-10-28 05:46:43 -07:00
snipe
46fb5c9d40 Remove die() from migration 2017-10-28 03:54:06 -07:00
snipe
52f10232a1 Merge branch 'features/flatten-locations' into develop 2017-10-28 03:50:32 -07:00
snipe
5278dac2b0 Eager loading assignedTo - I have no idea why this works 2017-10-28 03:50:02 -07:00
snipe
890012f6c4 Update references to assetloc to location 2017-10-28 02:58:38 -07:00
snipe
3991f79115 Use new location method for hardware view 2017-10-28 02:37:59 -07:00
snipe
0a114c7daf Log instead of echoing 2017-10-28 02:32:05 -07:00
snipe
3064b3f80e Updated availableForCheckout() method to be clearer 2017-10-28 02:31:54 -07:00
snipe
df430a2263 Removed assignedTo eager load for now - it’s not working 2017-10-28 02:31:36 -07:00
snipe
277e49468b Added deleted at to API 2017-10-28 02:31:13 -07:00
snipe
f687c8db24 Fix custom field seeder to drop old columns 2017-10-28 02:11:10 -07:00
snipe
c616041876 Use form selector 2017-10-28 01:51:10 -07:00
snipe
d76f858dcd Try eager loading assignedTo 2017-10-28 01:50:58 -07:00
snipe
7a543fa6d5 Use new location method on asset API 2017-10-28 01:49:13 -07:00
snipe
49afd325a9 Add more data from seeders to check for 1001 queries 2017-10-28 01:48:50 -07:00
snipe
ce5ccc31f0 Added location method, fixed assetLoc 2017-10-28 01:48:27 -07:00
snipe
c70db75de9 Migration to add location_id de-normed data 2017-10-28 01:47:59 -07:00
snipe
7b76bbfd68 Update seeders with more locations 2017-10-28 01:03:16 -07:00
snipe
cfd1925625 Nicer display of notifications on checkout 2017-10-28 01:00:26 -07:00
snipe
e8b4bdf6f4 Added location_id to assets table to denorm 2017-10-27 20:35:34 -07:00
snipe
7e0c33d535 Speed up user factory by only bcrypting once 2017-10-27 20:35:13 -07:00
tiagom62
24211cb674 Setup firewall rules on CentOS 7. (#4329) 2017-10-27 18:27:44 -07:00
snipe
2d758be0e1 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-27 18:01:46 -07:00
snipe
f49ecbdb61 Code cleanup 2017-10-27 18:01:42 -07:00
Daniel Meltzer
3cea12565b Add missing policies (#4330)
* Add Authorizable trait and interface to our user model so we have access to User::can/User::cant.  We should take a look at where else our user model has diverged from Larvel since it was created...

* Policy cleanup/fixes.

This commit adds policies for the missing backend/"settings" areas.  The
permissions were implemented a while back but the policies did not, so
authorizing actions was failing.

In addition, this condenses a lot of code in the policies into base
classes.  Most of the files were identical except for table names, so we
move all of the checks into a base class and override the table name in
each policy.

* Use a better name and permission for the check in the default layout.
2017-10-27 18:01:11 -07:00
snipe
b1ac024725 Refined upload code 2017-10-27 17:40:10 -07:00
snipe
ec68bd7842 Small refactoring for code quality 2017-10-27 17:38:11 -07:00
snipe
a224904ade Removed uncessary code
This is already handled in the env
2017-10-27 17:00:33 -07:00
snipe
cb3b294baa Clesned up status label model for code quality 2017-10-26 22:54:07 -07:00
snipe
0788347990 Cleaned up status label method 2017-10-26 22:49:57 -07:00
snipe
7496a902bd Removed unused getDataView controller method
This is all API based now
2017-10-26 22:39:41 -07:00
snipe
f2d04be8fe Remove unused methods in Settings API controller (for now) 2017-10-26 22:35:01 -07:00
snipe
f2499fc7d2 Removed else condition on custom fields API for code quality 2017-10-26 22:33:40 -07:00
snipe
00d910ddbc Added manufacturers and categories select lists 2017-10-26 22:09:08 -07:00
snipe
c6d191bcba Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-26 21:52:04 -07:00
snipe
f13836eb55 Use select2 partials 2017-10-26 21:52:00 -07:00
snipe
76c4c19b3e Fixed small issues with select2 partials 2017-10-26 21:51:53 -07:00
snipe
1d212b59bd Added model select2 partial 2017-10-26 21:51:27 -07:00
snipe
1174d37c20 Added model selectlist route 2017-10-26 21:51:09 -07:00
snipe
6bcb55a129 Slight tweak to model name presenter 2017-10-26 21:50:39 -07:00
snipe
a99e09e5e3 Removed extra with() calls, since we load those up via the select2 lists now 2017-10-26 21:50:27 -07:00
snipe
7d11cb0748 Added selectlist transformer for select2 API calls 2017-10-26 21:50:01 -07:00
snipe
9c29ee9c6d Fixed datepicker in asset checkout 2017-10-26 19:51:29 -07:00
snipe
bdb95e4e3d Added companies ajax select2 endpoint 2017-10-26 16:37:41 -07:00
snipe
1fa6228fb7 Add @techincolor as a contributor 2017-10-26 16:32:32 -07:00
Danielle
8b535c1806 Default DB_HOST 127.0.0.1 instead of localhost (#4324) 2017-10-26 16:30:48 -07:00
snipe
b71d0ab484 Fixed order number and warranty bug in importer 2017-10-26 16:13:35 -07:00
snipe
ea07517ad5 Added more ajax select2 boxes to checkouts, remove helper ->with() methods 2017-10-26 03:43:28 -07:00
snipe
82690e1fd7 Integrate ajax select2 menus in all asset checkouts 2017-10-26 02:28:17 -07:00
snipe
75b527ab59 Features/image uploads (#4320)
* Locations API support for image

* Added manufacturers API support for image

* Added manufacturers API support for image

* Added image support for locations add/update

* Added manufacturer image upload support to controller

* General image string

* Added blade support for image uploads/delete image

* Added $request support (from Input::)

* Added image support in API transformers

* Added image to Manufacturers presenter for data table

* Migration to create image fields

* Ignore the contents of the new image directories

* Create new image upload directories

* Created components/consumables uploads directory

* Fixed missing textSearch scope from companies

* Added ignore for companies uploads directory

* Added blade support for image upload

* Fixed path to upload directory on edit

* Added company image upport to transformers, controllers

* Added image support for categories

* Added support for images in Departments

* Added support for image in Consumables

* Added image support for components
2017-10-25 22:35:58 -07:00
snipe
b083541723 Fixed docblock copypasta 2017-10-25 20:23:59 -07:00
snipe
6dbb598616 Merge branch 'develop' 2017-10-25 20:18:24 -07:00
snipe
e8d938e188 Bumped hash 2017-10-25 20:18:06 -07:00
snipe
f7c92f61e1 Merge branch 'develop' 2017-10-25 20:16:16 -07:00
snipe
4f80eac467 Prod assets 2017-10-25 20:15:34 -07:00
snipe
0e5af78cf1 New route for menu state saving 2017-10-25 20:15:30 -07:00
snipe
0d34cc704a Added controller for state-saver for sidenav 2017-10-25 20:10:59 -07:00
snipe
d4bb4d2edd Added state-save for open/cloed sidenav 2017-10-25 20:10:41 -07:00
snipe
d008334f2d Fixed #2857 - better CSS for requestable assets page when no results 2017-10-25 20:07:10 -07:00
snipe
109ea82cb9 Merge branch 'develop' 2017-10-25 18:17:38 -07:00
snipe
e8847753f4 Fixed importer using previous row’s custom field 2017-10-25 18:16:28 -07:00
snipe
7dad71d2b6 Fixed smaller button for import/cancel 2017-10-25 18:14:22 -07:00
snipe
db5968f95a Fixed larger button size 2017-10-25 18:13:55 -07:00
snipe
d97a1edeb4 Moved style sheet call back into page content 2017-10-25 18:13:41 -07:00
snipe
3dd39a46be Fixed large font size on importer 2017-10-25 18:13:21 -07:00
snipe
dc9a908de7 Fixing laravel mix() fuckery, hopefully for the last time 2017-10-25 18:13:03 -07:00
snipe
687cf44d3d Use inline style for logo 2017-10-25 16:15:50 -07:00
snipe
e054504669 lol whoops 2017-10-25 16:08:29 -07:00
snipe
e1ad28aa20 Mix fuckery 2017-10-25 16:05:39 -07:00
snipe
87992b7f71 More mix() fuckery 2017-10-25 16:05:18 -07:00
snipe
32478f1a10 Fixed #4310 - logo not scaling correctly without text 2017-10-25 15:47:06 -07:00
snipe
abc722b9c0 Fix path to overrides 2017-10-25 15:43:57 -07:00
snipe
49ebf4a314 Removed old build dir 2017-10-25 15:04:37 -07:00
snipe
c503729a9a Removed build vue 2017-10-25 15:03:04 -07:00
snipe
0b7864b09c Fixed model number not saving in modal 2017-10-25 13:17:35 -07:00
snipe
ede16eec3c Fixed #4308 - checkin and delete from user page 2017-10-25 11:11:32 -07:00
snipe
21c1ca2336 Bumped hash 2017-10-24 19:26:21 -07:00
snipe
a305b1ea2d Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-24 19:24:39 -07:00
snipe
17d58d9cc5 Added snazzy rich user selection menu
TODO:
- Abstract this out so it can be used by other select2 menus
- Write a select2 transformer to standardize output
2017-10-24 19:24:35 -07:00
snipe
c605984db0 Added nicer table formatting for “assets checked out to this user” table 2017-10-24 19:21:53 -07:00
snipe
d678a0ebff Switched to using JS routes for better subdirectory support, removed debugging console logs 2017-10-24 19:20:00 -07:00
snipe
e0fe383815 Removed debug message in SaveUserRequest 2017-10-24 19:18:53 -07:00
snipe
7140efc561 Use the transformers, Luke 2017-10-24 19:18:20 -07:00
snipe
99be54fd96 Fixed success message on saving new user 2017-10-24 19:17:30 -07:00
snipe
35da7906cc Fix standardized exception formatter for correct payload 2017-10-24 17:57:49 -07:00
snipe
680ad676ca Remove debugging 2017-10-24 17:10:42 -07:00
snipe
4628c15813 Fixed typo in comments 2017-10-24 16:57:04 -07:00
snipe
8a9960f830 Fixed missing break that would override password requirement 2017-10-24 16:56:46 -07:00
snipe
2b45433255 Removed copypasta commented out seeder code 2017-10-24 16:55:59 -07:00
snipe
715da63581 Check that model name exists before trying to display it
I don’t know why this would ever trigger, but a user in Gitter complained earlier today, so… ¯\_(ツ)_/¯
2017-10-24 16:53:46 -07:00
Brady Wetherington
ad32bae62f Fix to bad relation definition in Location. (#4306) 2017-10-24 16:52:45 -07:00
snipe
bbda0dc3b4 Always return an avatar, and set a fun default 2017-10-24 13:02:30 -07:00
snipe
dc805dd9b1 Added user avatars to listing 2017-10-24 09:51:07 -07:00
snipe
167cd4e4a0 Merge branch 'develop' 2017-10-24 05:25:52 -07:00
snipe
8d68bb7a57 Sticky headers for bootstrap tables 2017-10-24 05:22:26 -07:00
snipe
7d64ab3158 Fixes #4294 - pass correct group ID for group user listings 2017-10-24 04:39:47 -07:00
snipe
a41d603e0f Merge branch 'develop' 2017-10-23 21:17:43 -07:00
snipe
c18220069d Bumped version 2017-10-23 21:17:23 -07:00
snipe
fbf516284c One more time… 2017-10-23 21:13:39 -07:00
snipe
3db25dca7a Downgrade doctrine for php5.6 2017-10-23 20:54:52 -07:00
snipe
47c7061af5 Merge branch 'develop' 2017-10-23 20:11:01 -07:00
snipe
3799ab87ed Fixed search query for default sorting 2017-10-23 20:10:37 -07:00
snipe
6232a1f941 Merge branch 'develop' 2017-10-23 19:51:29 -07:00
snipe
f5ce580593 Bumped version 2017-10-23 19:51:14 -07:00
snipe
3afa5f7cda Merge branch 'develop' 2017-10-23 19:50:09 -07:00
snipe
17b271918f Fix date picker for custom fields 2017-10-23 19:47:43 -07:00
snipe
3922569d7f Merge branch 'develop' 2017-10-23 19:14:13 -07:00
Nicolai Essig
3a302fe2d7 ref #2737 prevent assets with "rtd_location_id" null values to be removed on location sort (#4283) 2017-10-23 18:28:06 -07:00
snipe
249f86bb21 Merge branch 'develop' 2017-10-23 17:50:58 -07:00
snipe
0951a756cc Updated passport to 3.0
Re: https://stackoverflow.com/a/45029309/200021
via @robertpearce
2017-10-23 17:35:31 -07:00
snipe
fc644925ea Fixes #4291 - adds phone to user listing 2017-10-23 14:21:51 -07:00
snipe
59c0b63ad0 Merge branch 'develop' 2017-10-20 20:22:42 -07:00
snipe
c0f8b3773c Temp fix for markdown stuff 2017-10-20 20:22:14 -07:00
snipe
fd210c6439 Fixes #4267 - email notifications showing model name as number 2017-10-20 18:58:11 -07:00
snipe
f7e23cf7c8 Fixes #4272 - adds serial to assigned assets view 2017-10-20 18:51:14 -07:00
snipe
387351d7ed Merge branch 'master' of github.com:snipe/snipe-it 2017-10-20 17:44:35 -07:00
snipe
6438d30afc Merge branch 'develop' 2017-10-20 17:44:28 -07:00
snipe
b4e1d37b16 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-20 17:44:04 -07:00
Brady Wetherington
8ac57d0121 Need to prefix status_id with assets. for uniqueness (#4279) 2017-10-20 17:37:46 -07:00
Kasey
d3b51715dc adding --force to php artistan snipeit:legacy-recrypt (#4232) 2017-10-20 17:21:21 -07:00
snipe
b215924b1a Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-20 17:18:13 -07:00
Brady Wetherington
25d3d66880 Add performance-improving indexes (#4278) 2017-10-20 17:17:11 -07:00
Brady Wetherington
189574377a Add 'where' clause to hasManyThrough relationship. (#4276) 2017-10-20 16:58:39 -07:00
snipe
341ddec9d8 Adds built-in mail notification vendor templates 2017-10-20 16:52:12 -07:00
Nicolai Essig
abcce78944 use translation for "All" in sidebar menu (#4268) 2017-10-20 00:20:33 -07:00
snipe
22e13cd4d2 Allow sorting on asset counts, disable delete button if the user has items checked out to them 2017-10-19 17:15:21 -07:00
snipe
5c105b52b6 Merge branch 'develop' 2017-10-19 16:45:22 -07:00
snipe
f757da1a98 Bumped hash 2017-10-19 16:45:04 -07:00
snipe
c1f8db37d9 Added note about saving before testing LDAP 2017-10-19 16:35:59 -07:00
snipe
cfc215a013 Merge branch 'develop' 2017-10-19 16:28:26 -07:00
snipe
4215a3257b Fixes #1044 - adds suppliers and image to accessories (#4266)
* Ignore accesories uploads

* API: Allow searching accessories by supplier id

* Adds suppliers and image upload to accessories

* Allow sorting by counts for suppliers

* Validate supplier image uploads

* Remove purchase_date from protected accessory array, it was converting it to datetime in datepicker
2017-10-19 16:25:24 -07:00
snipe
1f247ff541 Don’t let the user checkout an asset to itself
(We should consolidate that AssetCheckoutRequest for the API)
2017-10-19 15:51:55 -07:00
snipe
2fc46746e2 Adds translation string placeholders for new LDAP functionality 2017-10-19 12:22:54 -07:00
snipe
e185dc68af Fixes #4240 - allows admins to use custom password reset URL 2017-10-19 12:22:27 -07:00
snipe
54000ff69f Allows sorting by number of assets, etc in category 2017-10-19 11:48:09 -07:00
snipe
f6a38e848a Allows sorting by whether or not the category requires acceptance 2017-10-19 11:44:42 -07:00
snipe
7e8d670f81 Disable delete buttons if there are assets, etc 2017-10-19 11:41:35 -07:00
snipe
287b150b7f Show disabled delete button if thing can’t be deleted 2017-10-19 11:29:58 -07:00
snipe
b379656d55 Adds more consistent visual display of status label types 2017-10-19 11:06:55 -07:00
snipe
2e11a983c8 Nicer card display of status type explanations 2017-10-19 10:52:30 -07:00
snipe
a9753eb646 Include asset count in status labels overview 2017-10-19 10:48:15 -07:00
snipe
707c4db881 API: Check there are no assets associated before allowing delete 2017-10-19 10:39:08 -07:00
snipe
d1de34394e Removed stupid count method 2017-10-19 10:37:30 -07:00
snipe
e5719441bc Merge branch 'develop' 2017-10-19 08:36:05 -07:00
snipe
7153013fb0 Fake sending the test email if the app is in demo mode 2017-10-19 08:33:46 -07:00
snipe
55500f77fb Merge branch 'develop' 2017-10-19 08:20:44 -07:00
snipe
2b826c3adc Merge branch 'features/mail_test_button' into develop 2017-10-19 08:19:24 -07:00
snipe
cd193ce8bb Fixes #4036 - adds test email button to general settings 2017-10-19 08:18:56 -07:00
snipe
cb50142ba3 Update @thakilla as a contributor 2017-10-19 06:16:03 -07:00
snipe
81aaed92ce Change default session name 2017-10-19 06:08:58 -07:00
Nicolai Essig
4bc551db82 ref #4042 scale barcode with label size (#4258) 2017-10-19 06:08:01 -07:00
snipe
471d665408 Add @thakilla as a contributor 2017-10-19 06:07:42 -07:00
Robin Temme
068308ef56 Change changepassword menu icon to fixed width (#4262) 2017-10-19 06:04:02 -07:00
Nicolai Essig
1e65c7bf9a load custom css also on login page (#4260) 2017-10-19 06:01:41 -07:00
snipe
ae567c08db Fixes incorrect language reference for consumables on checkout if consumable doesn’t exist 2017-10-19 03:44:01 -07:00
snipe
0d25a4868a Merge branch 'develop' 2017-10-19 03:37:52 -07:00
snipe
13586be6b0 Fixed load error if license is invalid 2017-10-19 03:37:27 -07:00
snipe
44c649c3c8 Fixes #4256 - double encoding on user bulk checkin and delete blade 2017-10-19 03:17:55 -07:00
snipe
1a7bfb75fa Merge branch 'develop' 2017-10-19 02:22:39 -07:00
snipe
2b803a6a6c Fixes #4257 - use admin url when editing groups 2017-10-19 02:22:05 -07:00
snipe
92e4bdc02e Merge branch 'develop' 2017-10-19 02:09:06 -07:00
snipe
4d3d19ca2b Fixes older route reference in consumables 2017-10-19 02:07:31 -07:00
snipe
9c06912efd Small tweaks to prevent Chrome autofill 2017-10-19 01:59:13 -07:00
snipe
c5893b4445 Fixes #4249 - display deployed location in listing 2017-10-19 01:30:40 -07:00
snipe
b90933bb8b Fixes #4139 - fixed route for deleting file 2017-10-19 01:11:24 -07:00
snipe
fe9a90854d Adds d.m.Y as date format, per #2423 2017-10-18 10:30:25 -07:00
snipe
3b012f2827 Some advanced search query tweaks 2017-10-18 10:07:35 -07:00
snipe
e31dadc99a Merge branch 'develop' 2017-10-18 09:28:00 -07:00
snipe
5519e2d4ae Fixes custom fields sorting on asset listings
I need a silkwood shower :(
2017-10-18 09:27:34 -07:00
snipe
6285a8b5b2 Merge branch 'develop' 2017-10-18 08:54:35 -07:00
snipe
a3139c6fc6 Fix accessories route for invalid accessory ID 2017-10-18 08:53:25 -07:00
snipe
389ba9059f Merge branch 'develop' 2017-10-18 08:45:22 -07:00
snipe
c0e50be03e Duh. Helps if you actually assign the array first. 2017-10-18 08:45:05 -07:00
snipe
447833c996 Only try to process model bulk editing if at least one model was selected 2017-10-18 08:15:54 -07:00
snipe
809e310565 Recrypt the LDAP password properly
Older installs should add a line to their .env:

`LEGACY_CIPHER=rijndael-256`
2017-10-18 08:15:23 -07:00
snipe
38dd03b5ad Merge branch 'develop' 2017-10-18 07:16:19 -07:00
snipe
68f6385eba Fixes 500 in bulk checkout if no asset is selected 2017-10-18 07:15:16 -07:00
snipe
bd376a4992 Possible fix for #4227 2017-10-18 07:02:18 -07:00
snipe
97f65ceac0 Merge branch 'develop' 2017-10-18 06:25:01 -07:00
snipe
9e39abcc32 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-18 06:24:41 -07:00
snipe
5cd2857d5d Use footer sumformatter for cost totals 2017-10-18 06:24:36 -07:00
snipe
585fcfb7d4 Use maintenances report API to populate the maintenances report 2017-10-18 05:47:47 -07:00
snipe
d9135a8aac Disallow deleting suppliers with associated assets, licenses or maintenances 2017-10-18 05:47:20 -07:00
snipe
081a64223a Add @TonisOrmisson as a contributor 2017-10-18 05:45:14 -07:00
Tõnis Ormisson
a4eeff01f0 FIXED upgrade Recrypt not working with changed cipher (#4245)
* FIX legacy cipher change

* FIX Recrypt Custom fields column names

* FIX ReCrypt Clean un-needed code
2017-10-18 05:43:54 -07:00
snipe
eedbe468ac Fix licenses not saving supplier id on edit 2017-10-18 05:43:15 -07:00
snipe
463d9513e2 Merge branch 'develop' 2017-10-18 04:49:16 -07:00
snipe
ed4aa7dec2 Account for deleted suppliers in asset maintenances report
This should all be reworked via the API though anyway
2017-10-18 04:48:52 -07:00
snipe
d6bbe15a76 Added upload state back to log test 2017-10-18 04:04:05 -07:00
snipe
ea0a0a4a69 Merge branch 'develop' 2017-10-18 02:35:43 -07:00
snipe
34442362ca Fixes bad route for new groups 2017-10-18 02:35:30 -07:00
snipe
b846bb20c6 Merge branch 'develop' 2017-10-18 01:23:01 -07:00
snipe
ab3f5f4f4d Bumped version 2017-10-18 01:21:50 -07:00
snipe
ea63ced2bd Fixes table alias bug in complex queries for Laravel 2017-10-18 01:21:08 -07:00
snipe
6bd49bfb72 Fixes #4191 - user search 2017-10-18 01:20:50 -07:00
snipe
b80d3ce50d Hopefully fixes #4218 2017-10-18 00:36:52 -07:00
snipe
c069829b33 Fixes #906 - groups view 2017-10-17 21:43:57 -07:00
Geoff Young
665a113ed8 Update account history query (#4237)
This will limit the action_log records displayed when a user is viewing
their own assets and history since both target_type and target_id must
be set for a where condition to be added to the history query.
2017-10-17 20:39:49 -07:00
snipe
aa7091d962 Fixes #4226 - adds log_max_files to app config and sample env 2017-10-17 20:04:07 -07:00
snipe
ceff6a9617 Merge branch 'develop' 2017-10-17 19:27:31 -07:00
snipe
c01850fc73 Corrected Italian to Irish in language selector 2017-10-17 19:19:46 -07:00
snipe
4c04dc7b84 Merge branch 'develop' 2017-10-17 18:55:49 -07:00
snipe
c0103cd878 Bumped version 2017-10-17 18:55:30 -07:00
snipe
1f1d9d5461 Merge branch 'develop' 2017-10-17 18:53:21 -07:00
snipe
c776fa4f7b Updated language strings 2017-10-17 18:52:20 -07:00
snipe
dc91d10395 More possible fixes for #4210 2017-10-17 17:35:48 -07:00
snipe
4df10ad26c Merge branch 'develop' 2017-10-17 17:19:15 -07:00
snipe
668a88bc86 Add autocomplete=off to settings forms for #4210 2017-10-17 17:18:17 -07:00
snipe
d6e0012818 Merge branch 'develop' 2017-10-17 17:05:38 -07:00
snipe
1d5fb52bfc Fix for LDAP where location ou is not null but blank 2017-10-17 16:59:50 -07:00
snipe
8efb02a0ee Merge branch 'develop' 2017-10-17 16:23:59 -07:00
snipe
dea42db18b Reversed order of withTrashed for deleted asset QR codes 2017-10-17 16:21:50 -07:00
snipe
78d5d3947f Merge branch 'develop' 2017-10-17 16:18:14 -07:00
snipe
a1f5e11517 Fix for broken QR code on deleted assets 2017-10-17 16:01:53 -07:00
snipe
99ad096a8a Added a space next to crowdin badge 2017-10-17 15:26:16 -07:00
snipe
65b4ffeed9 Higher res crowdin badge 2017-10-17 15:25:34 -07:00
snipe
0dac88816a Merge branch 'develop' of github.com:snipe/snipe-it into develop
# Conflicts:
#	README.md
2017-10-17 15:20:53 -07:00
snipe
f7626404b7 Add @BlueHatbRit as a contributor 2017-10-17 15:18:27 -07:00
Elliot Blackburn
78f4b08398 Change zenhub markdown shield to higher res (#4235)
The zenhub badge was fuzzy as it was a low resolution for retina display. It's now using a new shield which falls in line with some of the others (img.shield.io).
2017-10-17 15:16:49 -07:00
snipe
160fd1c86a Added setting to let admin decide whether footer text should link back to site 2017-10-17 13:54:03 -07:00
snipe
6ce01487c5 Merge branch 'develop' 2017-10-17 13:31:43 -07:00
snipe
b46cbac911 Fixes #4230 - adds model name and manufacturer to emails 2017-10-17 13:30:32 -07:00
snipe
e9c3d6bfb7 Full text search fixes - addresses laravel bug :( 2017-10-17 12:48:18 -07:00
snipe
9e9a5b7a53 Changed checkin/checkout buttons to different colors for easier visibility 2017-10-17 11:32:09 -07:00
snipe
e7fe91c9d4 Depreciation view 2017-10-17 11:20:05 -07:00
snipe
6d130326aa Merge branch 'develop' 2017-10-16 21:28:23 -07:00
snipe
02db0f9f9d Handle deleted assets in maintenance 2017-10-16 21:28:05 -07:00
snipe
e0668b7507 Handle references to suppliers that have been deleted 2017-10-16 21:19:06 -07:00
snipe
1376f246dc Merge branch 'develop' 2017-10-16 20:38:19 -07:00
snipe
113c55d905 Hopefully fixes #4150 2017-10-16 20:34:31 -07:00
snipe
9cc25bcfd0 Hopefully fixes #4150 2017-10-16 20:34:22 -07:00
snipe
81d3f78263 Production assets 2017-10-16 20:12:42 -07:00
snipe
66596b0b09 Production assets 2017-10-16 20:12:22 -07:00
snipe
7455b1019a Hacky possible fix for subdir issues 2017-10-16 20:12:11 -07:00
snipe
37df934e97 Fixes #4220 - allow nullable for completion date 2017-10-16 18:32:48 -07:00
snipe
6517a95d45 Check if there are any custom fields before trying to loop through them 2017-10-16 15:29:19 -07:00
snipe
4b84a0c916 Tidying some of the LDAP UDN logic 2017-10-16 15:29:06 -07:00
snipe
0f0a61e477 Merge branch 'develop' 2017-10-16 10:10:29 -07:00
snipe
f64382aa00 Nicer error display in LDAP tests 2017-10-16 10:10:11 -07:00
snipe
1743417ec9 Merge branch 'develop' 2017-10-16 09:38:26 -07:00
snipe
c61bed52c8 Removed danger class 2017-10-16 09:38:09 -07:00
snipe
edf75865dc Merge branch 'develop' 2017-10-16 09:27:01 -07:00
snipe
176a26e6c3 Bumped version 2017-10-16 09:26:44 -07:00
snipe
10a4d7e849 Merge branch 'develop' 2017-10-16 09:09:53 -07:00
snipe
cbe008d52f Fix assetLoc to assetloc because reasons? 2017-10-16 09:08:08 -07:00
snipe
aeb5152789 Removed extranneous class for danger text 2017-10-16 09:04:38 -07:00
snipe
938490df16 Merge branch 'develop' 2017-10-16 09:01:09 -07:00
snipe
45c2af80a3 More LDAP testing US refinements 2017-10-16 09:00:51 -07:00
snipe
892c1b04fd Merge branch 'develop' 2017-10-16 07:07:35 -07:00
snipe
1fbf3753bc More small LDAP test improvements 2017-10-16 07:07:21 -07:00
snipe
5ead5a94e3 Merge branch 'develop' 2017-10-16 06:47:01 -07:00
snipe
bcf435f625 Try for better error reporting on LDAP fail
Sorry for all the commits on this - my local LDAP isn’t working and I can’t figure out why, so no easy way to test locally
2017-10-16 06:46:33 -07:00
snipe
8638c46b1d Merge branch 'develop' 2017-10-16 06:40:15 -07:00
snipe
b107280b7b Slightly nicer UI for LDAP login test 2017-10-16 06:39:36 -07:00
snipe
1a60c20117 Merge branch 'develop' 2017-10-16 06:34:23 -07:00
snipe
f1a6926ad9 LDAP test login 2017-10-16 06:34:04 -07:00
snipe
c27a7f09bd Merge branch 'develop' 2017-10-16 05:55:27 -07:00
snipe
ba7b9d8168 Removed stray foo 2017-10-16 05:54:33 -07:00
snipe
5b070ee32f Merge branch 'develop' 2017-10-16 05:52:52 -07:00
snipe
59a126c47c Small tweaks to LDAP test 2017-10-16 05:52:18 -07:00
snipe
322e62418e Merge branch 'develop' 2017-10-16 05:23:26 -07:00
snipe
a98d94ccdc Pass token to LDAPtest 2017-10-16 05:22:37 -07:00
snipe
5addcb517f Merge branch 'develop' 2017-10-16 05:01:37 -07:00
snipe
56cbc005ae Fixes expected checkins name in console kernel 2017-10-16 05:01:15 -07:00
Daniel Meltzer
22e9246031 Fix more old routes. Should fix #4216 (#4217) 2017-10-15 23:32:40 -07:00
snipe
c0b39701cc Fixes #4170 - asset maintenance type not showing 2017-10-14 16:17:14 -07:00
madd15
e2bac62e36 Fix #4205 (#4213)
* Fixing various UI items

* Revert css change

* Dashboard icon CSS up 4px
2017-10-14 00:14:22 -07:00
snipe
c12a23b84a Merge branch 'develop' 2017-10-11 15:37:01 -07:00
snipe
fa95f6d836 Another attempt for #4165 2017-10-11 15:36:47 -07:00
snipe
280e8c7ed1 Revert "Another attempt for #4165"
This reverts commit 7617fda978.
2017-10-11 15:34:30 -07:00
snipe
7617fda978 Another attempt for #4165
(This is terrible and needs to be refactored.)
2017-10-11 15:33:53 -07:00
snipe
af69f7636b Merge branch 'develop' 2017-10-11 14:44:37 -07:00
snipe
6d4574130f Clearer indication of whether or not the user will be emailed a eula 2017-10-11 14:44:25 -07:00
snipe
485b6397d0 Possible (crummy temp) fix for #4165 2017-10-11 14:42:11 -07:00
snipe
93990327de Hopefully fixes #4163 2017-10-11 14:18:08 -07:00
madd15
4ee7765403 Change Save buttons to Checkout and add Cancel (#4202)
Bringing components and consumables checkout page inline with other
checkout pages
2017-10-11 13:29:22 -07:00
snipe
bdbad067b4 Merge branch 'develop' 2017-10-11 13:10:46 -07:00
snipe
13a716310c Bumped hash 2017-10-11 13:10:29 -07:00
snipe
d0c77c228b Merge branch 'develop' 2017-10-11 13:09:30 -07:00
snipe
36cbffa183 Fixes bug where custom fields would not store new name in custom fields table on edit 2017-10-11 13:09:10 -07:00
snipe
5edf9e143f Merge branch 'develop' 2017-10-11 12:46:42 -07:00
snipe
b6a1e0d12f Call migrate before passport install 2017-10-11 12:42:31 -07:00
snipe
2fda3a2d26 Merge branch 'develop' 2017-10-11 12:29:48 -07:00
snipe
f56eb16941 More specific order by clause for drilling down on order number 2017-10-11 12:29:08 -07:00
snipe
c15c082fb4 Merge branch 'develop' 2017-10-11 01:31:59 -07:00
snipe
64e7ab3a12 Fixes #4182 - empty names for assets when checking out to asset 2017-10-11 01:31:37 -07:00
snipe
afbd6c811b Merge branch 'develop' 2017-10-10 23:38:45 -07:00
snipe
f64c02ce12 Fix for ambiguous query on models 2017-10-10 23:12:16 -07:00
snipe
a0d8aa77d3 Merge branch 'develop' 2017-10-10 22:59:50 -07:00
snipe
bed7b29417 Fixes group search 2017-10-10 22:59:32 -07:00
tiagom62
40ed86bfe0 Sudo isnt available on every distro. (#4194) 2017-10-10 22:02:47 -07:00
snipe
33497c9811 Merge branch 'develop' 2017-10-10 12:48:05 -07:00
snipe
52a8597813 Fixes #4136 2017-10-10 12:44:28 -07:00
snipe
eeb07f73e5 Added Redis vairables to example env 2017-10-09 15:44:08 -07:00
Alex Markessinis
57422c9135 Added Redis support. (#4146)
* Fix forgotten password missing route (???)

* Fixes #4056 - check for assets before deleting user

* added predis/predis dependency to composer.json to support redis based cache/queue/session/broadcast
2017-10-09 15:39:20 -07:00
Richard Hofman
adca7cb0c5 Fix LDAP location sync issue in #3993 (#4181)
* Ensure locations with the most specific OUs take precedence during user assignment.

* Save 'ldap_ou' Location attribute during creation.
2017-10-09 13:06:47 -07:00
Richard Schwab
059f8f5bc9 Remove dead macro code (#4164)
The barcode_types macro existed twice in the code, the second occurrence overriding the first one.
This commit removes the first occurrence which is essentially dead code.
2017-10-09 13:06:05 -07:00
tiagom62
c676e9d794 Fix progress spinner. (#4178) 2017-10-09 13:04:48 -07:00
Daniel Meltzer
e9f10dd74e Maybe Fix #4052. Missed an absolute URL. (#4187) 2017-10-09 13:04:38 -07:00
snipe
e29d878d4f Remove unused method arguments 2017-10-07 15:09:50 -07:00
snipe
d4e3ea1412 Derp 2017-10-07 15:07:31 -07:00
snipe
c5462c5f1f Not sure why this isn’t working… commenting it out for now 2017-10-07 14:52:00 -07:00
snipe
d1328c3ba9 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-07 14:49:54 -07:00
snipe
8c406e8e55 Additional auth policies 2017-10-07 14:49:47 -07:00
snipe
6e33f36595 Set snipe-logo as default 2017-10-07 14:49:36 -07:00
tiagom62
48277606de Support Debian 8 installs. Shellchecked. More cleanup. (#4174) 2017-10-07 12:45:25 -07:00
snipe
d7c9fcc8df Small manufacturer display tweaks on license view to make text clearer, link phone 2017-10-07 08:27:56 -07:00
snipe
508576544b Merge branch 'develop' 2017-10-07 08:17:58 -07:00
snipe
9f2fc21649 I guess we don’t need to manually create the license seats 2017-10-07 08:17:38 -07:00
snipe
11f99a963a Removed extra comma in demo warning 2017-10-07 08:03:08 -07:00
snipe
27ab0271a1 Merge branch 'develop' 2017-10-07 08:00:11 -07:00
snipe
f858b2858d Add language about the demo resetting daily to en files 2017-10-07 07:59:57 -07:00
snipe
e2809f7bd0 Merge branch 'develop' 2017-10-07 07:45:58 -07:00
snipe
f468b37f36 Bumped hash 2017-10-07 07:45:43 -07:00
snipe
0b3d3de30f Merge branch 'develop' 2017-10-07 07:44:30 -07:00
snipe
907b909223 Fixed language on settings page 2017-10-07 07:44:08 -07:00
snipe
9dc79f7165 Demo Settings reset artisan command
We’ll set this as a cron job to reset the language back to english
2017-10-07 07:43:57 -07:00
snipe
324d44dbac Small accessory factory tweaks 2017-10-07 07:24:15 -07:00
snipe
7a0e695ea0 Merge branch 'develop' 2017-10-07 07:17:05 -07:00
snipe
a69a939034 Small barcode tweaks 2017-10-07 07:15:28 -07:00
snipe
ed8efbe759 Add dateFormatter to components view 2017-10-07 06:57:02 -07:00
snipe
834c6ad8f9 Removed extra space 2017-10-07 06:56:47 -07:00
snipe
2ce48fbc7e Use components transformer in API method 2017-10-07 06:56:39 -07:00
snipe
5d18937e94 Standardized component API output 2017-10-07 06:56:18 -07:00
snipe
b3186ba5ea Removed old getDataTable methods
These are no longer used because of the API
2017-10-07 06:56:02 -07:00
snipe
f51dc9a1c4 Use recent date range for factory 2017-10-07 06:17:39 -07:00
snipe
f3f9920bd3 Added Crucial as a manufacturer in factory 2017-10-07 06:17:18 -07:00
snipe
ea6140e786 Components factory and seeder 2017-10-07 06:16:53 -07:00
snipe
05c4d6dead Make HDD into HDD/SSD for factory 2017-10-07 06:16:36 -07:00
snipe
e2c6f36c70 Use recent date range for factory 2017-10-07 06:16:21 -07:00
snipe
21b1ecb6b3 Only checkout RTD assets 2017-10-07 06:16:06 -07:00
snipe
a62cf358ee More realistic prices for factories 2017-10-07 05:52:44 -07:00
snipe
20c429b600 Add some archived and pending assets too 2017-10-07 05:52:26 -07:00
snipe
38e25a388c lol whoops 2017-10-07 05:34:13 -07:00
snipe
2c6cedd62c Removed unused factories 2017-10-07 05:32:59 -07:00
snipe
9b0cca4a37 Added licensed to name, email to licenses factory 2017-10-07 05:32:39 -07:00
snipe
f6d198a39c Added back in some unused asset stuff for tests 2017-10-07 05:32:22 -07:00
snipe
d12fc6b13c Add depreciation ID to model factories 2017-10-07 05:32:00 -07:00
snipe
a12c7c83d4 Req acceptance on laptop category 2017-10-07 05:31:46 -07:00
snipe
affd4035c3 Added displays to seeders 2017-10-07 05:31:32 -07:00
snipe
ecfe1a5442 Depreciations factory 2017-10-07 05:30:53 -07:00
snipe
e9c77198d7 Fix duplicate call to licenses seeder 2017-10-07 04:54:16 -07:00
snipe
20be670648 Fixed ids for license 2017-10-07 04:43:55 -07:00
snipe
a03207e5b4 Show license notes 2017-10-07 04:42:53 -07:00
snipe
6555b3a49d No location id in licenses? 2017-10-07 04:36:23 -07:00
snipe
61a634bc1a No idea why this worked locally before, but… 2017-10-07 04:34:51 -07:00
snipe
00696a3668 Commented out slow users factories right now - will revert when finished 2017-10-07 04:26:37 -07:00
snipe
e20271791b Removed unused factories for now
Need to add these back, once the correct logic is applied
2017-10-07 04:26:21 -07:00
snipe
86b9fdbffe License and actionlog seeder tweaks 2017-10-07 04:25:35 -07:00
snipe
7e728094a1 User seeders 2017-10-07 03:41:46 -07:00
snipe
f865c621ef Removed commented code 2017-10-07 03:41:40 -07:00
snipe
e2f4685a55 Added notes back to list view 2017-10-07 03:36:50 -07:00
snipe
2148ea94bb Additional model seeders 2017-10-07 03:19:46 -07:00
snipe
1d2787250b Reference IDs 2017-10-07 03:19:27 -07:00
snipe
7f02ff12cf Asset seeder 2017-10-07 03:19:17 -07:00
snipe
65341a5d8a Added ID reference numbers 2017-10-07 03:19:07 -07:00
snipe
1608edbb28 Added ID reference numbers 2017-10-07 03:18:57 -07:00
snipe
776da1dea4 Add Dept seeder to db seeder 2017-10-07 03:18:46 -07:00
snipe
a6b3e4bbb1 Department seeder/factory 2017-10-07 03:18:36 -07:00
snipe
3b2ecda243 Consumables factory/seeder 2017-10-07 02:46:04 -07:00
snipe
57cbb5c5ce Removed unused factories 2017-10-07 02:45:54 -07:00
snipe
7b27d32121 Added ID numbers 2017-10-07 02:45:36 -07:00
snipe
ccfba324ee Indenting 2017-10-07 02:45:28 -07:00
snipe
9f55a76fcf Added avery 2017-10-07 02:45:21 -07:00
snipe
8fdddc310f More rigid seeders for more realistic data 2017-10-07 02:27:02 -07:00
snipe
064a4ebe33 Ability to skip deleting/generating new users
This will behave unpredictably if there is not a user id 1
2017-10-07 00:02:37 -07:00
snipe
4981077cb1 Merge branch 'develop' 2017-10-06 22:59:01 -07:00
tiagom62
fbea1c0823 Code dedupe, general cleanup and added a verbose option for debugging. (#4173) 2017-10-06 22:58:41 -07:00
snipe
a84da88114 Demo seeder 2017-10-06 22:58:00 -07:00
snipe
282b3b5b0a Remove catch-all “deployed” from pie chart 2017-10-06 18:41:10 -07:00
snipe
130a99c46f Merge branch 'develop' 2017-10-06 18:15:31 -07:00
snipe
df4cb7d351 Don’t reload the page if the API returns a 500 2017-10-06 18:15:13 -07:00
snipe
1dcff8d463 Remove eager loading on pie
This was causing memory issues for large asset sets
2017-10-06 18:15:01 -07:00
snipe
ae4ba6176d Merge branch 'develop' 2017-10-06 17:04:06 -07:00
snipe
e461c25428 Apply model image fix to update method 2017-10-06 17:03:51 -07:00
snipe
ea4bfdc51d Merge branch 'develop' 2017-10-06 16:57:51 -07:00
snipe
554ea8bb95 Fixed asset model image validation 2017-10-06 16:56:43 -07:00
Richard Hofman
f2be409914 LDAP sync improvements and DB query fix. (#4148)
* Set 'ldap_ou' Location field to NULL when an empty string is submitted.

* Consolidate LDAP user import logic in LdapSync.php.
2017-10-06 16:15:14 -07:00
snipe
3fc1bbea73 Apply PR changes to master. Again? 2017-10-06 14:28:19 -07:00
snipe
a97f7d2277 Merge branch 'develop' 2017-10-06 14:27:46 -07:00
snipe
af70cdaeac Adds email address to CoC 2017-10-06 13:44:39 -07:00
snipe
8919c3b52a Merge branch 'develop'
# Conflicts:
#	snipeit.sh
2017-10-05 23:14:13 -07:00
snipe
f580e20bc3 Fixed custom fields filter for advanced search 2017-10-05 23:09:02 -07:00
snipe
a054cec7c9 Supress output if no title is given
This should never happen, but….
2017-10-05 22:51:33 -07:00
tiagom62
1ad7bbdd0c Support configuring smtp settings (#4161)
* Able to configure smtp settings. General cleanup.

* Check if sudo is available.
2017-10-05 21:27:00 -07:00
snipe
cfe6759825 Merge branch 'develop' 2017-10-05 00:41:30 -07:00
snipe
10c13baf2b Add @GeoffYoung as a contributor 2017-10-05 00:40:32 -07:00
snipe
9fe4e11874 Merge branch 'develop'
# Conflicts:
#	snipeit.sh
2017-10-05 00:37:16 -07:00
snipe
f6d8642799 Fix $search variable to $search_var for new filter 2017-10-05 00:35:37 -07:00
snipe
adddc5324b Add @imjennyli as a contributor 2017-10-05 00:34:50 -07:00
snipe
f442b70ae7 Apply PR #4133 to develop 2017-10-05 00:02:14 -07:00
madd15
7b10213b3a Small UI Tweaks to Accessories (#4149)
* Small UI Change

Changing Save button for Checkout button and adding Cancel button

* Small UI Change

Move buttons to match checkout page and remove extra save button
2017-10-04 23:28:13 -07:00
tiagom62
ddc79b9070 Support Ubuntu and Debian installs. (#4133)
* Ubuntu related fixes.

* Further cleanup.

* Support Debian 9 install. More cleanup.
2017-10-04 23:19:38 -07:00
Jenny Li
5e9b04b0f5 update broken link to contributor docs (#4123) 2017-10-04 23:19:19 -07:00
snipe
2562eb2aeb Merge branch 'develop' 2017-10-03 21:23:46 -07:00
snipe
85da30894b Bumped version 2017-10-03 21:23:27 -07:00
snipe
bf344e9322 Merge branch 'develop' 2017-10-03 21:03:19 -07:00
snipe
f66e222f3d Fixes #4132 - associated accessory users 2017-10-03 21:03:00 -07:00
snipe
fb180d74a1 Merge branch 'develop' 2017-10-03 18:12:47 -07:00
snipe
eaf55f5e79 Hide table toolbar id models are deleted 2017-10-03 18:12:30 -07:00
snipe
8edb586576 Merge branch 'develop' 2017-10-03 18:06:59 -07:00
snipe
32b01b8f38 Toggle button deleted/not for models 2017-10-03 18:06:41 -07:00
snipe
340386f282 Merge branch 'develop' 2017-10-03 17:55:21 -07:00
snipe
6dd4282f1f Fixes #4130 - show deleted asset models 2017-10-03 17:53:08 -07:00
snipe
3f44987799 Small logo size tweaks 2017-10-03 14:15:03 -07:00
snipe
b4fec068d0 Use asset url for favicon on login blade 2017-10-03 13:44:50 -07:00
snipe
512632ce60 Make the export button contextual for requested status 2017-10-03 12:50:18 -07:00
snipe
58a9b0d3e8 Normalized route name 2017-10-03 12:50:01 -07:00
snipe
fcb1283a14 Added missing deployed page title 2017-10-03 12:49:53 -07:00
snipe
54671af7f0 Small export assets fix 2017-10-03 11:49:41 -07:00
snipe
aedabb0920 Merge branch 'develop' 2017-10-03 11:29:06 -07:00
snipe
aaf4acef83 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-03 11:28:45 -07:00
snipe
8e73cacf4e Fixes custom report to include assigned to names, etc 2017-10-03 10:38:28 -07:00
Daniel Meltzer
b4b2daebbd Fix Importer tests. (#4122) 2017-10-03 09:14:04 -07:00
snipe
3e2c18cb4d Merge branch 'develop' 2017-10-03 08:47:57 -07:00
snipe
c721fdd793 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-03 08:46:25 -07:00
Daniel Meltzer
d119372ff0 Fix License Import. (#4121)
The license name is not unique, so keying by license alone was causing issues.  Match using name + serial instead.
2017-10-03 08:46:06 -07:00
snipe
c8bed867da Export PDF as landscape 2017-10-03 07:32:18 -07:00
snipe
3a470ce789 Only report exceptions we want to see 2017-10-03 07:28:00 -07:00
snipe
f9105719a8 Merge branch 'develop' 2017-10-03 05:16:11 -07:00
snipe
d303cbd7cb Bumped version 2017-10-03 05:15:52 -07:00
snipe
96c09e7264 Merge branch 'develop' 2017-10-03 02:05:47 -07:00
snipe
ef19bc7c16 Workaround to fix #4117
This shouldn’t be necessary, but it looks like some folks missed the migration that drops the FK for company_id
2017-10-03 02:05:22 -07:00
snipe
aa8472971d Merge branch 'develop' 2017-10-02 21:58:17 -07:00
snipe
78257d5ca6 Merge branch 'develop' 2017-10-02 21:55:25 -07:00
tiagom62
655484b1da General cleanup and get ubuntu install working. (#4118) 2017-10-02 21:37:04 -07:00
snipe
ac8185339d Removed linebreaks 2017-10-02 20:32:42 -07:00
snipe
a021ce011b Correct argv 2017-10-02 20:31:43 -07:00
snipe
3687cbfdb3 Added rollbar support 2017-10-02 20:27:45 -07:00
snipe
594135fd70 Upgrade script improvements 2017-10-02 20:11:07 -07:00
snipe
2542b9cf59 Merge branch 'develop' 2017-10-02 17:21:36 -07:00
snipe
8a7abba427 Slash url 2017-10-02 17:21:18 -07:00
snipe
3775649c8a Merge branch 'develop' 2017-10-02 17:19:59 -07:00
snipe
9df648b428 Fix Session path 2017-10-02 17:19:22 -07:00
snipe
11a1efdbbc Merge branch 'develop' 2017-10-02 16:56:12 -07:00
snipe
a65c2f305e Set polict to same-origin in example env 2017-10-02 16:47:19 -07:00
snipe
50777f5c1d Merge branch 'develop' 2017-10-02 16:39:11 -07:00
snipe
ff38cdd09b Comment out the nullable middleware until we have a chance to check all model validators 2017-10-02 16:36:43 -07:00
snipe
9420913d25 Set referrer policy to same origin - should fix a lot of weird validation redirects 2017-10-02 16:35:57 -07:00
snipe
59225701b5 Redirect to previous after login 2017-10-02 16:00:42 -07:00
snipe
6aa5d4d58c Merge branch 'develop' 2017-10-02 15:27:33 -07:00
snipe
cd2e137fcc Bumped version 2017-10-02 15:27:18 -07:00
snipe
0b968e1d6b Merge branch 'develop' 2017-10-02 13:31:08 -07:00
snipe
7a27fda083 Update example env with CSP default 2017-10-02 13:29:42 -07:00
snipe
aab635154a Default to turning CSP off until we can fix vue/CSP issues 2017-10-02 13:29:14 -07:00
snipe
e389f67629 Merge branch 'develop' 2017-10-02 13:09:17 -07:00
Geoff Young
169dc779fd Fix box title in Label settings view (#4111) 2017-10-02 13:00:32 -07:00
tiagom62
26423c5c02 Fix various installer issues. (#4096)
* Various installer fixes.

* CentOS 6 related fixes.
2017-10-02 12:59:53 -07:00
snipe
862543428b Fix named route for license checkin 2017-10-02 12:58:17 -07:00
snipe
d28e882f5d Fixed form close tag 2017-10-02 12:57:01 -07:00
snipe
e26d038589 Fixed license checkin form UI 2017-10-01 15:08:32 -07:00
snipe
afc8133acf URL nullable validation isn’t working in Laravel 5.4? 2017-10-01 14:40:13 -07:00
snipe
abe19a6311 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-01 14:33:02 -07:00
snipe
07359c76ad Fixes #4094 - more nullables 2017-10-01 14:32:47 -07:00
Daniel Meltzer
a2453be573 Pass urls to passport vue components to make work in subdirectories. (#4090) 2017-10-01 13:59:07 -07:00
Daniel Meltzer
c2616412c0 Add laravel routes to javascript (#4092)
* Add laravel routes to javascript

This will clean up a lot of passing of urls.  Adds a route() helper and
everything...

This commit also moves the customfield fetching to only be fetched once
and shared with each file, rather than once for each file.

* Try to fix travis unit test things.

* Downgrade doctrine/inflector for php5

* Attempt to occasional seeder issues on travis if the asset does not generate validatable data.

* Update sql dump for functional tests.

* Try to fix api tests.
2017-10-01 12:59:55 -07:00
Daniel Meltzer
23ca2d9a0b Minor fixes (#4091)
* Fix old urls.

Still had some /admin/ urls floating around, which was causing bad
redirects in some instances.  Should fix #4085

* The modal seems to be confusing license upload. be more explicit in the route we redirect to.

* Fix #4039.  Use proper methods for location assets.

This also fixes a bunch of n+1 issues in the transformer.  Also: curious
to know what Location::assets() does, because it doesn't do what I want
it to :)
2017-10-01 12:57:04 -07:00
snipe
0bb186ad3b Prod manifest 2017-09-29 19:43:33 -07:00
snipe
b7abd8328a Prod asset build 2017-09-29 19:43:18 -07:00
snipe
ad2821b4ab Merge branch 'develop' 2017-09-29 17:57:48 -07:00
snipe
7d5b08446a Missing colon in status explanations 2017-09-29 17:52:43 -07:00
snipe
ba793355cb Nullable crap. Again. 2017-09-29 17:50:52 -07:00
Daniel Meltzer
655ca78633 Adjust the import controller to return a url to the process path to enable processing an uploaded file without refresh. (#4080) 2017-09-29 17:49:35 -07:00
snipe
f00dcb57cf Merge branch 'develop' 2017-09-29 17:39:09 -07:00
snipe
1e52fb546c Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-29 17:32:25 -07:00
snipe
24cad588bd Added preceding icons in polymorphic formatter 2017-09-29 17:32:16 -07:00
Daniel Meltzer
bbffde47f7 Remove conditional related to no longer existant testrun option. (#4079) 2017-09-29 17:31:39 -07:00
snipe
be7598b279 Fixes all assets showing as unassigned in license checkout 2017-09-29 17:14:38 -07:00
snipe
1a7e3fbb4b Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-29 17:05:32 -07:00
Daniel Meltzer
e8a0c44e93 Update some composer dependencies to try to fix everything breaking in subdirectories. Attempts to fix #4052, it seems to help here. (#4078) 2017-09-29 17:05:20 -07:00
snipe
fe644c76c5 Better explanations of how status label metas work 2017-09-29 17:03:21 -07:00
snipe
9301c56a77 Fixes #4069 - include request handler 2017-09-29 16:17:35 -07:00
snipe
e37503c734 Fixes #4075 - this is janky and should be revisited 2017-09-29 16:09:24 -07:00
snipe
6bc3f194ec Merge branch 'develop' 2017-09-29 15:59:40 -07:00
snipe
878bde67ac Few more issue template tweaks 2017-09-29 15:59:04 -07:00
snipe
314d6fa4c5 Expanded issue template 2017-09-29 15:55:58 -07:00
snipe
43ff66e4d9 More UI tweaking for meta statuses 2017-09-29 15:24:33 -07:00
snipe
2ea91266c0 Init lightbox 2017-09-29 14:26:00 -07:00
snipe
a0bd9bce39 Commented out security headers - might req apache module 2017-09-29 14:00:05 -07:00
snipe
5223ec1dbb Clearer status listing in the sidenav 2017-09-29 12:13:15 -07:00
snipe
4eda2a2f96 *ahem* 2017-09-29 12:03:12 -07:00
snipe
fe1975067a Hopefully clearer status label meta info 2017-09-29 12:03:02 -07:00
snipe
b9e79c27a8 Added nonce to basic blade 2017-09-29 05:49:26 -07:00
snipe
b8ed6a53b6 For #3998 - Added nonce to all.js 2017-09-29 04:53:24 -07:00
snipe
efd71f8bfe For #3998 - Disable CSP if debug=true
To avoid all the nonce hell from debugbar
2017-09-29 04:53:09 -07:00
snipe
05a8ba9a8e Fix weird url if license checkout fails 2017-09-29 04:37:33 -07:00
snipe
9ce2d1f560 Merge branch 'csp-middleware' into develop 2017-09-29 04:14:33 -07:00
snipe
5a1225a8bf Merge branch 'develop' 2017-09-29 03:48:37 -07:00
snipe
c6069b905b Bumped version 2017-09-29 03:47:50 -07:00
snipe
2e76620cf8 More specific required check 2017-09-29 03:44:23 -07:00
snipe
a4b30279ee Fixes #4067 - Make unrequired custom fields nullable 2017-09-29 03:30:13 -07:00
snipe
db59d4b2c4 Merge branch 'develop' 2017-09-29 02:08:06 -07:00
snipe
faf3802971 Fixes #4011 - do not send email to user on license checkout 2017-09-29 02:00:49 -07:00
snipe
7fe2a1f802 Fixes #4051 - use delete method for deleting user files 2017-09-29 01:48:16 -07:00
snipe
3d57d1bd1d Merge branch 'develop' 2017-09-29 01:30:30 -07:00
snipe
ba8bcce8eb Hopefully fixes #4020 2017-09-29 01:21:08 -07:00
snipe
8247a73182 Merge branch 'develop' 2017-09-28 22:22:37 -07:00
snipe
aab409dec2 Fixes #4061 - bulk checkout error 2017-09-28 22:22:21 -07:00
snipe
70a9b7bf05 Merge branch 'develop' 2017-09-28 21:21:01 -07:00
snipe
d9824a0454 Bumped version 2017-09-28 21:20:37 -07:00
snipe
e759a249bd Merge branch 'develop' 2017-09-28 21:18:39 -07:00
snipe
42c2a66946 Small UI tweaks for accessories 2017-09-28 21:18:16 -07:00
snipe
4d32f2b337 Fixes #4059 - accessories view 2017-09-28 21:18:00 -07:00
snipe
0e29744ec2 Don’t try to send an email if the user doesn’t have an email address 2017-09-28 20:57:33 -07:00
snipe
51236a2ad9 Fixes #4056 - check for assets before deleting user 2017-09-28 19:57:52 -07:00
snipe
9b6726a630 Fixes #4056 - check for assets before deleting user 2017-09-28 19:57:03 -07:00
snipe
cbdd05144a Merge branch 'develop' 2017-09-28 19:50:34 -07:00
snipe
46d87849f4 Added content security middleware 2017-09-28 19:45:15 -07:00
snipe
507f1f196c Added integrity hashes 2017-09-28 18:46:16 -07:00
snipe
b60febeea2 Removed space in XSS header because safari was getting angry 2017-09-28 18:45:54 -07:00
snipe
b3e0d8f675 Disallow / in robots 2017-09-28 17:47:48 -07:00
snipe
9b84a0d516 *eyeroll* 2017-09-28 17:34:47 -07:00
snipe
adac5ac544 Check for valid asset 2017-09-28 17:32:37 -07:00
snipe
1775995f26 Is this space necessary? Getting weird results from netsparker 2017-09-28 17:25:04 -07:00
snipe
df4700b411 Merge branch 'develop' 2017-09-28 17:17:03 -07:00
snipe
26a7701cda Added referrer-policy header 2017-09-28 17:12:58 -07:00
snipe
a34085f1d9 Added mode=block to XSSProtect header 2017-09-28 16:28:27 -07:00
snipe
8e682c715e Merge branch 'develop' 2017-09-28 16:07:04 -07:00
snipe
915c19ebda Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-28 16:03:41 -07:00
snipe
7fded367c4 Adds rel="noopener" to footer links 2017-09-28 16:03:36 -07:00
Daniel Meltzer
0a4743210c Pass urls to vue. Should fix subdirectory issues. (#4054) 2017-09-28 16:03:04 -07:00
Daniel Meltzer
af19e5d976 Fix old route (#4053)
Looks like we missed in the v4 port.  Fixes the unrelated issue in #4052
2017-09-28 16:02:50 -07:00
snipe
3d7277398c Fixes #4057 - sig file in lightbox 2017-09-28 15:13:05 -07:00
snipe
a7ad48a02a Make fields nullable for licenses 2017-09-27 22:11:20 -07:00
snipe
469a3fc608 Merge branch 'develop' 2017-09-27 17:48:32 -07:00
snipe
0fb4ff77f4 Restoring older SnipeModel attribute setters
In case needed by API - needs investigation
2017-09-27 16:39:20 -07:00
snipe
ac83dba2bb Fixes #4034 - save login note 2017-09-27 16:35:54 -07:00
snipe
979ecf961d Added back button to oauth page 2017-09-27 16:33:51 -07:00
snipe
13dcdf41b8 Fixes #4045 - missing back button 2017-09-27 16:32:37 -07:00
snipe
fc96fa756e Fix redirect default on password reset 2017-09-27 16:23:21 -07:00
snipe
ea9a502152 Added empty regsitration controller
When using the default Laravel auth routes, it expects a registration controller, even though we don’t have a concept of registration. This blank controller just prevents route caching from throwing errors.
2017-09-27 16:23:01 -07:00
snipe
d844734b6c Use named login route 2017-09-27 16:22:02 -07:00
snipe
ec8a3d2e56 Fixes #4027 - proper redirect on fieldset delete 2017-09-27 16:02:29 -07:00
snipe
5410dc4255 Fixed link to contributing docs in readme 2017-09-27 15:30:20 -07:00
snipe
d1112bbc99 Fix created_at date display for groups index 2017-09-27 15:28:02 -07:00
snipe
ecf041fa10 Fixes #4043 - standardize groups API response 2017-09-27 15:18:29 -07:00
snipe
0ab9bc1db8 Added normalization midddleware, removed 2017-09-27 15:18:05 -07:00
snipe
73e788b94b Make min_amt fillable 2017-09-27 14:50:56 -07:00
snipe
e91a537552 Use more modern Request handler 2017-09-27 14:50:48 -07:00
snipe
ef8c1abf28 Fixes #3113 and #4040 2017-09-27 14:50:17 -07:00
snipe
bd0498aa69 Fixes #4016 - signature file missing from history tab 2017-09-27 12:58:08 -07:00
snipe
ea45033588 Merge branch 'develop' 2017-09-26 16:03:09 -07:00
snipe
5e1df7049c Make collation and charset for mysql an env variable 2017-09-26 16:02:55 -07:00
snipe
e27e1a78c3 Fix for case where a fieldset is assigned to a model, but no fields are assigned 2017-09-26 16:01:23 -07:00
snipe
d585a34a26 Prevent users from running this as root/admin user 2017-09-26 13:11:01 -07:00
snipe
4edaba648e Tweaks to upgrade script 2017-09-26 13:05:34 -07:00
snipe
9dca7396f3 Merge branch 'develop' 2017-09-26 11:12:55 -07:00
snipe
4324242475 Bumped version 2017-09-26 11:12:36 -07:00
snipe
eca5a05335 Fix forgotten password missing route (???) 2017-09-26 11:08:47 -07:00
snipe
aa4d3c3ffb Fix forgotten password missing route (???) 2017-09-26 11:07:47 -07:00
snipe
d442feb687 Add @gizzmojr as a contributor 2017-09-25 22:18:25 -07:00
snipe
0a88141b18 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-25 22:17:38 -07:00
snipe
d5e6d82ca1 Bumped version 2017-09-25 22:17:00 -07:00
gizzmojr
f2a62857cb Clear audit input field (#4010) 2017-09-25 22:15:29 -07:00
snipe
b354ca817d Bumped version 2017-09-25 22:10:06 -07:00
snipe
4b9bfc178d One more try on #4001 2017-09-25 22:05:57 -07:00
snipe
abc2f7e789 Bumped build number 2017-09-25 21:57:16 -07:00
snipe
29cc9a0815 Tweaks to upgrade script 2017-09-25 21:50:41 -07:00
snipe
f2ee7dcabb Fixes #4001 - license checkout not working 2017-09-25 21:40:43 -07:00
snipe
26203801f6 Fixes #4009 - zip not populating on locations listing page 2017-09-25 20:45:05 -07:00
snipe
fbe9539130 Proto upgrade script 2017-09-25 20:30:18 -07:00
snipe
43ec959385 Add @richardhofman6 as a contributor 2017-09-25 16:58:47 -07:00
snipe
9b6276f281 Bumped version 2017-09-25 16:04:23 -07:00
snipe
0715791229 Include oauth keys in backup 2017-09-25 15:45:33 -07:00
snipe
0a0661bf41 Additional fixes for #3995 in atypical blades 2017-09-25 15:41:02 -07:00
snipe
6ee939d29b Allegedly fixes #3995 - subdirectory issues with JS/CSS 2017-09-25 15:39:18 -07:00
snipe
c3afbc0e53 Run backups before purging and importing 2017-09-25 15:00:23 -07:00
snipe
38326314ca Merge branch 'develop' 2017-09-25 11:53:33 -07:00
snipe
865950e766 Fixes #4000 - user_id blank 2017-09-25 11:53:10 -07:00
snipe
d49b67d033 Fix for assigned user location
Was breaking requestable page
2017-09-25 11:26:04 -07:00
snipe
6b63808e34 Fix for asset location null on user 2017-09-25 11:25:15 -07:00
snipe
34dfcb5add Merge branch 'develop' 2017-09-22 17:23:38 -07:00
snipe
30019a144a Disable login note editing on demo 2017-09-22 17:23:22 -07:00
snipe
3e222b674a Merge branch 'develop'
# Conflicts:
#	resources/views/hardware/view.blade.php
2017-09-22 17:04:43 -07:00
snipe
e316444c63 Show suppliers link - force cache break? 2017-09-22 17:03:57 -07:00
snipe
b29d7beb3a Merge branch 'develop'
# Conflicts:
#	.gitignore
#	Dockerfile
#	README.md
#	app/Http/Controllers/AccessoriesController.php
#	app/Http/Controllers/AssetMaintenancesController.php
#	app/Http/Controllers/AssetsController.php
#	app/Http/Controllers/ConsumablesController.php
#	app/Http/Controllers/GroupsController.php
#	app/Http/Controllers/LicensesController.php
#	app/Http/Controllers/ReportsController.php
#	app/Http/Controllers/UsersController.php
#	app/Http/routes.php
#	app/Models/Depreciation.php
#	app/Models/Location.php
#	config/version.php
#	resources/views/account/view-assets.blade.php
#	resources/views/hardware/edit.blade.php
#	resources/views/hardware/view.blade.php
#	resources/views/partials/modals.blade.php
#	resources/views/reports/custom.blade.php
#	snipeit.sh
2017-09-22 16:12:18 -07:00
snipe
f0a49fefd7 Bumped version 2017-09-22 15:50:24 -07:00
snipe
998c4a5fe5 Make model number nullable 2017-09-22 04:43:28 -07:00
snipe
626a6408d0 Additional Danish and Polish translations 2017-09-18 19:12:37 -07:00
snipe
126a5671fe Set timeout for LDAP server 2017-09-18 19:11:38 -07:00
snipe
aff104fa5d Bumped version for beta 2017-09-18 18:10:43 -07:00
Daniel Meltzer
a5764351f7 Migrate weird assigned_type issues, Issue #3972 (#3973)
For a while, prior to 987536930, we did not null assigned_type on
checkin.  This migration manually nulls all assigned_type fields if
assigned_to is unset.  Add a test to AssetTest for this as well...kind
of.  We need to extract an Asset::checkin() method for 4.1 that mirrors
Asset::checkOut() to really test this.

This also fixes a separate (but related) issue.  The Asset importer did
not set assigned_type when importing and creating users.  In this
instance, we assume that if assigned_to is set and assigned_type is not,
then the item was checked out to a user and update the DB accordingly.
Also add a check in ImporterTest for this issue.
2017-09-18 16:40:13 -07:00
snipe
348becbbec Production assets generated 2017-09-18 13:01:54 -07:00
Daniel Meltzer
922d6937ae Custom field import repair (#3968)
* There is no notes field on accessories.  Fixes Importer Test.

* Fix notification test.  We should see a checkout not allowed exception when trying to check out to a location if the asset requires acceptance.

* Fix Custom field import.

Add a test for custom field import, and fix a few issues related to
importing custom fields.  This will restore v3 functionality.

* Add UI support for mapping custom fields.

This still requires the field mappings to be created/assigned in
advance, but will fetch all custom field names and allow them to be
selected when setting up custom field mappings.

This commit also updates laravel-mix to v1.4.3 and other node
dependencies to fix some build issues.

* Fix some requestable asset page/assetloc issues.  I'd love to know why laravel expections relationships to be in lower case... but thats a question for another day.
2017-09-18 12:29:08 -07:00
snipe
c53dae4b72 Possible fix for #3919 - allow later versions of mcrypted base64 keys 2017-09-14 16:43:41 -07:00
snipe
17ad7f7800 Merge branch 'master' of github.com:snipe/snipe-it 2017-09-14 14:14:45 -07:00
snipe
6232a077b5 Fix more enum fuckery 2017-09-14 14:06:53 -07:00
snipe
e7d72beb88 Also check for $snipeSettings in the first place
Since the preflight also uses this basic blade
2017-09-12 13:08:43 -07:00
snipe
01e3f4a4db Use site name if provided in the settings table for basic template 2017-09-12 13:01:51 -07:00
snipe
1b76880b0e Add @imanghafoori1 as a contributor 2017-09-12 12:30:21 -07:00
Iman
ed4ea7f1f4 No new feature,No bug fix, Only refactoring (#3949)
* No change in logic !

Just exchanging the if and else code blocks and negating condition.

* remove unneeded else{} block

* Re-indented the code
2017-09-12 12:28:42 -07:00
snipe
81e358a01d Small maintenance fixes 2017-09-08 17:49:01 -07:00
snipe
6c283de60a Check for status_id key - related to #3928
TODO: Fix for model number
2017-09-08 17:24:28 -07:00
snipe
4e7a6c0ccf Fixes #3928 - adds correct key generation and passport install 2017-09-08 17:23:19 -07:00
snipe
eba145503b Bumped version 2017-09-06 19:02:05 -07:00
snipe
ae8c9d6afc Updated translations 2017-09-06 18:05:32 -07:00
snipe
faeca4139d Added new languages, commented out ones with 0% translated 2017-09-06 17:51:11 -07:00
snipe
47909b93f7 Fixed deleted users/restore users view 2017-09-06 17:11:43 -07:00
snipe
472658b2fe Fixes #3924 - missing/donked language string 2017-09-06 17:11:23 -07:00
snipe
42a03a0436 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-06 16:25:07 -07:00
Daniel Meltzer
ae0573b3da Fix asset create (#3929)
* Fix accidental commit of ImporterTest.

* Move the name() method to the presenter

This fixes some weird collisions between laravels voodoo and our
presenter voodoo that confused php.  It's also probably a cleaner place
to put it.  Should fix #3927

* Add missing parenthesis

* Add heading to tables on locations/view page.
2017-09-06 16:24:43 -07:00
snipe
34f816097e Fixed missing quotation mark 2017-09-06 15:43:07 -07:00
snipe
c651c9f1ed Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-09-05 18:35:43 -07:00
snipe
7c390cee2c Bumped version 2017-09-05 18:35:39 -07:00
Daniel Meltzer
987536930c Assorted fixes (#3923)
* Fix some n+1 problems

* Use route in notification dropdown to make sure we link to correct page

* Work on better UI support for checkout to non-user.  Fix links on index bootstrap table, work towards eliminating assignedUser

* Remove Asset::assigneduser() relationship.  Instead add a checkedOutToUser() method and/or port to using assignedTo()

* Adjust string to fit new reality

* Fix #3780.  Move the consumables getDataView method to the ApiController.  Not entirely RESTful, but it's a weird method that probably doesn't need its own controller and the functionality would be strange to stack on the userscontroller...

* Fix file uploads to assets and restore the delete route.

* Add asset maintence edit action to index.

* Suppliers asset list should link to the related asset, not to the supplier with same ID.

* Asset models page should use polymorphic formatter on assigned to to better handle assorted item types.

* Comment out more assigneduser fallacy until we figure out the query builder approach to searching for location text.
2017-09-05 17:54:58 -07:00
snipe
10f322198f Move audited count to top of table 2017-08-31 21:31:07 -07:00
snipe
761371509d Use notifiables for slack audit notification 2017-08-31 21:30:38 -07:00
snipe
3518ea7e7d Fixes #606 - email notifications for expected checkins 2017-08-31 21:18:05 -07:00
snipe
c92eed2b3e Small HTML email tweaks 2017-08-31 21:17:02 -07:00
snipe
0054ce3071 Fixes #3907 2017-08-31 13:45:48 -07:00
snipe
b0f74466bb Removed dd 2017-08-31 11:15:52 -07:00
snipe
b4a0484295 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-08-31 11:15:03 -07:00
Daniel Meltzer
bb874012d9 Progress towards better email notifications (#3911)
Working mail from notification.  Still requires testing/cleaning

Add tests around checkout notification.

This also removes the ability to check out an asset to a location|asset
that requires acceptance/a Eula.  For 4.1 we may think about how to
support such a thing, but at present it seems to make sense to only alow
such assets to be checked out to users, who can be responsible for the
items.
2017-08-31 11:14:21 -07:00
snipe
bb8583eb14 Remove lower casing for LDAP array re: #3910 2017-08-31 11:00:08 -07:00
snipe
8d2c229bc3 Move LDAP validation into form request 2017-08-31 10:44:00 -07:00
snipe
48e6208214 Fixes #3907 - do not require username on user if LDAP import 2017-08-31 10:43:36 -07:00
snipe
22233e3ba6 Bulk asset audit form (needs more testing) 2017-08-29 16:00:22 -07:00
KeenRivals
fd515654ff Fix broken user's manual link (#3902)
snipe-it-manual.readme.io goes to a 404 page
2017-08-29 13:50:57 -07:00
snipe
e439f15a64 Fixed some date math for auditing 2017-08-28 17:20:20 -07:00
snipe
42175782a5 Only pull logo if there is a value 2017-08-26 17:43:00 -07:00
snipe
c7a21e0e4d Production asset build 2017-08-26 17:39:59 -07:00
snipe
d98ffd94f9 Localized modal titles with correct headers 2017-08-26 16:16:41 -07:00
Brady Wetherington
6ad5da44f3 Formalize modals (#3898)
* Refactor Modal JS into standalone file, remove duplicated JS and HTML

* Finish fixing Bulk-checkout and checkout
2017-08-26 16:06:52 -07:00
snipe
479f422e68 Added default if no audit settings are in place 2017-08-26 15:27:50 -07:00
snipe
e10cdd57a5 Removed old getassetloist method 2017-08-26 15:22:04 -07:00
snipe
bf157773c8 Also related to #3888 2017-08-26 15:21:38 -07:00
snipe
fba3949530 Fixes #3888 - broken preview of existing assets 2017-08-26 15:21:10 -07:00
snipe
abc3dea8ac Fixed wonky datepicker on bulk checkout 2017-08-26 14:16:16 -07:00
snipe
51d74ac06d Auduting improvements 2017-08-25 18:40:20 -07:00
snipe
af835d6efc Additional setting validation for new fields 2017-08-25 17:59:01 -07:00
snipe
a7a10455ae Bumped version 2017-08-25 13:27:58 -07:00
snipe
bd02b9ed62 Audit tweaks 2017-08-25 10:18:18 -07:00
snipe
16f57e16cb Fixes #1190 - added basic audit workflow 2017-08-25 10:04:19 -07:00
snipe
af6f208c43 Reordered settings nav 2017-08-25 10:03:05 -07:00
snipe
52270fa4db Derp 2017-08-25 08:30:48 -07:00
snipe
bf3731d65c Set default min password to 10 2017-08-25 08:23:23 -07:00
snipe
233ebf06ee ANOTHER fix for enum fuckery 2017-08-25 07:36:44 -07:00
snipe
e27f6a483d Updated translations 2017-08-25 07:32:57 -07:00
snipe
19670f9dd8 Remove assigned_to constraint 2017-08-25 06:30:10 -07:00
snipe
1448229cd2 Fixes location user route 2017-08-25 06:30:00 -07:00
snipe
4721cab928 Grr. 2017-08-25 06:08:19 -07:00
snipe
08f3e78d26 Merge branch 'checkout-to-location-v2' of https://github.com/dmeltzer/snipe-it into dmeltzer-checkout-to-location-v2
# Conflicts:
#	app/Http/Controllers/Api/UsersController.php
#	app/Http/Transformers/LocationsTransformer.php
#	resources/views/locations/view.blade.php
#	routes/api.php
#	tests/_data/dump.sql
2017-08-25 06:04:22 -07:00
snipe
62227ec27d Link to location in user view 2017-08-25 05:48:32 -07:00
snipe
10711245ba Fixes #3792 - parent/child locations in API 2017-08-25 05:32:12 -07:00
snipe
29a7c8577d Fixes #3849 - fillable for accessories 2017-08-25 03:48:07 -07:00
snipe
dfb1ff81e6 Fixes settings problem in unit tests 2017-08-25 03:40:56 -07:00
snipe
021e723acf Fixed typo 2017-08-25 03:27:41 -07:00
snipe
14c0c314aa Make sure payload is always passed, even if null 2017-08-25 03:27:31 -07:00
snipe
d23ea70b08 Added auth check back to asset store 2017-08-25 03:26:50 -07:00
snipe
1b047c768b Added fullName() presenter for locations 2017-08-25 03:26:10 -07:00
snipe
e6323e0a1b Version bump for beta 2 2017-08-24 23:32:33 -07:00
snipe
73ce5f98bb Removed some logging to make test debugging less verbose 2017-08-24 23:20:51 -07:00
Brady Wetherington
a37cb616eb Add Error DIV's to all modals (#3886) 2017-08-24 22:43:05 -07:00
snipe
659d953f3f Fixed custom error message for status labels 2017-08-24 22:40:07 -07:00
Brady Wetherington
c1dcc22217 Refactor and improve Modal support for Assets (#3884) 2017-08-24 22:24:02 -07:00
snipe
6a67426140 Create travis user? I don’t know wqhy it’s still looking for a travis user 2017-08-24 17:21:50 -07:00
snipe
4ba474cf73 Fixes asset test 2017-08-24 16:52:27 -07:00
snipe
fb6caa35ff Only increment if settings table has a value
(This should only ever come up in the CI tests)
2017-08-23 14:07:01 -07:00
snipe
a5870c888e Removed incrementer from non-asset event listeners 2017-08-23 13:59:59 -07:00
snipe
f35f8477d3 Maybe the travis user isn’t created automatically anymore? 2017-08-23 13:42:17 -07:00
snipe
d0637d38f3 More travis fixes 2017-08-23 13:32:30 -07:00
snipe
7141968d64 Trying to fix travis. Again. Always. 2017-08-23 13:22:01 -07:00
snipe
0f7b7d8e6a Add @zwerch as a contributor 2017-08-23 13:13:50 -07:00
snipe
ca78b3ed7c Fixes models on create asset 2017-08-23 13:08:42 -07:00
snipe
2d2cae10b9 Fixed wonky “maintained” badge 2017-08-23 12:07:00 -07:00
Robin Temme
5347b19910 Update Maintained Badge (#3877)
The maintenance badge was outdated and displayed "no!". As I guess this was not it's intended purpose, I changed it to the new path, and it now displays "yes".
2017-08-23 12:05:29 -07:00
snipe
5e9331f5ae Fixed typo. (English is hard. Let’s go shopping!) 2017-08-23 04:00:10 -07:00
snipe
6e30fa6922 Fixes custom fields in asset listing where no custom fields were assigned 2017-08-23 03:28:13 -07:00
snipe
58b3f0519d Add empty errors array 2017-08-23 00:31:37 -07:00
snipe
f119c69698 Possible fix for #3852 2017-08-22 22:46:02 -07:00
snipe
57f4c986af Enforce password complexity rules on new account password change 2017-08-22 22:41:35 -07:00
snipe
2958630923 Fixed some settings text 2017-08-22 22:11:26 -07:00
snipe
72dacda4f9 Trying again to resolve doctrine/php7 issue 2017-08-22 21:58:42 -07:00
snipe
9c2b986bb0 Fixes doctrine/etc compatibility issue
See: https://github.com/laravel/framework/issues/20490
2017-08-22 21:51:50 -07:00
snipe
06c5bce3c7 Fixes #3865 - employee number mislabeled in list output 2017-08-22 21:40:35 -07:00
snipe
a0cbca85bf Fixes for API calls for password complexity stuff 2017-08-22 21:15:35 -07:00
snipe
9bda62d295 ADDED: Password rules for complexity, min length, rejecting common passwords 2017-08-22 20:32:39 -07:00
snipe
1d7e243d0a Fixes #3790 - parent display on locations index 2017-08-22 15:02:31 -07:00
snipe
63bc2ec09f Fixes #3802 - make id an (int) in API repsonse 2017-08-22 14:53:48 -07:00
snipe
d5cadeab1a Fixes #3854 - more fillable fields for supplier API 2017-08-22 14:30:39 -07:00
snipe
31516abef9 Fixes #3858 - use transformer for single listing categories 2017-08-22 14:26:08 -07:00
snipe
d2535582f3 Fixes #3853 - added notes/zip to suppliers API response 2017-08-22 14:18:21 -07:00
snipe
eaaea303f4 Fixes #3860 - return JSON instead of redirect in API delete dept call 2017-08-22 14:15:13 -07:00
snipe
8c5312b931 Fixes #3866 - weird API behavior in status label types 2017-08-22 14:10:54 -07:00
snipe
4ef6e292d1 Fixes #3868 - model_number/notes in models API call 2017-08-22 13:56:51 -07:00
snipe
6310670835 Fixes #3869 - asset maintenances API endpoints 2017-08-22 13:52:06 -07:00
snipe
15bb30acd6 Fixes #3873 - show groups correctly on user listing page 2017-08-22 12:29:46 -07:00
snipe
148d41d8dc Removes erroneous else 2017-08-22 12:22:32 -07:00
snipe
71c1c74164 Fixes #3085 - adds “change password” functionality back to user accounts 2017-08-22 12:09:04 -07:00
snipe
9c02526a37 Make fields nullable in settings 2017-08-22 10:41:59 -07:00
snipe
25dc26aac3 Added 30 as page selector value for label printing 2017-08-21 22:31:37 -07:00
snipe
afc763ebac Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-08-21 22:31:04 -07:00
snipe
6a73ec6537 First stab at a recrypter for legacy mcrypt conversion 2017-08-21 22:30:56 -07:00
Ryan McGuire
481143b891 Fixes for Red Hat Enterprise Linux 7 and CentOS 6 (#3846)
* Changes for Red Hat Enterprise Linux.

RHEL uses "rhel" in the ID field in /etc/os-release. We'll leave
"redhat" in the script just in case.

Also, RHEL uses a two digit version number in the VERSION_ID field. So
instead of looking for just "7", look for anything that starts with "7".
This should fix RHEL installations as well as not break anything
with CentOS.

* Fixes issue #3079.

"lsb_release -s -r" returns a two digit version number (at least on recent CentOS releases) while the script is looking for a single digit version. We'll change the script so that it only looks for the version starting with 6. This fixes recent releases of CentOS, while also not breaking previous versions that may have used a single digit.
2017-08-18 15:12:09 -07:00
Brady Wetherington
cef67695cd New Dockerfile fixes to add support for the new barcode library (#3856) 2017-08-16 13:10:25 -07:00
snipe
4576cb6f56 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-08-10 14:38:08 -07:00
snipe
56f88d2c22 Fixes #3836 - Adds supplier to licenses column 2017-08-10 14:38:04 -07:00
gibsonjoshua55
c1d1cb8122 Address #3840 and fixes group transformation in UsersTransformer (#3841)
Removes the incorrect variable access in UsersTransformer of a users's
groups and adds an array of groups' ids and names to the return array.
2017-08-10 13:37:54 -07:00
snipe
ac8f46c93c Bumped version for beta release 2017-08-09 19:01:07 -07:00
snipe
101a09b421 Removed chart (for now) 2017-08-09 19:00:54 -07:00
snipe
cb1e3b7978 Fixed encryption warning on custom fields 2017-08-09 18:40:45 -07:00
snipe
de18e2a887 Only bcrypt passwords on user save if the password value is passed 2017-08-08 14:41:58 -07:00
snipe
241bf0cd4b Allow port number to be configured via env 2017-08-08 12:36:44 -07:00
snipe
7da4fe4524 Fixes #3825 - allows fillables through location API 2017-08-08 12:36:28 -07:00
snipe
9bfd345774 Use Company transformer with payload 2017-08-03 19:55:08 -07:00
snipe
df87c82ddc Fixes #3805 - add/update/delete methods for User API 2017-08-03 19:50:18 -07:00
snipe
3e8b7d9c94 Check for overall asset delete permissions before checking to see if the user can delete that specific asset 2017-08-03 19:49:41 -07:00
snipe
7adfab9d9f Fixes #3632 and #3817 - set archived to default 0 so imported assets will be included in expiring reports 2017-08-03 16:04:47 -07:00
Jason Spriggs
7c4ee54f8b Fix #3803 - Return asset object instead of just id (#3811) 2017-08-01 20:01:11 -07:00
snipe
03f9d01aab Fixes #3775 - added missing created_at and updated_at fields 2017-07-29 17:01:03 -07:00
snipe
5b420fb4b9 Fixes #3779 - corrects presenter used for Consumables 2017-07-29 16:44:51 -07:00
snipe
a2d63dd3e4 Fixes #3776 - adds Company to Dept UI 2017-07-29 16:42:17 -07:00
snipe
0408509fdc Fixes #3777 - correct response for two_factor_actived for user API 2017-07-29 16:25:42 -07:00
snipe
5e47c18d68 Fixes #3786 - checkin/checkout to components UI 2017-07-29 16:13:17 -07:00
snipe
b3293a7a5f Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-07-29 15:46:38 -07:00
snipe
4e5cf531f7 Fixes #3799 - use transformers on show() method as well 2017-07-29 15:46:10 -07:00
Donald M
f0bbe38c71 fixes #3725 in develop branch (#3800)
added button for create Asset Model to index.blade.php in develop branch.
2017-07-29 15:33:16 -07:00
snipe
ccb7556281 Fixes javascript error when custom fields exist but do not belong to any fieldsets or models 2017-07-26 17:06:58 -07:00
snipe
ee7348d0d5 Better fix for javascript formatter for employee number 2017-07-26 16:47:47 -07:00
snipe
7d6bf4ac24 Fixes stupid javascript for employee_number 2017-07-26 16:26:47 -07:00
snipe
cb903ab9fd Fixes manager user->name to username in Users API 2017-07-26 08:51:16 -07:00
snipe
66d8308163 More compact payload for users API on location and manager 2017-07-26 08:47:54 -07:00
snipe
847262e989 Fixes #3774 - fixes for inconsistent API responses (‘’ vs null) and adds escaping 2017-07-25 23:40:30 -07:00
snipe
59006964d1 Fixes #3771 - adds username to API response on Asset API 2017-07-25 22:11:50 -07:00
snipe
ac29b142dc Adds max thumbnail width to asset listings, settings 2017-07-25 19:36:38 -07:00
snipe
978a906513 Fixes undefined error when validation fails on asset save 2017-07-25 19:32:09 -07:00
snipe
dfa0714d44 Fixes #3770 - account update not saving correctly 2017-07-25 12:28:11 -07:00
snipe
0fbf9236f4 Fixes #3742 - add employee number to asset listing
Also removes asset report, since it’s basically the exact same output as the asset listing
2017-07-25 01:17:23 -07:00
snipe
c64ca76b3d Fixes API route 2017-07-25 01:16:31 -07:00
snipe
73052d3fc1 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-07-25 00:24:26 -07:00
snipe
ae0b639d0c Added link to AGPL 2017-07-25 00:24:22 -07:00
Jan Felix Wiebe
5cc84ca137 Use correct array when deleting users with location (#3769) 2017-07-24 23:15:15 -07:00
snipe
29f1cf2b48 Increase depreciation max 2017-07-19 19:41:21 -07:00
snipe
7510f9f128 Fixes #3753 - use route direction instead of URL 2017-07-17 16:42:39 -07:00
snipe
7c6143bbb8 Fixes #3754 - incorrect language translation location for supplier 2017-07-17 16:39:19 -07:00
snipe
346156bae1 Added colored icons to indicate status types versus status labels 2017-07-17 16:19:52 -07:00
snipe
3eea1a23f8 Slug the filename
So upoloads with weird characters like parens don’t get hoarked when trying to delete
2017-07-14 02:38:13 -07:00
snipe
1fda4593c1 Update @gibsonjoshua55 as a contributor 2017-07-12 20:41:20 -07:00
snipe
6b5a0983ee Added model validation to custom fieldsets 2017-07-12 19:34:45 -07:00
snipe
ad12a5ce11 Added store/update methods to custom fields API 2017-07-12 19:34:34 -07:00
snipe
ec09ea53db Changed custom fieldset API routes to resource routes for consistency 2017-07-12 19:24:21 -07:00
snipe
5abe72ce02 Added whether the field is required to the custom fieldsets transformer 2017-07-12 19:23:54 -07:00
snipe
3e3276334b Added escaping to API output 2017-07-12 19:23:15 -07:00
snipe
b13e04095e Removed commented code 2017-07-12 19:22:58 -07:00
gibsonjoshua55
3cfcc43efa Adds basic GET api support for CustomFieldsets (#3697)
* Adds basic GET api support for CustomFieldsets

Currently there is not support for getting what fields a given fieldset contains
from the API.  This commit creates a new API Controller for CustomFieldsets as
well as Transformers for CustomFields CustomFieldsets.  Additionally, the api
route has been updated so that a show method can be access from
http://myapp/api/v1/fieldsets/{id}

* CustomFieldsetsTransformer only returns id and name of model

* Added index api method for CustomFieldsets

* Removes copy/paste error in CustomFieldsetController (including search)

* Added id to CustomFieldsetsTransformers

* Adds custom_fieldset_id as a field when storing and updating AssetModels

* Removed uncessesary parameter from CustomFieldsetsController.index

* Cleaned up CustomFieldset API
2017-07-12 17:51:22 -07:00
Daniel Meltzer
f432f98e12 Importer tests + Fixes (#3733)
* Fix Bug in User::generateFormattedNameFromFullName

In a name "John Doe", this method would split it into "John" and " Doe",
Leaving a space in the last name when importing to the database.  Strip
this space.

* Cleanup/fix some item mapping.

Also make some changes to the importer schema to allow for unit testing.
Generate a default item mapping, and then merge that with any custom
mappings.

* Beginning work on importer unit tests.

* Strip out testrun branches from importer.  It added a lot of complexity and was not terribly useful with web importer as it stood, might reconsider down the road however.

* Normalize the mapped keys when using custom field mappings.

* Add test for custom asset import mapping.

* Make all unit tests inherit from a new custom base.

This baseclass currently calls Artisan::migrate() and seeds a Settings
instance.  This fixes unit tests after the autoincrement bits.

* Store requestable as a boolean.  Fixes some import oddities

* Work on tests for accessory importer.

* Test for custom mapping of accessory import, also adjust the internal field for purchase date.

* Update default locale fallback for currency detection

* Fix Reassignable in consumable as well.

* More importer tests and fixes.
2017-07-11 20:37:02 -07:00
Sorvani
f56862c684 change CentOS 7 to pull from git (#3734)
The package setup for CentOS 7 already installs git so use it for the install. This makes later updates easier for the end users. They can simply use git pull like the instructions say for updating.
2017-07-11 20:28:31 -07:00
snipe
9bcfe0748b Make custom fields into their own JSON array 2017-07-08 18:44:28 -07:00
snipe
de5e7bd9ba Removed null formatter 2017-07-08 17:04:52 -07:00
snipe
635299cd74 Decrypt encrypted fields 2017-07-08 17:04:40 -07:00
snipe
20376daec4 Fix encryption on first save 2017-07-08 17:04:24 -07:00
snipe
50e1570543 Added additional models for #3702 2017-07-08 14:49:43 -07:00
snipe
eb94c4333d Fixes #3702 - Added artisan command to walk through special characters and unescape them in the DB 2017-07-08 14:47:43 -07:00
snipe
a49fd16916 Fixed delete file settings route 2017-07-08 13:42:05 -07:00
snipe
0cc9c214aa Added tooltip for undeployable items 2017-07-08 13:39:07 -07:00
snipe
c48bcb7ed1 Added tooltips for checkin/checkout 2017-07-08 13:36:37 -07:00
snipe
09de201027 Removed question mark on delete title 2017-07-08 13:36:25 -07:00
snipe
6ce2127d75 Fixes incorrect link on dashboard 2017-07-08 13:36:06 -07:00
snipe
e79260a0d4 Fixes #3732 - broken tooltips and weird select2 option text behavior
The solve here was a few things - first, load jquery-ui before bootstrap. They have conflicting tooltips. Second, initiate the tooltips in the wenzhixin/bootstrap-table formatter using `data-tooltip=“true”`, and thirdly, add some JS that tells BS table to inititalize tooltips within the table using that `data-tooltip=“true”` business
2017-07-08 13:21:13 -07:00
snipe
932e25ca9b Only add an update log message if the asset isn’t being checked in or out 2017-07-08 03:38:39 -07:00
snipe
b02148f59e Updated formatter for location display 2017-07-08 02:24:30 -07:00
snipe
6046063666 Updated LDAP icon 2017-07-08 02:24:20 -07:00
snipe
fd57617c8e Check that the LDAP extension is loaded and functions are available 2017-07-08 01:48:29 -07:00
snipe
44569558e9 Remove admin label 2017-07-08 01:41:07 -07:00
snipe
d2f6c8af11 Require first name field if LDAP is enabled 2017-07-08 01:40:56 -07:00
snipe
814bf18a4b Updated phpinfo title 2017-07-08 00:23:28 -07:00
snipe
33557f3792 Nicer formatting for phpinfo output 2017-07-08 00:22:30 -07:00
snipe
c2927c4a2e Added phpinfo page if debugging is enabled 2017-07-08 00:09:39 -07:00
snipe
ab2b2f3043 Updated UI for Admin Settings 2017-07-07 23:44:48 -07:00
snipe
0e598702f8 Fixed error field name in settings > login note 2017-07-07 19:56:14 -07:00
snipe
9894edb008 Added login note to settings 2017-07-07 19:54:10 -07:00
snipe
c2e8803d1e Addec clone button back to models, assets, licenses, etc 2017-07-07 18:45:49 -07:00
snipe
7e1f7297b3 Settings tweaks 2017-07-07 18:06:31 -07:00
snipe
3401f3dfb9 Added @uberbrady as code owner of custom fields API too 2017-07-07 17:10:23 -07:00
snipe
31cabaa4db Fixes #3724 - adds edit/delete button back to companies listing 2017-07-07 17:10:06 -07:00
snipe
06c04bf271 Created a contibuting md so GH would stop whining at me
(even though it’s documented elsewhere)
2017-07-07 16:37:11 -07:00
snipe
4c39be1f3c First stab at a code owners doc
More info:
https://github.com/blog/2392-introducing-code-owners
https://help.github.com/articles/about-codeowners/
2017-07-07 16:36:45 -07:00
snipe
c8c279588f Updated language strings 2017-07-07 13:14:51 -07:00
snipe
8aa920ca3a Update composer 2017-07-07 12:59:59 -07:00
snipe
0242283a11 Updated composer 2017-06-23 18:56:42 -07:00
snipe
9e0c5e50b6 Merge branch 'develop' of github.com:snipe/snipe-it into develop
# Conflicts:
#	composer.lock
2017-06-23 18:55:18 -07:00
snipe
95d1612234 Updated composer 2017-06-23 18:52:21 -07:00
Daniel Meltzer
61c6160b98 Importer mapping - v1 (#3677)
* Move importer to an inline-template, allows for translations and easier passing of data from laravel to vue.

* Pull the modal out into a dedicated partial, move importer to views/importer.

* Add document of CSV->importer mappings.  Reorganize some code.

Progress.

* Add header_row and first_row to imports table, and process upon uploading a file

* Use an expandable table row instead of a modal for import processing.  This should allow for field mapping interaction easier.

* Fix import processing after moving method.

* Frontend importer mapping improvements.

Invert display so we show found columns and allow users to select an
importer field to map to.  Also implement sample data based on first row
of csv.

* Update select2.  Maintain selected items properly.

* Backend support for importing.  Only works on the web importer currently.  Definitely needs testing and polish.

* We no longer use vue-modal plugin.

* Add a column to track field mappings to the imports table.

* Cleanup/rename methods+refactor

* Save field mappings and import type when attempting an import, and repopulate these values when returning to the page.

* Update debugbar to fix a bug in the debugbar code.

* Fix asset tag detection.

Also rename findMatch to be a bit clearer as to what it does.
  Remove logging to file of imports for http imports because
it eats an incredible amouint of memory.

This commit also moves imports out of the hardware namespace and into
their own webcontroller and route prefix, remove dead code from
AssetController as a result.

* Dynamically limit options for select2 based on import type selected, and group them by item type.

* Add user importer.

Still need to implement emailing of passwords to new users, and probably
test a bit more.

This also bumps the memory limit for web imports up as well, I need to
profile memory usage here before too long.

* Query the db to find user matches rather than search the array.  Performance is much much better.

* Speed/memory improvements in importers.

Move to querying the db rather than maintaining an array for all
importers.  Also only store the id of items when we import, rather than
the full model.  It saves a decent amount of memory.

* Remove grouping of items in select2

With the values being set dynamically, the grouping is redundant.  It
also caused a regression with automatically guessing/matching field
names.  This is starting to get close.

* Remove debug line on every create.

* Switch migration to be text field instead of json field for compatibility with older mysql/mariadb

* Fix asset import regression matching email address.

* Rearrange travis order in attempt to fix null settings.

* Use auth::id instead of fetching it off the user.  Fixes a null object reference during seeding.
2017-06-21 16:37:37 -07:00
Daniel Meltzer
d12159c042 Fix travis with new observer code (#3683)
* Rearrange travis order in attempt to fix null settings.

* Use auth::id instead of fetching it off the user.  Fixes a null object reference during seeding.
2017-06-20 20:38:13 -07:00
snipe
e009bb956d Possible fix for travis donkery 2017-06-20 19:48:57 -07:00
snipe
25e99194ce Fixes #3681 - corrected fieldname for bulk asset delete 2017-06-20 14:41:30 -07:00
snipe
274f3511f5 Fixes new setup migration for auto-increment 2017-06-20 12:23:32 -07:00
snipe
5b9bcd8fa2 Add next auto-increment to settings form 2017-06-20 12:23:16 -07:00
snipe
4bde058192 Save next_auto_tag_base in settings on edit 2017-06-20 12:19:05 -07:00
snipe
ffba9789b7 Fixed typo in variable name 2017-06-20 12:13:36 -07:00
snipe
1052be670d Increase size of state field 2017-06-15 21:04:10 -07:00
snipe
770092f23f Added save/update/delete observers
This should make it easier to handle action logging between the GUI and the API
2017-06-15 20:54:14 -07:00
snipe
99f65cbf69 Hopefully fixes #3203 and #3439 2017-06-15 19:42:43 -07:00
Daniel Meltzer
5d4920c741 [WIP] Improvements to unit tests. (#3574)
* Improvemenets to unit tests.

* Break up modelfactory into multiple files, populate many states.
* Begin testing validation at the unit test level, test relationships.
* Add tests for Asset::availableForCheckout.
* Model factories now generate all needed relationships on demand,
  which allows us to unit test with a empty database.
* To faciliate the empty database, we move to using sqlite in memory as
  the unit testing database.

* Fix bug with logs of checkouts to non users.

* Fix location finding for assets.  Also Fix location show page to show users associated with location.  Still need some work to show assets.

* More test and generator improvements

* More unit test fixes. PermissionsTest is borked still.

* More Updates

* Rewrite permissionstest.  Check that we have access on the model level rather than via web requests.  Also test delete permissions.

* Fix seeders.

* Make the default asset model factory generate assets that are rtd for testing.

* Save progress.

* Rebase tests, fix department unit test, update database for functional tests.

* Update functional and api tests to use new modelfactory signatures.
2017-06-12 17:39:03 -07:00
Daniel Meltzer
54279f22a3 Update DB to fix tests. 2017-06-12 18:24:20 -05:00
Daniel Meltzer
dfea47a272 Fix location view display. Migrate to api controller methods and fix missing bits to make this happen. Show manager on the location view page. 2017-06-12 18:24:20 -05:00
Daniel Meltzer
f0d78091d2 Add a manager field to locations.
This is round one of the rethink of checkout-to-everything.  A location
now has a manager field, and the manager (by default) be responsible for
assets checked out to the location.
2017-06-12 18:23:50 -05:00
snipe
966a736602 Patches #3640 to develop 2017-06-12 15:31:11 -07:00
Daniel Nemanic
30a9704625 Update UsersController.php (#3640)
If a user is disabled in your Active Directory, it should be deactivated in the licensing too.

The standard state is now deactivated for synced accounts.
Maybe we can change this throw a Setting for a standard state.

The codes comes from this site:
http://www.netvision.com/ad_useraccountcontrol.php
2017-06-12 15:29:55 -07:00
Daniel Meltzer
30487bfee7 Fix importer vue issues. (#3655)
We currently depend on a "vue-strap" module to provide the modal dialog.
The default npm vue-strap is not compatible with vue2, so we need to use
a fork.  Update package.json to reflect this.  Probably makes sense to
move away from vue-strap in all my copious amounts of free time.
2017-06-12 15:24:49 -07:00
snipe
3046d7d33c Fixes #3644 - broken datepicker 2017-06-09 18:53:19 -07:00
snipe
0aa76bd946 PHPStorm lag strikes again 2017-06-09 16:49:55 -07:00
snipe
26d7fffae8 Added in/out string to general 2017-06-09 16:47:28 -07:00
snipe
14f3868b46 Update all controllers to use laravel 5 return view method 2017-06-09 16:44:03 -07:00
snipe
a5cd4a0a22 Use updated view method for laravel 5 2017-06-09 16:31:25 -07:00
snipe
ad816264e9 WIP import mapper #3639
@dmeltzer, I’m not sure how much extra work this will be to integrate.
2017-06-09 16:29:42 -07:00
snipe
c2494fe0e5 Removed social buttons for BS styles 2017-06-09 14:59:23 -07:00
snipe
02edb74cdf Style updates 2017-06-09 14:58:43 -07:00
snipe
408bb6476f Removed some unneeded styles 2017-06-09 14:57:57 -07:00
snipe
ee5b5acffb Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-06-09 12:44:49 -07:00
snipe
e387673e74 Fixes #3651 - backups not working 2017-06-09 12:44:11 -07:00
Ben
17ce4ea08b Fixes #3649 (#3650)
Fixes issue #3649 with gohilurvish’s recommended fix.
2017-06-09 12:23:32 -07:00
snipe
0bc709206f Fixes #3652 - missing groups UI element in user edit view 2017-06-09 12:19:12 -07:00
snipe
e9dd119b9c Fixes some vue styles 2017-06-09 02:06:10 -07:00
snipe
269997a1fb Push v3.6.5 changes into v4 2017-06-09 00:35:23 -07:00
snipe
041b794e1f Fixes #3634 - use new routes for groups 2017-06-08 20:24:26 -07:00
snipe
da2d9b2374 Fixes presenter issue on user history 2017-06-08 19:09:42 -07:00
snipe
a59db7062d Fix license datatable on license view 2017-06-08 18:59:53 -07:00
snipe
58b2834c54 Fixed case for userloc 2017-06-08 18:46:07 -07:00
snipe
2aaa635345 Fixes crash on asset detail view 2017-06-08 18:26:55 -07:00
snipe
8269aec71a Removed erroneous file 2017-06-08 17:51:30 -07:00
snipe
d62896f945 Fixes #3414 - bulk update for asset models 2017-06-08 17:48:48 -07:00
snipe
a717ca683c Fixes #3648 - missing endif in blade 2017-06-08 16:16:49 -07:00
snipe
d7827fedd9 Fixed dashboard size element 2017-06-06 15:19:19 -07:00
snipe
fbcc40a145 Commented out dashboard chart until proper data provided 2017-06-06 02:51:29 -07:00
snipe
12a7309ecd Note that the chart doesn’t work, to clear things up 2017-06-06 02:40:50 -07:00
snipe
9caf045930 Production assets 2017-06-06 02:36:02 -07:00
snipe
1c056d1a59 Updated asset urls 2017-06-06 02:35:26 -07:00
snipe
240a07b108 Formatting for error pages 2017-06-06 02:32:04 -07:00
snipe
c977dda5bb Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-06-06 01:45:25 -07:00
snipe
03411a3ae8 Removed poop class, added comment to webpack file 2017-06-06 01:44:17 -07:00
snipe
01cc00c832 Fixing webpack. Again 2017-06-06 01:39:53 -07:00
snipe
9338b37b74 Try to fix the datepicker :(
Webpack has fucked everything. I hate everything.
2017-06-06 00:51:41 -07:00
snipe
9cd2881ad0 Initiate datepicker 2017-06-06 00:29:52 -07:00
snipe
64b483b686 Updated build css output
I don't know why I need to do this. :(
2017-06-06 00:06:29 -07:00
snipe
1f7266274d Updated manifest 2017-06-06 00:06:01 -07:00
snipe
4e5b2d1fe9 Updated font awesome 2017-06-06 00:05:47 -07:00
snipe
ada2ac06f2 Minified all.css 2017-06-06 00:05:22 -07:00
snipe
9d948e8d23 Removed erroneous bower dir 2017-06-06 00:04:55 -07:00
snipe
4062228092 Updated contribs 2017-06-05 23:54:13 -07:00
Wyatt
3e67cdc501 Allows a null location value for importing LDAP users (#3598)
* Allows a null location value for importing LDAP users

* Forgot you need to set the variable to null..
2017-06-01 21:20:40 -07:00
snipe
be42a0add1 Add @steveelwood as a contributor 2017-06-01 21:03:05 -07:00
snipe
6cadde23d7 Add @manassesferreira as a contributor 2017-06-01 21:00:47 -07:00
snipe
a6c1f7b30b Add @n8felton as a contributor 2017-06-01 20:59:15 -07:00
snipe
1b5b0e9d19 Add @jasonspriggs as a contributor 2017-06-01 20:58:50 -07:00
snipe
2a226fd3f9 Update @pakkua80 as a contributor 2017-06-01 20:58:26 -07:00
snipe
043182593d Add @BrettFagerlund as a contributor 2017-06-01 20:57:55 -07:00
snipe
08dc07b46b Apply PR #3620 to develop (oracle linux support) 2017-06-01 20:55:30 -07:00
Lee Thoong Ching
9a9b6ae228 Update snipeit.sh (#3620)
To support Oracle Linux ( equivalent to redhat which check os version and type )
2017-06-01 20:54:23 -07:00
snipe
2d3ea4eec6 Apply PR #3616 (Docker updates) 2017-06-01 20:53:01 -07:00
morph027
7c8dc9fe2d docker: use ubuntu:xenial with php7.0 (#3616)
Nice, thanks! I'll patch this over to develop as well.
2017-06-01 20:50:09 -07:00
snipe
74aaadcdc5 Fixes #3583 - incorrect 404 status code, use curl for checking .env 2017-06-01 20:41:23 -07:00
snipe
0238dd59a3 Fixes #3600 - wrong route for hardware 2017-06-01 20:34:01 -07:00
snipe
461ca53289 Fixes #3603 - Depreciation report crashes 2017-06-01 20:31:44 -07:00
snipe
ccc0063556 Fixes #3604 - company view not displaying assets on page load 2017-06-01 20:28:09 -07:00
snipe
66145a625c Upgraded laravel mix 2017-05-31 13:57:34 -07:00
snipe
cc5192c91e Updated asset url 2017-05-31 13:57:23 -07:00
snipe
de5334e525 Updated with production assets 2017-05-31 13:27:17 -07:00
snipe
b797604627 Fixed &%#!@@!ing ES that prevented npm run production from working 2017-05-31 13:26:48 -07:00
snipe
293981148c Few more stabs at this dumb uglify error 2017-05-31 12:56:09 -07:00
snipe
66eaff739a More npm/css/js updates 2017-05-31 12:34:05 -07:00
snipe
8b5cb6e8c2 Added build directory for misc laravel mix crap 2017-05-31 12:01:45 -07:00
snipe
7b4b2d79cf Moved CSS/JS/etc assets up one level 2017-05-31 10:31:56 -07:00
snipe
c3af13298e Added webpack.mix.js
Everything is terrible
2017-05-31 10:28:53 -07:00
snipe
312fd27d11 Removed weird temp file 2017-05-31 10:28:07 -07:00
snipe
1b55d38c63 Attempts at un-fscking package.json for laravel mix 2017-05-31 10:26:27 -07:00
snipe
1992906790 Use laravel mix asset call 2017-05-31 09:41:52 -07:00
snipe
b0fc2489f6 Almost fixes user history table
Something is still fucky here
2017-05-31 09:39:52 -07:00
snipe
b13726880e Fixes #3601 - HTML instead of username for manager in user view 2017-05-31 09:39:30 -07:00
snipe
25dca80644 Fixes #3602 - actions missing from suppliers in v4 2017-05-31 06:52:37 -07:00
snipe
748aba3c48 Add assets dashboard feature
- also a really broken sample graph
2017-05-31 05:23:54 -07:00
snipe
c9484fa30b Updated compiled assets 2017-05-31 05:23:26 -07:00
snipe
d950ddbda7 Changed go button to primary class 2017-05-31 05:23:09 -07:00
snipe
fd0165b4b8 New compiled assets 2017-05-31 03:32:29 -07:00
snipe
fd0da6ecf0 Ignore the icon column in exports 2017-05-31 02:03:09 -07:00
snipe
7b1265cb89 Cache totals for later dashboard use 2017-05-31 02:02:55 -07:00
snipe
92052442e3 Include department in user info 2017-05-31 01:17:30 -07:00
snipe
71b668e1a3 Removed extra else to make Codacy happy 2017-05-23 15:53:39 -07:00
snipe
3c167c9d33 Remove debugging frowny-face 2017-05-23 15:06:43 -07:00
snipe
9f87f72c96 Updated route name for activity report API call 2017-05-23 14:33:36 -07:00
snipe
5e0cc93ba2 Use new formatters in bootstrap table 2017-05-23 14:33:19 -07:00
snipe
3724442318 Exclude some BS table calls if it’s just a simple ajax table with no sorting/searching 2017-05-23 14:32:58 -07:00
snipe
610a773620 Use activity report for view-assets 2017-05-23 14:32:21 -07:00
snipe
a1c7d2922e Tweaks to dashboard API calls 2017-05-23 14:32:02 -07:00
snipe
8ab29c628f Added icon to presenter 2017-05-23 14:31:44 -07:00
snipe
4ada5eaa94 Added targetType method 2017-05-23 14:31:14 -07:00
snipe
5499735f3a Actionlogs API transformer 2017-05-23 14:31:04 -07:00
snipe
40e11b6661 Removed old getActivityReportDataTable() method, since we’re using the API now 2017-05-23 14:30:55 -07:00
snipe
b1fa25b73e Added filter to report by user_id for activity 2017-05-23 14:30:29 -07:00
snipe
452c52a3ab Changed variable name for consistency 2017-05-23 14:30:07 -07:00
snipe
76926c97c1 Updated dashboard to use new API activity report and added category summary 2017-05-23 09:25:45 -07:00
snipe
6775c39a5e Added simple_view to use compact, no pagination view of tables 2017-05-23 09:25:20 -07:00
snipe
ce856bdb0a Moves activity report into API routes 2017-05-23 09:24:53 -07:00
snipe
52f24f7587 Reports API controller 2017-05-23 09:24:36 -07:00
snipe
fba7f1ccf6 Fixes #3605 - missing table close tag causing page layout bonking 2017-05-23 03:20:50 -07:00
snipe
ed46dee646 Dept tests 2017-05-23 03:03:30 -07:00
snipe
332076bbe5 Fixed location test method name 2017-05-23 03:03:25 -07:00
snipe
9d45375b8a Added dept to user edit/create/bulk edit views 2017-05-23 02:51:09 -07:00
snipe
d3b07411da Added depts to bootstrap-table javascript formatters 2017-05-23 02:50:51 -07:00
snipe
d17d3a2ef0 Dept views 2017-05-23 02:50:27 -07:00
snipe
dd8511c66a Dept strings 2017-05-23 02:50:16 -07:00
snipe
f57ebc664e Added dept management to permissions 2017-05-23 02:50:08 -07:00
snipe
f285bd3c29 Added Dept field to users table presenter 2017-05-23 02:49:51 -07:00
snipe
aeca549bab Dept search in User query scopes 2017-05-23 02:49:27 -07:00
snipe
e0d2cbef20 Allow manager to be null for dept 2017-05-23 02:49:00 -07:00
snipe
96e8109d01 Allow location/company/manager to be null via validation 2017-05-23 02:48:51 -07:00
snipe
b223630f72 Added dept to User API transformer 2017-05-23 02:48:03 -07:00
snipe
e9c39add4f Added dept helper reference in create/edit/bulk edit users 2017-05-23 02:47:49 -07:00
snipe
7ec1724308 Create, edit and destroy for depts 2017-05-23 02:47:25 -07:00
snipe
811ddd2f67 Include depts in returned list, sort by dept 2017-05-23 02:46:55 -07:00
snipe
cefb20d46f Set dept manager ID for dept 2017-05-23 02:46:25 -07:00
snipe
bb323db685 Load relationships in query 2017-05-23 02:46:03 -07:00
snipe
eb3aa99e4f Added destroy method to dept 2017-05-23 02:45:51 -07:00
snipe
a7592de304 Added helper for dept list 2017-05-23 02:37:37 -07:00
snipe
64f17cecbf Updated department transformer 2017-05-23 02:37:30 -07:00
snipe
e2f2f9e154 Additional methods for Department web UI 2017-05-23 01:09:13 -07:00
snipe
8caf6623f1 Filter by dept for user display 2017-05-23 01:09:03 -07:00
snipe
6c623866c0 Additional API methods for departments 2017-05-23 01:08:55 -07:00
snipe
659e60fd12 Department controllers and transformer 2017-05-22 21:32:33 -07:00
snipe
ae329f4160 Department model 2017-05-22 21:32:22 -07:00
snipe
e314de3594 Department migration 2017-05-22 21:32:12 -07:00
snipe
b0479923b1 Add link to departments 2017-05-22 21:31:58 -07:00
snipe
e3a5accace Departments routes 2017-05-22 21:31:47 -07:00
snipe
b81dd18576 Fixed delete_at 2017-05-19 03:00:19 -07:00
snipe
dac4b58892 Show warning on deleted items 2017-05-19 02:58:55 -07:00
snipe
71fd430f8e Fixes issue with older, deleted asset tags caching QR codes 2017-05-19 02:51:35 -07:00
snipe
5265821bcc Bumped version 2017-05-18 22:24:03 -07:00
snipe
cb494a74ca Merge branch 'master' of github.com:snipe/snipe-it 2017-05-18 22:20:57 -07:00
snipe
c526ffbf68 Bumped version 2017-05-18 22:20:52 -07:00
snipe
6f5fe83a91 Fixed missing language string in asset display 2017-05-18 22:20:43 -07:00
snipe
0c43e64160 Licenses detail view 2017-05-18 22:10:45 -07:00
snipe
708385e23a Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-05-16 12:32:05 -07:00
Jason Spriggs
0ab37af7ab Add in location parent and child information into API requests (#3582)
* Add location hierarchy to LocationsTransformer

* Add in parent_id
2017-05-16 12:30:11 -07:00
snipe
891660d4ea Fixes #3580 - reduce minimim username size to 1 2017-05-16 12:26:38 -07:00
snipe
dcfc434075 Added suppliers back to new side menu (oops) 2017-05-15 20:55:53 -07:00
snipe
0c8308f5a4 Fixes for ordering on asset listing 2017-05-15 20:55:39 -07:00
snipe
1bd798c4d2 Removed Brady’s email address 2017-05-15 19:51:05 -07:00
snipe
23be188a49 Fixes #3579 2017-05-15 19:28:26 -07:00
snipe
fdd88af44a Fixes #3556 2017-05-15 19:14:55 -07:00
Nate Felton
5a3816c907 Fixing a regression for RHEL 6 with snipe/snipe-it#2993 (#3572) 2017-05-12 17:19:41 -07:00
snipe
c8796cf045 Only prompt for checkin if there is an assigned user 2017-05-10 03:37:30 -07:00
snipe
f4095c6dd0 Bumped version 2017-05-09 15:32:34 -07:00
snipe
ee2c67a65f Added missing logging back in 2017-05-09 15:30:45 -07:00
snipe
5614578710 Don’t make the item clickable if it’s been deleted 2017-05-09 15:30:19 -07:00
snipe
07882110fa Cleanup dashboard controller
We ajax this data in now
2017-05-09 00:38:45 -07:00
snipe
7a05467dbf Fixed missing password routes 2017-05-09 00:37:37 -07:00
snipe
b7843ea565 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-05-08 20:07:03 -07:00
snipe
4f9a253103 Fixes #3546 - increases size of field values in custom fields for listboxes 2017-05-08 20:06:52 -07:00
snipe
322817b8b1 Bumped version 2017-05-08 20:02:52 -07:00
Daniel Meltzer
82dc06ae19 Remove php7 feature (#3548)
...
2017-05-08 18:19:34 -07:00
Daniel Meltzer
38372f2b37 Remove php7 specific feature (#3547)
Not sure how this got in... I don't remember writing it (but I guess I did!)
2017-05-08 18:19:15 -07:00
Daniel Meltzer
e9b056f66c Importer fixes v2 (#3524)
* Pave the imports table

* Format error response if file is the wrong type.

* If a custom field doesn't exist, don't insert a blank string into the custom fields table

* CustomField::db_column_name can return the stored name in the db.  It's slugified when that value is set initially.  This fixes a weird issue where _1 was replaced with _xx
2017-05-03 12:14:35 -07:00
snipe
f6be7caaeb Updated composer to prevent ambiguous warnings 2017-04-27 18:50:00 -07:00
snipe
d7f6d8e997 Fix for completely erroneous DBAL error
This should not work, but somehow does.

¯\_(ツ)_/¯
(╯° °)╯︵ ┻━┻

I hate computers.
2017-04-27 16:56:15 -07:00
snipe
841e3efe96 Add passport commands to boot() to allow us to call them via PHP 2017-04-27 07:09:46 -07:00
snipe
8ba19aa855 Fixes explode for trusted proxy 2017-04-27 07:09:23 -07:00
snipe
5ad9e8585d New composer options 2017-04-27 05:24:30 -07:00
snipe
68e831d555 Fixes #3514 - removed debugging code that was forcing 404 2017-04-27 05:22:03 -07:00
snipe
6fbf45554b Updated phpunit, removed post install and post update commands to see if that fixes upgrade issues 2017-04-27 02:28:22 -07:00
snipe
07f8c9dc5e Added additional mail markdown config variables 2017-04-27 02:27:57 -07:00
snipe
e088eea2f7 Pull out weird query scope that breaks stuff when querying large datasets 2017-04-26 22:40:20 -07:00
snipe
f4cdeaa956 Fixes #3514 - move tinker requirement out of dev 2017-04-25 07:46:56 -07:00
snipe
59a4e161ff Fixes checkout buttons on licenses list 2017-04-24 21:37:20 -07:00
snipe
6d53a5f58d Updated test for new JSON 2017-04-22 19:31:22 -05:00
snipe
54a2c46101 Fixes #3491 - adds supplier info to assets listing 2017-04-22 19:14:28 -05:00
snipe
a7132fcdd7 Updated translation strings 2017-04-06 21:36:33 -07:00
snipe
7305169b79 Removed extra comments 2017-04-06 21:18:42 -07:00
snipe
dcd8e800b1 Moved statuslabels API for route priority 2017-04-06 21:18:35 -07:00
snipe
9c637ba62c Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-04-06 21:03:47 -07:00
snipe
08ef78356d Update Crowdin configuration file 2017-04-06 20:59:25 -07:00
snipe
ef42bcc223 Update Crowdin configuration file 2017-04-06 20:59:24 -07:00
snipe
120c08b3a3 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-04-06 20:08:52 -07:00
snipe
e32d2f98b5 Added nameUrl presenters (for now) 2017-04-06 20:08:47 -07:00
snipe
aa354e883e Removed erroneous comments 2017-04-06 20:08:38 -07:00
itsupportcmsukorg
89ba221765 fix upload hour so it's 24hr instead of always AM (#3480)
correct an error where logUpload was trying to save 12-hr time to a 24-hr field, e.g. 15:00 (3pm) was becoming 3:00 (3am)
2017-04-06 19:48:15 -07:00
snipe
c7acbc0eee Stunning hook 2017-04-06 19:45:47 -07:00
snipe
961c019554 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-03-31 15:48:49 -07:00
snipe
b2e122c1e9 Add @HinchK as a contributor 2017-03-31 15:46:42 -07:00
Kasey
5663429280 applying PR #3456 changes to develop branch (#3472)
* updating with changes made to master

apply PR # 3456 to develop as well

* updating develop with changes made to master

apply PR # 3456 to develop as well

* updating paveit with passport-created oauth tables
2017-03-31 15:43:37 -07:00
Manasses Ferreira
47ac59abef We noted that the barcode folder was not being created. (what is done now in the entrypoint.sh) At the same time, a wrong directory was being created. (which explains the deleted line in Dockerfile) (#3456) 2017-03-31 13:48:50 -07:00
Daniel Meltzer
1bb89db451 A few fixes (#3437)
* This method was renamed, so we need to rename the eager load

* Readd tinker to the composer dev packages.

* Update sql dump for functional tests.
2017-03-31 13:48:31 -07:00
Andrea Bergamasco
7ee172888d /api_tests: Completed tests for basic CRUD for Assets and Components (#3426)
* Refactored AssetsTransformer

Casted all ids to int, escaped all text values,

* Added warranty_expires attribute to Asset model

$asset->warranty_expires contains a Carbon object with the warranty
expiration date. Returns null when either purchase_date or
warranty_months are not set.

* Ignoring php-cs cache files

* Restored asset tests expectations

Work in progress - tests still fail

* API controller refactoring, fixed HTTP status codes in responses

* Restored $request->get - debugging

* Added further checks in ApiAssetsCest::updateAssetWithPatch

* /api_tests: Fixed bugs in update() method + code formatting

* /api_tests: Minor code formatting

* /api_tests: Mirrored test code for PATCH and PUT methods

* Removed repeated code

* Test cleanup

* Fixed issues with update() through PATCH and PUT methods

* Added API test suite to .travis.yml
2017-03-31 13:48:11 -07:00
snipe
45ac1de6bb Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-03-14 16:21:49 -07:00
snipe
f45a3fedca Remove circle-ci config 2017-03-14 16:21:37 -07:00
Andrea Bergamasco
99cc8293ef Fixed undefined metod in unit/PermissionTest (#3422)
* Refactored AssetsTransformer

Casted all ids to int, escaped all text values,

* Added warranty_expires attribute to Asset model

$asset->warranty_expires contains a Carbon object with the warranty
expiration date. Returns null when either purchase_date or
warranty_months are not set.

* Ignoring php-cs cache files

* Restored asset tests expectations

Work in progress - tests still fail

* API controller refactoring, fixed HTTP status codes in responses

* Restored $request->get - debugging

* Added further checks in ApiAssetsCest::updateAssetWithPatch

* Fixed undefined method

* Fixed initial underscore trimmed by str_slug

* CustomFieldTest now works where intl PHP extension is not installed

If a server doesn't have the intl php extension installed, the custom fields tests failed. Now the tests perform the same check done in the CustomField class.
2017-03-14 08:39:03 -07:00
Andrea Bergamasco
e03ebc3fd0 AssetTransformer refactoring, restored tests (#3407)
* Refactored AssetsTransformer

Casted all ids to int, escaped all text values,

* Added warranty_expires attribute to Asset model

$asset->warranty_expires contains a Carbon object with the warranty
expiration date. Returns null when either purchase_date or
warranty_months are not set.

* Ignoring php-cs cache files

* Restored asset tests expectations

Work in progress - tests still fail

* API controller refactoring, fixed HTTP status codes in responses

* Restored $request->get - debugging

* Added further checks in ApiAssetsCest::updateAssetWithPatch
2017-03-14 08:37:39 -07:00
snipe
0469a44582 Bumped codeception version to fix travis breakage
Tests are still failing, but for the right reasons now
2017-03-11 17:12:21 -08:00
snipe
6f269e18fe Remove useless validation for custom CSS 2017-03-11 15:34:54 -08:00
snipe
c5dd33f213 Switch to post 2017-03-11 15:25:19 -08:00
snipe
c8c5c05351 Added asset checkin/checkout API methods 2017-03-11 14:04:52 -08:00
snipe
c3cc2c924f Updated route for asset display 2017-03-11 12:14:41 -08:00
snipe
70761b8157 Status label view 2017-03-11 12:14:19 -08:00
snipe
8dbd9fb299 Added additional permissions for checkin/checkout buttons 2017-03-11 12:14:10 -08:00
snipe
00aa06882c Fixed incorrect route for components 2017-03-11 12:13:35 -08:00
snipe
e475ccc1d4 Use new presenter for colum headers 2017-03-11 12:13:14 -08:00
snipe
542adfa926 Updated sidenav to use proper status labels 2017-03-11 12:12:10 -08:00
snipe
36ad82a041 Updated todo for user_can_checkout 2017-03-11 12:11:39 -08:00
snipe
79197a719e Fixes BS tables for items that don’t have a checkin 2017-03-11 12:11:24 -08:00
snipe
742e3d044e API improvements
Components still need some work here
2017-03-11 10:05:50 -08:00
snipe
a4697f046e Some model/manufacturer cleanup 2017-03-11 08:53:02 -08:00
snipe
2c3b9426fe Update @uberbrady as a contributor 2017-03-11 08:21:37 -08:00
snipe
ae6828a0fa Removed dumb code comment 2017-03-11 08:03:30 -08:00
snipe
2a4bf65883 Basic sanity checking on some of the API controllers 2017-03-11 08:03:16 -08:00
snipe
9027bd1d9d Added in/out to model view display 2017-03-11 08:01:22 -08:00
snipe
f079be5e2f More small README changes 2017-03-11 08:01:05 -08:00
snipe
343da2968a Small README updates 2017-03-11 07:59:45 -08:00
snipe
35705813b3 Add @rashivkp as a contributor 2017-03-11 07:53:03 -08:00
snipe
5e3daee212 Add @abrahamvegh as a contributor 2017-03-11 07:52:39 -08:00
snipe
25d7345bbd Add @peelman as a contributor 2017-03-11 07:52:20 -08:00
snipe
76f5b6c0f0 Add @desmondmorris as a contributor 2017-03-11 07:52:03 -08:00
snipe
9da84a1d4d Add @flashingcursor as a contributor 2017-03-11 07:51:46 -08:00
snipe
2523072b1d Add @feeva as a contributor 2017-03-11 07:51:32 -08:00
snipe
f38ef8db7f Add @FleischKarussel as a contributor 2017-03-11 07:51:03 -08:00
snipe
d2b79ce39e Add @jakemcgraw as a contributor 2017-03-11 07:50:53 -08:00
snipe
6b47b0480e Add @archpoint as a contributor 2017-03-11 07:45:40 -08:00
snipe
a916cbb0d0 Add @buzzedword as a contributor 2017-03-11 07:45:25 -08:00
snipe
5f7c7503ec Add @leostat as a contributor 2017-03-11 07:45:05 -08:00
snipe
e6b88fa565 Add @technogenus as a contributor 2017-03-11 07:44:50 -08:00
snipe
db1730ae06 Add @burlito as a contributor 2017-03-11 07:44:34 -08:00
snipe
1366d0bfb9 Add @rrdial as a contributor 2017-03-11 07:44:21 -08:00
snipe
cf4698d2ed Add @mizar1616 as a contributor 2017-03-11 07:43:58 -08:00
snipe
8875ce7243 Add @mrshu as a contributor 2017-03-11 07:43:47 -08:00
snipe
07f3ae5e0b Add @pakkua80 as a contributor 2017-03-11 07:43:21 -08:00
snipe
cbca7e45c3 Add @diwanicki as a contributor 2017-03-11 07:43:06 -08:00
snipe
9fccec661b Add @svpernova09 as a contributor 2017-03-11 07:42:34 -08:00
snipe
bf837af73a Update @splaer as a contributor 2017-03-11 07:42:03 -08:00
snipe
bd8fc5a07f Add @splaer as a contributor 2017-03-11 07:41:52 -08:00
snipe
564a9f0ed7 Add @cleathley as a contributor 2017-03-11 07:41:39 -08:00
snipe
ded648f293 Add @j0k3r as a contributor 2017-03-11 07:41:06 -08:00
snipe
0b156098df Add @ivarne as a contributor 2017-03-11 07:40:42 -08:00
snipe
3e4fe838f0 Add @southwolf as a contributor 2017-03-11 07:40:29 -08:00
snipe
b3f6e4e47e Add @DanielNemanic as a contributor 2017-03-11 07:40:17 -08:00
snipe
68f3c1727c Add @tomcastleman as a contributor 2017-03-11 07:40:03 -08:00
snipe
79e9072c0a Add @mdaniels5757 as a contributor 2017-03-11 07:39:32 -08:00
snipe
619a1dc63f Add @balticer as a contributor 2017-03-11 07:39:09 -08:00
snipe
82b072ed44 Add @IDM-Helpdesk as a contributor 2017-03-11 07:38:56 -08:00
snipe
839a053077 Add @kobie-chasehansen as a contributor 2017-03-11 07:37:24 -08:00
snipe
6fb7f0d7b6 Add @aalaily as a contributor 2017-03-11 07:36:50 -08:00
snipe
2e3f18d5f2 Add @irasnyd as a contributor 2017-03-11 07:36:37 -08:00
snipe
7c4ccaf75c Add @jbirdkerr as a contributor 2017-03-11 07:36:26 -08:00
snipe
482cdaadcd Add @hawk554 as a contributor 2017-03-11 07:36:12 -08:00
snipe
323899034e Add @Miliamber as a contributor 2017-03-11 07:35:50 -08:00
snipe
79bfc1c246 Add @patgmac as a contributor 2017-03-11 07:35:35 -08:00
snipe
8f7e1b0e81 Add @MicaelRodrigues as a contributor 2017-03-11 07:35:17 -08:00
snipe
2e27456444 Add @Ahimta as a contributor 2017-03-11 07:35:05 -08:00
snipe
52140929c4 Add @swoopdk as a contributor 2017-03-11 07:34:52 -08:00
snipe
4dd732a6eb Add @thehereward as a contributor 2017-03-11 07:34:33 -08:00
snipe
b7d0432aa6 Add @adamdunson as a contributor 2017-03-11 07:34:22 -08:00
snipe
dfd275467e Add @csayre as a contributor 2017-03-11 07:33:54 -08:00
snipe
e6a9657da1 Add @mathieuk as a contributor 2017-03-11 07:33:19 -08:00
snipe
c836c64b0c Add @reidblomquist as a contributor 2017-03-11 07:33:05 -08:00
snipe
847c604d73 Add @balous as a contributor 2017-03-11 07:32:48 -08:00
snipe
67d00ec521 More contributor updates 2017-03-11 07:30:56 -08:00
snipe
edfab0111b Add @merid14 as a contributor 2017-03-11 07:30:05 -08:00
snipe
daa9ccfcf8 Add @YetAnotherCodeMonkey as a contributor 2017-03-11 07:29:41 -08:00
snipe
1aeea54105 Add @rassie as a contributor 2017-03-11 07:29:29 -08:00
snipe
510fb59a56 Add @ddreier as a contributor 2017-03-11 07:29:18 -08:00
snipe
c355ff8fb7 Add @opsydev as a contributor 2017-03-11 07:29:05 -08:00
snipe
8c6aa6d900 Update @tiagom62 as a contributor 2017-03-11 07:28:15 -08:00
snipe
04a79f8ad3 Add @FREImedia as a contributor 2017-03-11 07:27:53 -08:00
snipe
114e064326 Update @itsupportcmsukorg as a contributor 2017-03-11 07:27:22 -08:00
snipe
5854c62cd4 More contributor updates 2017-03-11 07:14:20 -08:00
snipe
1a5899e276 Updated README 2017-03-11 06:56:37 -08:00
snipe
5959308d6e Add @vjandrea as a contributor 2017-03-11 06:54:15 -08:00
snipe
f586304086 Add @vsposato as a contributor 2017-03-11 06:53:53 -08:00
snipe
39707943f6 Add @madd15 as a contributor 2017-03-11 06:53:46 -08:00
snipe
e58100252b Add @mtucker6784 as a contributor 2017-03-11 06:51:23 -08:00
snipe
5bd6869be5 Add @dmeltzer as a contributor 2017-03-11 06:49:34 -08:00
snipe
4c2bba8734 Add @uberbrady as a contributor 2017-03-11 06:48:49 -08:00
snipe
4e81b97145 Toggle visibility of fields in categories presenter 2017-03-11 05:30:14 -08:00
snipe
a680541fe8 Fixed target variable 2017-03-11 05:17:02 -08:00
snipe
793d9e9250 Fixed routes that didn’t have auth requirement 2017-03-11 05:16:56 -08:00
snipe
b51b51b949 Removed logging 2017-03-11 04:49:14 -08:00
snipe
23b1a4ca34 Higher per page 2017-03-11 04:39:35 -08:00
snipe
5b450c5978 Disambiguated company_id in assets api controller 2017-03-11 04:39:28 -08:00
snipe
875d0dd343 Added advanced search to assets 2017-03-11 04:26:01 -08:00
snipe
119e0772c1 Dragtable libraries
(These don’t work exactly as expected - weird CSS behavior)
2017-03-11 03:09:53 -08:00
snipe
b16ff263ec Addtional search filters for licenses API 2017-03-11 02:57:19 -08:00
snipe
bb2b6cb642 Foxed license name in licenses API 2017-03-11 02:52:50 -08:00
snipe
d799ab2a9c Narrowed down the accessories, consumables, etc APIs for manufacturer filtering 2017-03-11 02:49:24 -08:00
snipe
c511073bc2 Fixes checkin/checkout for assets on manufacturers detail page 2017-03-11 02:03:13 -08:00
snipe
5803af7409 Use date display formatter on licenses 2017-03-10 22:09:35 -08:00
snipe
caf2f648b1 Added updated/created to locations API 2017-03-10 22:09:21 -08:00
snipe
c132036f5c Fixes #3311 - more fields for manufacturers 2017-03-10 22:08:59 -08:00
snipe
1682867d02 Added employee number back into user listings 2017-03-10 20:58:09 -08:00
snipe
da66eecd74 Allow string as employee number 2017-03-10 20:57:57 -08:00
snipe
73f5457b1b Fixed subnav permissions in Asset Menu for expanded permissions 2017-03-10 20:48:58 -08:00
snipe
f1fd152513 Fixes #3335 - added additional pagination at top of table 2017-03-10 20:35:03 -08:00
snipe
bf99534f8d Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-03-10 20:07:49 -08:00
snipe
ad510cecd5 Fixes #2422 - Adds bulk edit for select user attributes 2017-03-10 20:07:44 -08:00
Karol
4af2a8eb35 Update user(view) for support multilanguage (#3383)
Update user(view) for support multilanguage. Added lang var. and strings
2017-03-10 16:58:29 -08:00
snipe
fee60ec8dc Fixes #3145 2017-03-10 16:49:26 -08:00
snipe
01ae8802c1 Fix user export 2017-03-03 19:54:06 -08:00
snipe
318ac2be9a Break cookie 2017-03-03 19:53:58 -08:00
snipe
8dfc44d4fd Removed old unused files 2017-03-03 19:28:08 -08:00
snipe
69b014a342 Switch to URL 2017-03-03 19:07:57 -08:00
snipe
5d8faa934b Fixed link in error pages 2017-03-03 19:06:34 -08:00
snipe
878f4f868d Added date formatter to consumables 2017-03-03 19:04:54 -08:00
snipe
9af5600c69 Wordwrap in side nav so it doesn’t cut off 2017-03-03 18:50:05 -08:00
snipe
2e274b5f21 Fixed user upload route 2017-03-03 18:41:13 -08:00
snipe
97dd5a58fb Removed space between time and AM/PM 2017-03-03 18:28:22 -08:00
snipe
51fccbdda4 Added last login to user menu 2017-03-03 18:28:13 -08:00
snipe
2b4443f810 Fixed user created at in listing 2017-03-03 18:16:23 -08:00
snipe
a9950392c4 Fixed weird sidenav breakage from smaller font size 2017-03-03 18:03:20 -08:00
snipe
6a2fd74219 Fixed API gate names in tranformers for new, more granular admin stuff 2017-03-03 17:56:05 -08:00
snipe
097096aa24 More custom date display via API 2017-03-03 17:53:42 -08:00
snipe
4f1669189e Try to make the select all checkbox work for users.
Not sure where/how this broke?
2017-03-03 17:30:37 -08:00
snipe
3c53ff9329 Set protected date formats 2017-03-03 17:30:19 -08:00
snipe
b5424462c9 Moved non-superadmin settings into sidebar 2017-03-03 17:30:12 -08:00
snipe
dc38e575d9 Allow date/time formatting selection in settings 2017-03-03 17:29:41 -08:00
snipe
161bd7acce Fixed double-encoding of ampersand 2017-03-03 17:17:51 -08:00
snipe
c6c3ddba3e Remove detail() presenter 2017-02-23 17:01:01 -08:00
snipe
ce2babcafb Fixed name display in transformer for asset listing 2017-02-23 17:00:50 -08:00
Andrea Bergamasco
c0c02eebd2 REST API - Refactoring of routes file, more tests added (#3345)
* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390

* Delete asset image: made checkbox more visible

Related to #3153

* Added personal-access-token component

* Created basic API testing configuration

* First version of /components endpoind cest

* On-the-fly bearer token generation

* Completed testing of PATCH and PUT methods

* Added /components/{id}/assets route with tests

* Updated route and dataTable in view

* Completed test assertion

* Added links to assets in ComponentsAssets view

* Linked Company in AssetView page

* Fixed purchase_cost format expectation in ApiComponentsCest

* Refactored api routes file

Sorted all prefixes in alphabetical order, removed duplicate routes. For every prefix I placed first Route::resource and then any additional route in a Route::group. Expanded arrays for readability and consistency. Removed useless calls as create and edit everywhere.

* Refactored and added one more test to ApiComponentsAssetsCest

* Marked one test as incomplete, 404 response should return json

* Fixed value expectation

* Refactored getToken()

* Added API debugging routes

* Added more information to ValidationException reporting

Now the payload contains the validation errors for each invalid
attribute.

* /apitests: refactored expectations in component assertions

* Created ApiAssetsCest

* /apitests: Cleanup in Exceptions/Handler

* Reverted change to use

* Marked two tests as incomplete, looking for solutions
2017-02-23 16:32:35 -08:00
snipe
9ccaad8b9c Added created_at and modified_at to asset listing/api 2017-02-23 16:23:02 -08:00
snipe
9e7f998df5 Add model_number to accessory search 2017-02-22 22:55:20 -08:00
Andrea Bergamasco
e7f7d739ed Bugfixes based on functional tests (#3338)
* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390

* Delete asset image: made checkbox more visible

Related to #3153

* Added personal-access-token component

* Created basic API testing configuration

* First version of /components endpoind cest

* On-the-fly bearer token generation

* Completed testing of PATCH and PUT methods

* Added /components/{id}/assets route with tests

* Updated route and dataTable in view

* Completed test assertion

* Added links to assets in ComponentsAssets view

* Linked Company in AssetView page

* Bugfixes based on functional tests

* Removed unused function

* Marked tests as incomplete

* Added check for  existence in groups/edit.blade.php
2017-02-22 22:49:19 -08:00
Daniel Meltzer
ad9470b6f8 Assorted fixed (#3332)
* Better error handling of failed file uploads.  Also improve formatting of modal dialog

* Readd app.js to the js build, fixes collapsing sidebar

* Update JS

* Fix font size.  We need to explicitly set the font size for the table because everywhere else that comes from bootstrap-tables
2017-02-22 22:49:06 -08:00
snipe
381526e488 Additional permissions 2017-02-22 22:21:26 -08:00
snipe
7cf42ecf9e New side menu for “other stuff”?
Need to figure out what to call this
2017-02-22 22:21:17 -08:00
snipe
8c20c2bd39 Fixed Illuminate path 2017-02-22 22:20:57 -08:00
snipe
904be03ffd Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-02-22 22:20:32 -08:00
snipe
ebbc2a60c3 Fix JS error when there is no row.update/delete defined 2017-02-21 15:40:25 -08:00
snipe
53ba2bbb4e Updated method for eager loading assets 2017-02-21 15:39:59 -08:00
snipe
2c6f323820 Increase max EOL 2017-02-21 15:39:46 -08:00
snipe
f520eaa6ce Increase max depreciation months 2017-02-21 15:39:39 -08:00
Andrea Bergamasco
fde46251de Components Assets view + stubbed API tests (#3325)
* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390

* Delete asset image: made checkbox more visible

Related to #3153

* Added personal-access-token component

* Created basic API testing configuration

* First version of /components endpoind cest

* On-the-fly bearer token generation

* Completed testing of PATCH and PUT methods

* Added /components/{id}/assets route with tests

* Updated route and dataTable in view

* Completed test assertion

* Added links to assets in ComponentsAssets view

* Linked Company in AssetView page
2017-02-21 14:26:46 -08:00
Andrea Bergamasco
4f73a13c6b Hotfix/translate row delete msg (#3305)
* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390

* Delete asset image: made checkbox more visible

Related to #3153

* Added missing translation to bootstrap table partial

The "Are you sure to delete" confirmation was hardcoded. Added translation row to all locales and other small corrections to it/general lang file.
2017-02-21 14:25:46 -08:00
snipe
edf36cd850 Sigh. 2017-02-17 18:30:48 -08:00
snipe
a710c98b50 Set string length because laravel is dumb sometimes 2017-02-17 18:26:53 -08:00
snipe
8fe8ff71e8 Category improvements 2017-02-16 00:32:33 -08:00
snipe
a18e90c22a Fix delete modals 2017-02-15 23:04:49 -08:00
Andrea Bergamasco
d9fe39c00e Delete asset image: made checkbox more visible (#3293)
Related to #3153
2017-02-15 21:52:10 -08:00
Andrea Bergamasco
9975d55249 jQuery script to toggle the disabled state of auto_increment_prefix (#3292)
* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate.

* Toggles the disabled state of auto_increment_prefix

To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390
2017-02-15 21:51:30 -08:00
snipe
c67b44b3d5 Moving license index to new API format 2017-02-10 18:43:30 -08:00
snipe
eb3767890e Changed font size
This seems wrong - shouldn’t need to upload 10 files for a one-line CSS change
2017-02-10 18:42:38 -08:00
snipe
74c5c55838 Removed outdated comment 2017-02-08 18:25:06 -08:00
snipe
eea90dda78 Added Gate integration into API transformers 2017-02-08 18:24:55 -08:00
snipe
959fafe04b Expanded checkin/checkout buttons formatter 2017-02-08 18:21:03 -08:00
snipe
2892e32095 Set visible to true for checkin/checkout buttons 2017-02-08 18:20:44 -08:00
snipe
8cce1a3882 Fixed users formatter method 2017-02-08 18:20:23 -08:00
snipe
86198badbb Added more company_id filtering, more transformers 2017-02-08 08:48:41 -08:00
snipe
2986747fd7 Fixed sidenav status type filtering 2017-02-08 03:51:52 -08:00
snipe
72ec362f72 Search on status label type 2017-02-08 03:37:44 -08:00
snipe
4cf01e4f48 Added ability to search by company_id and location_id 2017-02-08 03:31:42 -08:00
snipe
dd6a477355 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-02-06 18:49:13 -08:00
snipe
6d40a98a16 User assets presenter for column headers 2017-02-06 18:49:08 -08:00
snipe
584e2ba618 Added a space 2017-02-06 18:47:54 -08:00
Daniel Meltzer
79f3ca3074 Modify the jquery that fires select2 to prevent it being run twice on the importer vue select2 (which is created separately). (#3276) 2017-02-06 18:04:36 -08:00
snipe
1751a8c0e0 Switched to presented for ugly BS table column headers 2017-02-03 22:20:11 -08:00
snipe
d37b9fe169 Use hardrware listing for status labels detail link 2017-02-03 20:34:09 -08:00
snipe
8b0066cbad Fixed user link formatter on models detail 2017-02-03 20:22:41 -08:00
snipe
98e0ecbd88 Added manufacturer sorting on models 2017-02-03 20:20:03 -08:00
snipe
2c0df6159f Added formatting for Suppliers index 2017-02-03 20:19:54 -08:00
snipe
9cae6066a0 Added asset by manufacturer listing 2017-02-03 19:52:00 -08:00
snipe
f1c4aed5b5 Removed additional groups API fields 2017-02-03 19:35:22 -08:00
snipe
78f2b30b31 Removed the plus icon from file uploader in vue
I cannot believe I have to check in 7 files just for this. Vue is garbage.
2017-02-03 19:35:07 -08:00
snipe
1e2a23e955 Use main assets API to pull view pages for assets listings 2017-02-03 19:34:24 -08:00
snipe
a69090c91c Moved import to top level sidenav 2017-02-03 19:33:40 -08:00
snipe
85d5b75de6 Removed console debugging messages 2017-02-03 03:55:43 -08:00
snipe
cddd3cdb81 Added spinner icon to loading message 2017-02-03 03:54:38 -08:00
snipe
729b709de2 Include notes in custom asset report 2017-02-03 02:16:37 -08:00
snipe
f1592aa63f Tighter asset API responses, sanitization 2017-02-03 02:04:17 -08:00
snipe
cdb6349035 Updated JS 2017-02-03 01:45:41 -08:00
snipe
6c1a827ed0 Added warranty info to listing 2017-02-03 01:45:12 -08:00
snipe
f9bed9264f Removed oops state if model has no fields 2017-02-02 19:11:10 -08:00
snipe
ca490fd129 Updated javascript 2017-02-02 19:00:42 -08:00
snipe
93719b2214 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-02-02 18:55:29 -08:00
snipe
0c3840503e Laravel 5.4 2017-02-02 18:54:54 -08:00
snipe
498dc9da43 Removed duplicate header 2017-02-02 18:54:31 -08:00
Brady Wetherington
7dbbe862ce Tried to fix build environment/select2 (#3262) 2017-02-02 18:53:50 -08:00
snipe
d19cc9d9c1 Added legacy redirector 2017-02-02 18:14:25 -08:00
snipe
b7d3b65bae Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-02-02 17:12:55 -08:00
Daniel Meltzer
3c8910eeab Add select2 to node for now. (#3243) 2017-02-02 17:07:26 -08:00
snipe
18e2db24bd Updated groups index route name 2017-02-01 19:13:17 -08:00
snipe
f1a83bd007 Fixed bulk delete 2017-02-01 19:07:39 -08:00
snipe
12c5ca67ff Pulled accidental debugging exit 2017-02-01 18:56:47 -08:00
snipe
00f7cc028b Fix dumb icon bug 2017-02-01 18:54:43 -08:00
snipe
bf919e133c Center boolean results in the list 2017-02-01 18:53:27 -08:00
snipe
541a5e6776 Groups API starter
Edit/Create still broken for some reason
2017-02-01 18:50:28 -08:00
snipe
464c524375 Depreciations API 2017-02-01 17:59:03 -08:00
snipe
7086ac8a8b Tweaks to status labelas listing display 2017-02-01 17:48:28 -08:00
snipe
7642d17fb8 Fix category listing 2017-01-26 21:11:27 -08:00
snipe
a80af854ba Switch companyName to company 2017-01-26 21:05:33 -08:00
snipe
99d837fa50 Added API methods and UI to view assets associated with a particular model 2017-01-26 21:02:59 -08:00
snipe
8d50bb19af Switched to route from redirect->to 2017-01-26 20:13:59 -08:00
snipe
ec7e89ecb8 Added Helper 2017-01-26 20:08:15 -08:00
snipe
377cb8e5d1 Use Locations transformer for location API 2017-01-26 20:08:06 -08:00
snipe
c7003897ae Fixed Locations transformer for assigned assets 2017-01-26 20:07:46 -08:00
snipe
231f4dce07 Basic components API 2017-01-26 19:16:06 -08:00
snipe
5e9e6da577 Basic consumables API 2017-01-26 18:46:18 -08:00
snipe
0e1f5c173c Added blue-imp to package.json 2017-01-26 18:31:24 -08:00
snipe
34ab78ef99 Updated db column name test for various languages 2017-01-26 16:57:23 -08:00
snipe
11810ae9ad Minor CSS changes 2017-01-26 14:56:34 -08:00
snipe
5d3568136e Added some printer-friendly stuff 2017-01-26 06:20:53 -08:00
snipe
9ac9c6f1bc Updated convertUnicodeDbSlug to use Patchwork if intl isn’t installed 2017-01-26 04:59:14 -08:00
snipe
863735a6e5 Fix utf encoding on DB 2017-01-26 04:52:38 -08:00
snipe
d02a7064e3 Added patchwork for fallback for systems without intl 2017-01-26 04:52:30 -08:00
snipe
b4c6d0c897 Added optional help text field to custom fields 2017-01-26 04:52:11 -08:00
snipe
b0f84fa82b Updated migration to fix field names 2017-01-26 04:49:01 -08:00
snipe
19313e4b83 Fixed borked down() method in migration 2017-01-26 04:48:43 -08:00
snipe
5e9c69711b Set trimonsearch to false to prevent weird trimming on search queries 2017-01-26 04:48:22 -08:00
snipe
cbff2ec57d Removed log comment 2017-01-25 22:29:05 -08:00
snipe
a688822899 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-01-25 21:33:25 -08:00
snipe
481722507d Composer update 2017-01-25 21:33:11 -08:00
Daniel Meltzer
5ba2ec881c Vue importer (#3235)
* Begin work on vueifying the importer

* Beginning work on migrating the importer to use a vue/components for future interactivity

Update JS

More importer work.  Move to a vue based modal, begin handling of processing.  Still need to port error messages.

More importer work.  Move to a vue based modal, begin handling of processing.  Still need to port error messages.

Update importer.  Add error display.  Fix modal, update vue-strap to vue2

More progress.  Add select2 vue bits.

* Move to querying the db to find importer matches.  It scales better on large datasets.

Fix select2 related issues.  We were trying to initialize it twice, which led to the custom data being overwritten.

* Better error handling on uploads and deletion of files.  Restore progressbar on upload.

* Add support for generic exception reporting if app.debug is enabled.

* Handle Http 500 errors better.  Display errors if debug is enabled.  Assorted cleanups.

* Fix codacy issues, remove unused methods.

* Only bind vue to the importer for now.

* Load vue for passport as well.
2017-01-25 21:29:23 -08:00
snipe
a9bf34cf61 Minor transformer cleanup 2017-01-25 21:01:00 -08:00
snipe
855d775c41 Fix fieldset blade path 2017-01-25 18:39:45 -08:00
snipe
dc47a0fad6 Fixes #2551 - use UTF-8 safe slugging for MySQL columns 2017-01-25 18:38:20 -08:00
snipe
de869c7ed0 Switched to labels for clearer UI on fieldsets 2017-01-25 05:14:39 -08:00
snipe
ec88d54a5b Prevent demo users from deleting/creating API keys 2017-01-25 05:03:30 -08:00
snipe
2a7469d898 Fix uniqueness issue on saving edited fields 2017-01-25 04:52:47 -08:00
snipe
b1a80f8ed8 Fixes #1684 - Allow editing of custom fields
@uberbrady can finally get out of the sea
2017-01-25 04:34:11 -08:00
snipe
7350514a0d Moved custom fields blades to fit the rest of the app convention 2017-01-25 04:31:18 -08:00
snipe
af211c1203 Remove unused method 2017-01-25 03:10:44 -08:00
snipe
ed7e967f4a Helper cleanup, slug helper 2017-01-25 03:10:35 -08:00
snipe
80da0f801c Fixes #2921 - use toUTF8 instead of fixUTF8 2017-01-25 03:10:18 -08:00
snipe
5f49e7c1a7 Accessories API routes
Still a bit of a WIP
2017-01-25 02:19:26 -08:00
snipe
75d35273c9 Pull image formatter out of dynamic loop, pass full url to formatter 2017-01-24 22:46:07 -08:00
snipe
04bb180a80 More bootstrap tables formatting 2017-01-24 22:25:17 -08:00
snipe
8f12486f6c More generic javascript formatters, updated api routes 2017-01-24 21:04:38 -08:00
snipe
f13719bf32 Updated named route for consumables 2017-01-24 19:24:59 -08:00
snipe
c907414e71 Added escaping 2017-01-24 19:24:47 -08:00
snipe
945d910ca2 Added columns to be ignored on export 2017-01-24 19:24:33 -08:00
snipe
e8310c8f3c Additional API routes for licenses, manufacturers 2017-01-24 18:58:07 -08:00
snipe
e4e4e6ae47 Licenses transformer for API 2017-01-24 18:57:33 -08:00
snipe
afacee90ae Fixed variable for transformer 2017-01-24 18:57:21 -08:00
snipe
ebe3f58805 Added missing manufacturer data on save 2017-01-24 18:57:03 -08:00
snipe
88faefe1ed Licenses listing index API 2017-01-24 18:56:27 -08:00
snipe
8f0ab1e719 Fixed weird status code error on ajax 2017-01-24 18:56:15 -08:00
snipe
b019e6e950 Removed closures from routes files so that we can allow people to cache routes for performance optimization 2017-01-24 17:37:07 -08:00
snipe
eea779c16d Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-01-24 17:07:23 -08:00
snipe
b1dcc208e9 Updated vue dist 2017-01-24 17:07:17 -08:00
snipe
d0f062458e Additional API transformers, formatters 2017-01-24 17:07:00 -08:00
snipe
6d2a37fa80 Remove google font from maintenance mode template 2017-01-24 17:03:48 -08:00
snipe
9d555f5436 Fixes #3139 and #3230 - use native font stack, removing google fonts
Especially helpful for those running Snipe-IT in a closed environment
2017-01-24 17:02:39 -08:00
Daniel Meltzer
0f9e39e4cd Avoid magic numbers in the seeders, instead adjust order of seeding and load relationships directly. (#3224) 2017-01-22 14:57:09 -08:00
Daniel Meltzer
df4c7e32b1 Another Travis Fix (#3211)
Make sure we use the testing env for passport:install so we write to the correct db.
2017-01-19 11:48:06 -08:00
snipe
ad4bf83aaa Add ability to view assets by status label
This should be refactored. Lots of copypaste from the assets views here
2017-01-18 20:41:40 -08:00
snipe
19cb428fb0 Asset and status formatters 2017-01-18 19:46:43 -08:00
snipe
b7555e780c Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-01-18 19:38:54 -08:00
snipe
ea3d45882d Fix bulk actions with new bootstrap tables checkboxes 2017-01-18 19:38:42 -08:00
snipe
bebb653a70 Pull common formatters into partial 2017-01-18 19:28:35 -08:00
Daniel Meltzer
a2bf685901 Seeds and tests (#3210)
* Update composer phpcs

* Fix failing model validation in the database seeders.  Should make travis happy.  Also fix a few test issues
2017-01-18 16:50:33 -08:00
snipe
5d14acaac4 Fixed bulk actions using native bootstrap-tables checkall 2017-01-18 07:40:40 -08:00
snipe
47cf734f72 Hide the app div
This will need to be fixed later, but the flash of unstyled content on the non-api pages was making me cray
2017-01-18 05:02:00 -08:00
snipe
3161420eb3 ¯\_(ツ)_/¯ no idea why this doesn’t seem to matter
Too sleepy to investigate it right now
2017-01-18 04:59:56 -08:00
snipe
297034c037 APIified custom fields reorder 2017-01-18 04:58:56 -08:00
snipe
db7b1b586c Fixed custom fields improprly nested form tags 2017-01-18 04:58:31 -08:00
snipe
f6ccbfc9b0 Fixed stupid font error for fontawesome 2017-01-18 04:34:59 -08:00
snipe
eac025263b Basic hardware bootstrap table formatter 2017-01-18 04:16:25 -08:00
snipe
8a4a4264cd Formatters for bootstyra-tables users listing
We should probably abstract these out, since we use similar or the same fields imn other places
2017-01-18 04:12:13 -08:00
snipe
fd594ed608 Gitkeep 2017-01-18 03:56:46 -08:00
snipe
39b4725114 Load assigneruser relationship 2017-01-13 22:17:20 -08:00
snipe
f642013774 Check for validity before trying to display relation data 2017-01-13 22:06:12 -08:00
snipe
b462888850 Derp 2017-01-13 21:15:36 -08:00
snipe
9bc10edc8d Rervet that for now - still need to only enable dropdown if more than one is selected 2017-01-13 21:05:02 -08:00
snipe
e284e9900a Use native boostrap tables check-all 2017-01-13 21:02:55 -08:00
snipe
ba71122449 Removed old datatable method from Locations 2017-01-13 21:00:25 -08:00
snipe
d39b7e19eb Updated debugbar settings 2017-01-13 21:00:02 -08:00
snipe
e54036991c Updated trusted proxy config for newer version 2017-01-13 20:59:54 -08:00
snipe
acd8eafc8c VERY basic example of dynamic buttons in table 2017-01-13 20:58:17 -08:00
snipe
2879114fe9 Misc API cleanup for assets 2017-01-13 11:41:00 -08:00
snipe
b968839a1d Pull out extraneous use statements 2017-01-13 11:38:25 -08:00
snipe
89edda2c23 Manufacturers API 2017-01-13 09:37:06 -08:00
snipe
73eee1b7b1 Derp. Really check for valid category_type in Category model now 2017-01-13 09:28:08 -08:00
snipe
213a234bef Check for valid category types in validation 2017-01-13 09:27:32 -08:00
snipe
14ddcc56a2 Categories API 2017-01-13 09:01:10 -08:00
snipe
edca6ad19d Make eol, notes sortable on asset model 2017-01-13 08:41:17 -08:00
snipe
ae540af2a9 Additional fields for asset models 2017-01-13 08:39:02 -08:00
snipe
34f427920b Removed legacy getDataTable method from status labels 2017-01-13 08:18:37 -08:00
snipe
e7a243f8bc Added search, sorting to assetmodels 2017-01-13 08:18:22 -08:00
snipe
66f724cd35 Added created_at to status labels for consistency 2017-01-13 07:55:03 -08:00
snipe
b423fa3bd4 Added sorting, searching, etc to status labels index 2017-01-13 07:47:39 -08:00
snipe
295744be64 Added searching, sorting, etc to locations API 2017-01-13 07:40:08 -08:00
snipe
fbbf5e77b4 Make sure the category and manufacturer ids exist 2017-01-13 07:14:32 -08:00
snipe
c7e1b426e7 Additional API routes and controllers for models, suppliers, users, locations and status labels 2017-01-13 04:50:20 -08:00
snipe
bc5fcf8736 Fixed dashboard chart 2017-01-13 03:19:39 -08:00
snipe
06d66f6e94 Fixed formatting 2017-01-13 01:45:14 -08:00
snipe
f9a501d774 Fixed array name 2017-01-13 01:44:47 -08:00
snipe
f54fcb88e2 Fixed formatting 2017-01-13 01:43:51 -08:00
snipe
58b1ac5f0f Some route/transformer edits 2017-01-13 00:13:57 -08:00
snipe
d2f84a2329 Added some hidden fields 2017-01-13 00:12:26 -08:00
snipe
4f35c8af3e Docblock edits 2017-01-12 23:43:45 -08:00
snipe
a35323131f Updated documentation link 2017-01-12 23:43:20 -08:00
snipe
fbd6dcbfbb Fixed path to query builder in docblocks 2017-01-12 23:42:59 -08:00
snipe
9c41881d99 Use transformers for API response
This is experimental
2017-01-12 23:42:39 -08:00
snipe
f52680b971 Masde response formatting a little nicer 2017-01-12 23:41:45 -08:00
snipe
e66e830b44 Added additional routes 2017-01-12 23:41:08 -08:00
snipe
6d7c226acd Experimental transformers 2017-01-12 23:40:35 -08:00
snipe
ca2fa3dab9 Added transformer autoload 2017-01-12 23:40:23 -08:00
snipe
267f0bdfea Added additional API routes 2017-01-12 19:41:01 -08:00
snipe
2f43f78e62 Validated category_id on accessories, name model_number fillable 2017-01-12 19:40:42 -08:00
snipe
b11a951e95 Starter API controllers 2017-01-12 19:40:20 -08:00
snipe
ed325d2fe5 Let whatever the other error is fall through 2017-01-12 19:38:40 -08:00
snipe
3c6010679a Nicer handler for JSON model not found errors 2017-01-12 19:06:39 -08:00
snipe
d9d048f90d Fixed #2303 - redirect correctly after login 2017-01-12 07:09:44 -08:00
snipe
b7a650e986 Switched ordering of status labels and status label types to more user-friednly config 2017-01-12 06:50:54 -08:00
snipe
534e40feb8 Fixed bad fieldset call 2017-01-12 03:55:54 -08:00
snipe
a283b13069 Handled bad model id 2017-01-12 03:53:52 -08:00
snipe
41b864cc49 Added update method for assets 2017-01-12 03:48:18 -08:00
snipe
03874a5481 Validate asset model and status on the model level 2017-01-12 03:47:45 -08:00
snipe
7d2d6a3c1b Removed unused use statements 2017-01-12 02:53:43 -08:00
snipe
29b5678a8d Format destroy responses 2017-01-12 02:50:55 -08:00
snipe
93542f3478 Make sure the model_id and status_id actually exist 2017-01-12 02:41:02 -08:00
snipe
8f2019c62e Added DELETE for destroy method 2017-01-12 02:20:32 -08:00
snipe
fddcc0fefd Added better form request handling
This will break a ton of things
2017-01-12 02:20:20 -08:00
snipe
15373d5f4a Fixed a few asset API issues 2017-01-12 02:19:55 -08:00
snipe
704ee59541 WTF why is name required?? 2017-01-12 02:19:36 -08:00
snipe
5a848566f9 Helper method to standardize JSON responses 2017-01-12 02:19:17 -08:00
snipe
268308004c Fixed number of results returned 2017-01-11 23:45:03 -08:00
snipe
0cf4014b75 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-01-11 23:41:02 -08:00
snipe
95f2d94e01 Added create/destroy methods for asset API 2017-01-11 23:40:56 -08:00
Richard Hofman
c506f30562 Add support for location-specific LDAP OUs. (#3176)
* Add support for location-specific LDAP OUs.

* Shortened variable names as suggested by Codacy review.
2017-01-11 23:37:14 -08:00
snipe
2c695cf7e5 404 JSON response if the asset is not found
(Brady and I will argue about this forever)
2017-01-11 19:12:25 -08:00
snipe
74696d5ded View asset API 2017-01-11 19:00:34 -08:00
snipe
7a124e85d6 Cleaned up unused use statements 2017-01-11 18:26:05 -08:00
snipe
b8d81c0734 Added order_number to presenter 2017-01-11 18:25:53 -08:00
snipe
900e6e2419 Starter asset API 2017-01-11 18:14:06 -08:00
snipe
ff2c489af4 Fixed javascript conflicts, fixing select2 and icheck 2017-01-11 17:07:27 -08:00
snipe
55d0f8c0df I think this is necessary for the Vue.js version? 2017-01-11 15:21:33 -08:00
snipe
aefbb76592 Possible fix for acceptance tests (needs oauth key) 2017-01-11 15:14:33 -08:00
snipe
73d45e9a14 Fixed CSRF on create asset ajax 2017-01-11 15:03:04 -08:00
snipe
15bab7fa9e Test Auth::guard('api')->user() as user_id 2017-01-11 14:55:04 -08:00
snipe
8f31ee318e Removed extra commented out crap 2017-01-11 14:54:50 -08:00
snipe
c7167fc73c Only allow users to create a new user via modal if they have users.create permission 2017-01-11 14:54:39 -08:00
snipe
9e4a391b8b Updated auth:api in kernel 2017-01-11 14:54:11 -08:00
snipe
1d2489cf9c Updated composer lock 2017-01-11 14:53:51 -08:00
snipe
1364494e92 Updated JS 2017-01-11 14:52:32 -08:00
snipe
dbf3a074f7 Fix CSRF issue on bootstrap tables 2017-01-11 14:52:05 -08:00
snipe
4940bf05f0 Removed auth:api from api middleware since it happens magically now 2017-01-11 14:51:00 -08:00
snipe
c08fe196c4 Fix api middleware call 2017-01-11 14:50:38 -08:00
snipe
93a087b29a Set longer token expiration 2017-01-11 14:50:26 -08:00
snipe
4590054c75 Fixed path in comments 2017-01-11 14:50:13 -08:00
snipe
80e047f679 Removed poop 2017-01-11 14:45:47 -08:00
snipe
86926675ce Fixed passport token generation 2017-01-11 08:44:34 -08:00
snipe
41f58efb70 Font-awesome for bower 2017-01-11 06:53:49 -08:00
snipe
e2d5401f3a Stop double-loading jquery 2017-01-11 05:51:13 -08:00
Daniel Meltzer
9bbfb64f17 Add a use declaration to Userscontroller to prevent assigning the wrong item_type. Should fix/prevent #3160 (#3168) 2017-01-11 04:27:05 -08:00
snipe
84154d936a Added self.api permission 2017-01-11 04:05:39 -08:00
snipe
57d0eba58f API blade cleanup 2017-01-11 04:05:32 -08:00
snipe
a02f406fa6 Removed personal access token from settings api page 2017-01-11 03:58:09 -08:00
snipe
19fcda5c25 Basic API management
I’m getting a 500 on personal token creation, but there’s nothing in the logs, so I have no idea what’s wrong
2017-01-11 03:55:47 -08:00
snipe
b5ddd9ab0a More vue/api work 2017-01-11 03:38:55 -08:00
snipe
d14ed4a73c Bower components 2017-01-11 03:05:06 -08:00
snipe
33ffc58ffe Fixed vue issue with api 2017-01-11 01:27:02 -08:00
snipe
11d86d83b6 Fixing JS to actually use vue 2017-01-11 01:02:32 -08:00
snipe
c99f9e321f Temprarily disable auto-phpcs 2017-01-10 23:22:00 -08:00
snipe
b7bbc44052 Removed unneeded use statement 2017-01-10 23:21:47 -08:00
snipe
34a9564f30 Updated language strings 2017-01-10 17:53:06 -08:00
snipe
3060de8e0c Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-01-10 17:11:11 -08:00
Daniel Meltzer
44683c784f Importer: Add License Importer and refactor (#3143)
* Major code simplification of the importers.

Move towards using Model::fill and Model::update rather than reinventing
the wheel.  This makes the updating/creating logic a lot clearer, and
allows for the deletion of a lot of code.  Also allows for supporting of
more fields in the future really easily.

* Cleanup constructors and use setters instead.

* Set the LC_MONETARY locale, and use it to strip currency symbols in Helper::parseFloat()

* Move licenseseat creation/deletion logic into an event handler on the model rather than the controller.

* Move the logging of parsed values to array_smart_fetch rather than writing it out everywhere

* Move to storing dates as carbon rather than strings.  Allows for the parsing of more arbitrary strings from the importer

* Add a license importer with support for checking out to users or assets.

* Make a directory for sample/mock import csvs and populate it

* Adjust how we store/retrieve dates to fix some issues the tests found.
2017-01-10 16:19:18 -08:00
snipe
b4bed2a11f Move cipher into the .env 2017-01-09 19:41:24 -08:00
snipe
57d2b88900 Remove number formatting 2017-01-06 03:14:32 -08:00
snipe
208ccab4c1 Fixes #3104, #2914 and #2918 - auto-increment incorrectly generating next ID 2017-01-06 02:54:06 -08:00
Daniel Meltzer
6ce20c32b1 Component Importer and various Importer Fixes (#3132)
* Importer fix: we were trimming the wrong part of the classname when creating a category.  This led to categories not being recognized.

* Add a component importer.  Uses same fields as consumable importer.  Only trick: If an asset_tag is present, we checkout a component to that asset on import

Enable component importer.  Also calculate the importer classname in a cleaner fashion.

* Fix comparisons.  find can return an index of 0, which is falsy.
2017-01-05 15:45:12 -08:00
Daniel Meltzer
816d2fd095 More minor fixes (#3126)
* Update composer phpcs

* Minor Fixes found while working.
ALlow user_id to be nullable in category model validation

Point to the correct route in accessoriespresenter datatable.  Also add name() method

Add a translated category name

Fix typo in translation

* Add a method to the asset presenter to show deployed when asset is deployed.  Fixes #3114
2017-01-03 16:55:43 -08:00
Bob Clough
f999c797e5 Allow a license to be pre-checked-out to an asset. (#3124)
Some licenses, such as windows licenses, are machine specific.  This
commit allows an extra field to be added to the license importer to
allow these licenses to be imported and attached directly to an asset
2017-01-03 16:55:21 -08:00
snipe
6351746c62 Bumped to v4.0-develop 2016-12-30 16:28:47 -08:00
Daniel Meltzer
3a6bbcc615 More improvements (#3116)
* Restore display of deleted items in the logs

* Fix functional tests.
2016-12-30 11:44:47 -08:00
Daniel Meltzer
8a782bf34a Present assetlogs (#3112)
* Set user if asset is checked out to user.  fixes email problems.

* Use sometimes validation to ignore this when no values are present.

* Move Actionlog details to a presenter and port the activity table to use it.  Still need to port other parts of the application, but this consolidates a lot of logic.

* Attempt test fix

* Port users,licenses, and assets view to use the presenter to generate table values.
2016-12-29 22:23:36 -08:00
snipe
6029bea2de Fixed error if user not set 2016-12-29 18:19:08 -08:00
Daniel Meltzer
4ad9c3623e Show a glyph in the assets datatable. (#3111) 2016-12-29 18:12:27 -08:00
snipe
4c5fce847b Removed user “new” buttons next to non-user options 2016-12-29 17:41:39 -08:00
snipe
0581499029 Fixed migration 2016-12-29 17:26:50 -08:00
snipe
16bd78fb3f Removed stray poop 2016-12-29 17:26:21 -08:00
snipe
199fdf39ec Merge branch 'dmeltzer-checkout-to-things-v1' into develop 2016-12-29 16:20:37 -08:00
snipe
221cf1f9c8 Merge branch 'checkout-to-things-v1' of https://github.com/dmeltzer/snipe-it into dmeltzer-checkout-to-things-v1
# Conflicts:
#	app/Http/Controllers/AssetsController.php
#	app/Http/Controllers/ReportsController.php
#	app/Http/Controllers/UsersController.php
#	app/Presenters/AssetPresenter.php
2016-12-29 16:20:17 -08:00
Daniel Meltzer
ce8eda1325 Fix some accessory sorting (#3108)
Fixes at least some of #3045
2016-12-29 15:50:18 -08:00
snipe
51ceaedfaf Small phpcbf cleanup 2016-12-29 14:02:18 -08:00
Daniel Meltzer
62dd474d44 Update dump with new migration. 2016-12-29 14:06:16 -05:00
Daniel Meltzer
aa2d3cf026 The assets method was renamed to assignedAssets in User class. Adjust places to reflect that 2016-12-29 11:45:37 -05:00
Daniel Meltzer
fa974b93c0 This authorization is now handled in the controller. 2016-12-29 11:44:34 -05:00
Daniel Meltzer
13b51bc934 Allow checkout of item to things on create page. Need to test validation better and maybe extract code to one place for checkout.blade and edit.blade 2016-12-29 11:10:52 -05:00
Daniel Meltzer
7ccd71978c Cleanup checkedout to part of asset view. 2016-12-29 09:56:19 -05:00
Daniel Meltzer
8cc695b65f Port more assignedUser to assignedTo. 2016-12-29 09:31:16 -05:00
Daniel Meltzer
165487ac92 Add migration 2016-12-27 22:04:30 -05:00
Daniel Meltzer
719463ef54 Progress 2016-12-27 22:04:11 -05:00
snipe
fd805bde50 Fixes #1247 - allow SVG logo upload 2016-12-27 17:31:53 -08:00
snipe
55415e8f56 Fix image resizing for later version of Intervention 2016-12-27 17:31:29 -08:00
Daniel Meltzer
d262aec4c3 Save Progress. 2016-12-27 19:24:41 -05:00
Daniel Meltzer
13cf11368f Reformat all view files. (#3105)
* Reformat all view files.  Check for matching tags and rearrange to make everything line up.

* Fix regression on asset create where the log was no longer saved.
2016-12-27 12:03:47 -08:00
Daniel Meltzer
cafafe851c Notification rework (#3103)
* Move slack integration to laravel5.3 style notifications, part 1.

* Fix consumable tab when active.

* Move the slack notifiable to the settings model.  Move all slack notifications into logCheckout/logCheckin.  Should think about refactoring this as an event at some point still.  Move Asset checkin/checkout to use the general loggable trait rather than it's own solution.

* Fix a logic error where assets with a non deployable status would show checkin instead of no button at all.

* Fix an html formatting error that resulted in us not closing a form.  This would cause the checkin page to try to submit a delete request (related to the modal form) rather than the desired checkin request.  Also fix formatting in this file.
2016-12-26 15:19:04 -08:00
Daniel Meltzer
06af9311fc Move sanitization of input to the model attribute setters. This cleans up a lot of checks in the various controller methods and ensures data will be set in the model accurately regardless of where it's set. Add unit tests for these methods (#3102) 2016-12-26 15:17:46 -08:00
Daniel Meltzer
fd450e2773 Two asset maintence related fixes (#3101)
* Fix maintenances create button, and post to the proper route in maintences edit

* Fix consumable tab when active.

* Fix an html formatting error that resulted in us not closing a form.  This would cause the checkin page to try to submit a delete request (related to the modal form) rather than the desired checkin request.  Also fix formatting in this file.

* Use log mail driver for testing, should fix the functional issue.  Disable acceptance tests on travis for now.

* Fix Category edit page.

* EOL Can be null.
2016-12-26 15:17:12 -08:00
Daniel Meltzer
57374955a8 Importer rework (#3100)
* Step 1 of refactoring importer to use separate classes.

* Port web importer.  Fix an issue with validation where index 0 would be treated as false and cause weird results.

* Farewall, AssetImport.  You've served us well.
2016-12-26 15:16:42 -08:00
snipe
8857faee65 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2016-12-23 19:23:27 -08:00
Daniel Meltzer
02c1a45025 View presenters (#3099)
* Add presenters for models.  Move bootstrap table JSON generation to these presenters, which cleans up controllers a lot.  Move view specific modifications from the models to the presenters as well.

* Fix some issues found by travis and codacy

* Fix a few more issues found while testing.

* Attempt another acceptance test fix

* Try something else

* Maybe..

* Move conditionals out of the datatable method and into dedicated url methods.
2016-12-23 19:23:07 -08:00
snipe
d1fecb6128 Added php codesniffer 2016-12-23 18:32:07 -08:00
snipe
40615f9504 Merge branch 'develop' of github.com:snipe/snipe-it into develop
# Conflicts:
#	app/Http/Controllers/StatuslabelsController.php
2016-12-23 17:53:03 -08:00
Daniel Meltzer
61543f3a04 Add presenters for models. (#3098)
* Add presenters for models.  Move bootstrap table JSON generation to these presenters, which cleans up controllers a lot.  Move view specific modifications from the models to the presenters as well.

* Fix some issues found by travis and codacy

* Fix a few more issues found while testing.

* Attempt another acceptance test fix

* Try something else

* Maybe..
2016-12-23 17:52:00 -08:00
snipe
297b9d25f0 Fixed issue with status labels 2016-12-23 16:07:58 -08:00
snipe
94a6b555a8 Darker red for warning 2016-12-22 21:21:10 -08:00
snipe
0cf5ad120b Removed todo 2016-12-22 21:09:01 -08:00
snipe
93dd27339b Fixed missing with clause 2016-12-22 21:08:43 -08:00
snipe
42db9563ae Updated composer lock 2016-12-22 21:03:52 -08:00
snipe
fc18aa7f6d Fixes bug #3086 - unabel to create asset from asset model screen 2016-12-22 17:08:42 -08:00
snipe
d6b7036518 NOT FINISHED: Added case for component import
This is not finished. The actual component creation isn’t written yet
2016-12-22 16:06:55 -08:00
snipe
2fe984013b Fixes #3092 - user location not being exported correctly 2016-12-22 15:53:34 -08:00
Daniel Meltzer
323c3807fa Cleanup controller escaping (#3084)
* Make delete routes work.  We put a little form in the modal that spoofs the delete field.

* Fix route on creating a user.

* Fix redundant id parameter.

* Port acceptance tests to new urls.

* Initial work on migrating to model based policies instead of global gates.  Will allow for much more detailed permissions bits in the future.

* This needs to stay for the dashboard checks.

* Add user states for permissions to build tests.

* Build up unit tests for gates/permissions.  Move accessories/consumables/assets to policies instead of in authserviceprovider

* Migrate various locations to new syntax.  Update test to be more specific

* Fix functional tests.

Add an artisan command for installing a settings setup on travis-ci

* Try a different id... Need to come up with a better way of passing the id for tests that need an existing one.

* Try to fix travis

* Update urls to use routes and not hardcode old paths.  Also fix some migration errors found along the way.:

* Add a environment for travis functional tests.

* Adjust config file to make travis use it.

* Use redirect()->route instead of redirect()-to

* Dump all failures in the output directory if travis fails.

* Cleanups and minor fixes.

* Adjust the supplier modelfactory to comply with new validation restrictions.

* Some test fixes.

* Locales can be longer than 5 characters according to faker... fex gez_ET.  Increase lenght in mysql and add a validation

* Update test database dump to latest migrations.

* Extend Supplier phone/fax length.

This catches issues found in testing with a phone number with a five digit extension.  fex (356) 654-3024 x36632

Also move away from escaping all values put into eloquent.  Eloquent
already uses PDO parameter binding, and this was leading to names like
Mr Ryan O'Malley turning into an html escaped version of that name when
stored.  All values should be escaped when using {{}}, we'll just have
to be more cautious when we use {!!, but I think we already are?

* Remove additional escaping here, like we did in suppliers controller.

* No need to eager load all of these relationships when we can call the count on the querybuilder directly

* Work on controller cleanup

* Always start from scrach, catches more issues this way.

* Update sql dump.  Remove old code from permissions test.

* Generate a deletable item on demand in the test, rather than relying on one existing.  I think we should probably move to mock all the database stuff at some point..

* More travis related fixes

* Break script into multiple functional lines

* Update all controllers to use the new helper, also cleanup syntax and docblocks along the way.
2016-12-19 22:00:50 -08:00
Daniel Meltzer
cd8c585377 Discussion: Moving to policies for controller based authorization (#3080)
* Make delete routes work.  We put a little form in the modal that spoofs the delete field.

* Fix route on creating a user.

* Fix redundant id parameter.

* Port acceptance tests to new urls.

* Initial work on migrating to model based policies instead of global gates.  Will allow for much more detailed permissions bits in the future.

* This needs to stay for the dashboard checks.

* Add user states for permissions to build tests.

* Build up unit tests for gates/permissions.  Move accessories/consumables/assets to policies instead of in authserviceprovider

* Migrate various locations to new syntax.  Update test to be more specific

* Fix functional tests.

Add an artisan command for installing a settings setup on travis-ci

* Try a different id... Need to come up with a better way of passing the id for tests that need an existing one.

* Try to fix travis

* Update urls to use routes and not hardcode old paths.  Also fix some migration errors found along the way.:

* Add a environment for travis functional tests.

* Adjust config file to make travis use it.

* Use redirect()->route instead of redirect()-to

* Dump all failures in the output directory if travis fails.

* Cleanups and minor fixes.

* Adjust the supplier modelfactory to comply with new validation restrictions.

* Some test fixes.

* Locales can be longer than 5 characters according to faker... fex gez_ET.  Increase lenght in mysql and add a validation

* Update test database dump to latest migrations.
2016-12-19 11:04:28 -08:00
Daniel Meltzer
ae2cb5fe68 Make delete routes work. (#3077)
* Make delete routes work.  We put a little form in the modal that spoofs the delete field.

* Fix route on creating a user.

* Fix redundant id parameter.

* Port acceptance tests to new urls.
2016-12-19 10:42:33 -08:00
snipe
c7e98366be Fixed Users functional test 2016-12-15 21:17:11 -08:00
snipe
84e1b531fd Bumped hash 2016-12-15 20:53:50 -08:00
snipe
9ea05bacf3 User resource routes 2016-12-15 20:52:39 -08:00
snipe
76344a8c9b Misc view rounte cleanups 2016-12-15 20:09:51 -08:00
snipe
4751bcd002 Use redirect()->route instead of ->to 2016-12-15 20:02:47 -08:00
snipe
f832b15cf3 Components routes 2016-12-15 19:59:42 -08:00
snipe
e685e0f019 Cleaned up custom fields controllers, views, names, etc 2016-12-15 19:17:07 -08:00
snipe
040774d646 One more /admin fix 2016-12-15 18:25:39 -08:00
snipe
37b4b29653 Fixed URL for statuslabels route 2016-12-15 18:23:26 -08:00
snipe
01f9deb5a6 Fixed forgotten to-> to ->route 2016-12-15 18:20:41 -08:00
snipe
d6b41759f0 Updated manufacturers, suppliers, depreciations for new route resources 2016-12-15 18:18:13 -08:00
snipe
e8945ad85e Fixed hardware.show for controller 2016-12-15 18:17:20 -08:00
snipe
406b828b4e Updated named location routes in hardware 2016-12-15 17:18:22 -08:00
snipe
c308fbce0d Updated resources, named routes, tests for Locations 2016-12-15 17:12:22 -08:00
snipe
3e4be6671e Updated categories with resty routes 2016-12-15 16:42:47 -08:00
snipe
2bb94e6806 Removed old hardware delete route to force failure 2016-12-15 16:42:28 -08:00
snipe
f182a7db2a Removed leading slash for url helper 2016-12-15 16:42:14 -08:00
snipe
aab0933856 Use url() helper over URL::to 2016-12-15 16:41:36 -08:00
snipe
b7a2c4c26d Use Request instead of Input:: 2016-12-15 15:56:52 -08:00
snipe
c6ab34faee Updated Companies for #3059 2016-12-15 15:48:30 -08:00
snipe
6c8e9327c1 Fixed Request path 2016-12-15 15:47:08 -08:00
snipe
8ad509d7f1 Update validation in AssetRequest form request 2016-12-15 15:24:55 -08:00
snipe
e5286229e0 Small model validations 2016-12-15 15:15:26 -08:00
snipe
cf2b57cb15 More for #3057 2016-12-15 15:15:11 -08:00
snipe
4603000d12 Removed print_r 2016-12-15 15:14:08 -08:00
snipe
816affc83b Tests updated for #3066 2016-12-15 15:14:00 -08:00
snipe
ad1bf86a08 And still more license route updates 2016-12-15 13:07:34 -08:00
snipe
bea1a93e9b More license route updates 2016-12-15 12:48:15 -08:00
snipe
1ab414453f Use $request instead of Input:: 2016-12-15 12:47:12 -08:00
snipe
eb9207d0fe Fixed license routes in getDataTable 2016-12-15 12:14:16 -08:00
snipe
0c5d3d1c74 Updated license routes 2016-12-15 11:57:19 -08:00
snipe
456e4a633e Corrected route for license view 2016-12-15 11:41:08 -08:00
snipe
3106c336bb Temporarily remove Vue extras 2016-12-15 06:11:53 -08:00
snipe
d46e77c037 Updated test for #3066 2016-12-15 06:11:32 -08:00
snipe
a6b975b168 More updates for #3060, #3058 2016-12-15 06:11:03 -08:00
snipe
8faffb1889 Started to fix some of the URLs and routes in test (#3066) 2016-12-15 04:28:16 -08:00
snipe
b17e380aae Removed stray echo tag 2016-12-15 04:27:33 -08:00
snipe
9b167d87d2 Updated to use named routes (#3060), some FORM spoofing for PUT (#3061)
(Updating assets isn’t currently working - investigating that)
2016-12-15 04:27:17 -08:00
snipe
7f65c6e472 Started to break API routes out (#3058) 2016-12-15 04:12:18 -08:00
snipe
2f03c5ddb0 Updated validation to allow nullable integrers #3064 2016-12-15 04:11:31 -08:00
snipe
e67b3e474f Use more RESTy method names, uses route names where possible (#3059, #3060) 2016-12-15 04:09:40 -08:00
snipe
a369a111e0 Updated validation for nullable integers 2016-12-15 01:37:19 -08:00
snipe
112ee3c8ff Removed extra api in prefix, added v1 2016-12-14 17:49:52 -08:00
snipe
a8e09728a5 Passport vue edits 2016-12-14 12:10:28 -08:00
snipe
899493e4d5 Not so much for the nightlies ;) 2016-12-14 11:23:18 -08:00
snipe
30bc3cbe9b Make company ID nullable 2016-12-14 11:12:21 -08:00
snipe
030207d626 Downgrade laravel-backup for users not on PHP7 2016-12-14 11:11:48 -08:00
snipe
fb0cce0640 Try nightly and latest 5.6.4 2016-12-14 11:01:32 -08:00
snipe
4753d70946 Travis doesn’t support 5.6.4, so remove it 2016-12-14 11:00:03 -08:00
snipe
38eb910999 Updated email template with signoff and Site Name 2016-12-14 10:54:22 -08:00
snipe
9840683c97 Moved API routes into API routes file 2016-12-14 10:54:05 -08:00
snipe
43619fa3c9 Moved API routes into API route file 2016-12-14 10:53:53 -08:00
snipe
4f2439ae1c Add Passport API token generator 2016-12-14 10:53:01 -08:00
snipe
a80ece9b84 New passport views 2016-12-14 10:17:07 -08:00
snipe
25f60264bd Passport scaffolding 2016-12-14 10:06:05 -08:00
snipe
97a1a166f0 Ignore generated OAuth keys 2016-12-14 10:05:54 -08:00
snipe
91fe136fd3 More nullables 2016-12-14 09:58:12 -08:00
snipe
37c847ea08 Clearer/updated/named custom fields routes
This is still broken
2016-12-14 09:56:23 -08:00
snipe
37f93a2861 Better validation using nullable 2016-12-14 09:55:45 -08:00
snipe
7c774352e5 Fix depreciated lists() method 2016-12-14 09:55:35 -08:00
snipe
863e200430 Hopefully fixes tons of PEBKAC where users have the wrong app.url 2016-12-14 08:20:05 -08:00
snipe
8e5977ad84 Updated maintenance views 2016-12-14 07:56:01 -08:00
snipe
a8d5d4a446 Specified PHP 5.6.4 for unit tests 2016-12-14 06:36:25 -08:00
snipe
68ac4abe2c Updated throttling override for 5.3 2016-12-14 06:30:51 -08:00
snipe
69031d4215 Put notifiable back in 2016-12-14 05:07:02 -08:00
snipe
433adb1dcb Updated traits and method names for 5.3 2016-12-14 05:06:51 -08:00
snipe
5cd7e84d98 Renamed Password controllers to new 5.3 versions 2016-12-14 05:06:15 -08:00
snipe
6f55db1251 Updated email addy 2016-12-14 04:38:03 -08:00
snipe
44312b0026 Removes c3 becase fuck codeception and fuck travis.io 2016-12-14 04:37:54 -08:00
snipe
c5b24c83df Updated app config for 5.3
THIS WILL BREAK ON UPGRADE RIGHT NOW.

I need to more gracefully handle the switch between mcrypt and the new cipher
2016-12-14 04:33:27 -08:00
snipe
f6e567d5d4 Updated dependencies for 5.3 2016-12-14 04:32:34 -08:00
snipe
b83f73f7d6 Updated providers for 5.3 2016-12-14 04:32:24 -08:00
snipe
8ca5c6c25e Moved routes to new new routes files for 5.3 2016-12-14 04:32:10 -08:00
snipe
f0dd4c5a97 Remove middleware for checking for maintenance
This is (mostly) a built-in feature in Laravel 5.3
2016-12-14 04:31:19 -08:00
snipe
65db55908a Rename to AuthController to LoginController 2016-12-14 04:30:56 -08:00
4718 changed files with 250166 additions and 254002 deletions

1736
.all-contributorsrc Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,29 +7,31 @@ APP_KEY=ChangeMe
APP_URL=null
APP_TIMEZONE='UTC'
APP_LOCALE=en
MAX_RESULTS=500
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=localhost
DB_HOST=127.0.0.1
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_CHARSET=utf8mb4
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
DB_SSL_CIPHER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
@@ -43,7 +45,7 @@ MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_BACKUP_NOTIFICATION_ADDRESS=you@example.com
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
@@ -51,7 +53,6 @@ MAIL_REPLYTO_NAME='Snipe-IT'
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
@@ -62,6 +63,15 @@ COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
@@ -69,7 +79,20 @@ SECURE_COOKIES=false
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
CACHE_PREFIX=snipeit
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=null
REDIS_PASSWORD=null
REDIS_PORT=null
# --------------------------------------------
# OPTIONAL: MEMCACHED SETTINGS
# --------------------------------------------
MEMCACHED_HOST=null
MEMCACHED_PORT=null
# --------------------------------------------
# OPTIONAL: AWS S3 SETTINGS
@@ -89,7 +112,11 @@ LOGIN_LOCKOUT_DURATION=60
# OPTIONAL: MISC
# --------------------------------------------
APP_LOG=single
APP_LOG_MAX_FILES=10
APP_LOCKED=false
FILESYSTEM_DISK=local
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
APP_CIPHER=AES-256-CBC
GOOGLE_MAPS_API=
BACKUP_ENV=true
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600

View File

@@ -3,7 +3,7 @@
# --------------------------------------------
APP_ENV=testing
APP_DEBUG=true
APP_KEY=ChangeMe
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
APP_URL=http://localhost:8000
APP_TIMEZONE='US/Pacific'
APP_LOCALE=en
@@ -21,7 +21,7 @@ DB_PASSWORD=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_DRIVER=smtp
MAIL_DRIVER=log
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=YOURUSERNAME
@@ -69,3 +69,4 @@ SECURE_COOKIES=false
# OPTIONAL: APP LOG FORMAT
# --------------------------------------------
APP_LOG=single
APP_LOG_LEVEL=debug

View File

@@ -15,13 +15,13 @@ FILESYSTEM_DISK=local
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=snipeit_unit
DB_USERNAME=travis
DB_USERNAME=root
DB_PASSWORD=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_DRIVER=smtp
MAIL_DRIVER=log
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=YOURUSERNAME

View File

@@ -1,9 +1,22 @@
APP_ENV=local
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=mysql
DB_DEFAULT=mysql
DB_HOST=localhost
DB_DATABASE=snipeittests
DB_USERNAME=snipeit
DB_PASSWORD=snipe
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# (LOGIN_LOCKOUT_DURATIONin minutes)
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=1000000
LOGIN_LOCKOUT_DURATION=100000000
MAIL_DRIVER=log
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME=Snipe-IT

19
.env.unit-tests Normal file
View File

@@ -0,0 +1,19 @@
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=sqlite_testing
DB_DEFAULT=sqlite_testing
DB_HOST=localhost
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# (LOGIN_LOCKOUT_DURATIONin minutes)
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=1000000
LOGIN_LOCKOUT_DURATION=100000000
MAIL_DRIVER=log
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME=Snipe-IT

22
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,22 @@
# Code ownership for pull request reviews, per
# the feature detailed here:
# https://github.com/blog/2392-introducing-code-owners
# https://help.github.com/articles/about-codeowners/
# These owners will be the default owners for everything in the repo.
* @snipe
# Order is important. The last matching pattern has the most precedence.
# So if a pull request only touches javascript files, only these owners
# will be requested to review.
# For example:
# *.js @octocat @github/js
app/Importer/* @dmeltzer
app/Http/Controllers/CustomFields* @uberbrady
app/Http/Controllers/Api/CustomFields* @uberbrady
resources/views/custom_fields/* @uberbrady
docker/* @uberbrady

5
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# You can add one username per supported platform and one custom link
# patreon: # Replace with your Patreon username
# open_collective: # Replace with your Open Collective username
# ko_fi: # Replace with your Ko-fi username
custom: https://snipeitapp.com/donate

View File

@@ -12,20 +12,27 @@
#### Please confirm you have done the following before posting your bug report:
- [ ] I have enabled debug mode
- [ ] I have enabled debug mode
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
-----
#### Please provide answers to these questions before posting your bug report:
#### Provide answers to these questions:
- Is this a fresh install or an upgrade?
- Version of Snipe-IT you're running
- Version of PHP you're running
- Version of MySQL/MariaDB you're running
- What OS and web server you're running Snipe-IT on
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the error
- If a stacktrace is provided in the error, include that too.
- Any errors that appear in your browser's error console.
- Confirm whether the error is [reproduceable on the demo](https://snipeitapp.com/demo).
- Include any additional information you can find in `app/storage/logs` and your webserver's logs.
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
- Include any additional information you can find in `storage/logs` and your webserver's logs.
- Include what you've done so far in the installation, and if you got any error messages along the way.
- Indicate whether or not you've manually edited any data directly in the database
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.
https://snipe-it.readme.io/docs/getting-help

61
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@@ -0,0 +1,61 @@
---
name: Bug report
about: Create a report to help us improve
---
#### Please confirm you have done the following before posting your bug report:
- [ ] I have enabled debug mode
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Error Messages**
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
- If a stacktrace is provided in the error, include that too.
- Any errors that appear in your browser's error console.
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
- Include any additional information you can find in `storage/logs` and your webserver's logs.
**Additional context**
- Is this a fresh install or an upgrade?
- What OS and web server you're running Snipe-IT on
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
- Include what you've done so far in the installation, and if you got any error messages along the way.
- Indicate whether or not you've manually edited any data directly in the database
Add any other context about the problem here.
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.

View File

@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

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

43
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- :woman_technologist: ready for dev
- :moneybag: bounty
- :hand: bug
- "🔐 security"
- "👩‍💻 ready for dev"
- "💰 bounty"
- "✋ bug"
exemptMilestones: true
# Label to use when marking an issue as stale
staleLabel: stale
only: issues
# Comment to post when removing the stale label.
unmarkComment: >
Okay, it looks like this issue or feature request might still be important. We'll re-open
it for now. Thank you for letting us know!
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Is this still relevant? We haven't heard from anyone in a bit. If so,
please comment with any updates or additional detail.
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Don't
take it personally, we just need to keep a handle on things. Thank you
for your contributions!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed because it has not had
recent activity. If you believe this is still an issue, please confirm that
this issue is still happening in the most recent version of Snipe-IT and reply
to this thread to re-open it.

1
.github/travis-memory.ini vendored Normal file
View File

@@ -0,0 +1 @@
memory_limit= 2048M

11
.gitignore vendored
View File

@@ -27,8 +27,16 @@ public/uploads/logo.png
public/uploads/logo.svg
public/uploads/models/*
public/uploads/suppliers/*
public/uploads/accessories/*
public/uploads/locations/*
public/uploads/manufacturers/*
public/uploads/components/*
public/uploads/consumables/*
public/uploads/companies/*
public/uploads/categories/*
public/uploads/users/*
storage/app/private_uploads/users/*
public/uploads/departments/*
storage/debugbar/
storage/dumps/*
storage/laravel-backups
@@ -40,3 +48,6 @@ tests/_support/_generated/*
/npm-debug.log
/storage/oauth-private.key
/storage/oauth-public.key
*.cache
/public/storage

1
.nvmrc Normal file
View File

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

View File

@@ -1,4 +1,7 @@
addons:
code_climate:
repo_token:
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
hosts:
- localhost
sudo: false
@@ -6,26 +9,39 @@ sudo: false
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php
services:
- mysql
# list any PHP version you want to test against
php:
- 5.6
- 7.0
- 7.1.2
- 7.2
- 7.3
matrix:
allow_failures:
- php: 7.3
# execute any number of scripts before the test run, custom env's are available as variables
before_script:
- phpenv config-add .github/travis-memory.ini
- phantomjs --webdriver=4444 &
- sleep 10
- mysql -e "create database IF NOT EXISTS snipeit_unit;" -utravis
- sleep 4
- mysql -e 'CREATE DATABASE snipeit_unit;'
- mysql -e 'CREATE USER "travis'@'localhost";'
- mysql -e 'GRANT ALL PRIVILEGES ON * . * TO "travis'@'localhost";'
- mysql -e 'FLUSH PRIVILEGES;'
- composer self-update
- composer install -n --prefer-source
- cp .env.testing-ci .env
- chmod -R 777 storage
- php artisan migrate --database=mysql --force
- php artisan migrate --env=testing-ci --database=mysql --force
- ./vendor/bin/codecept build
- php artisan key:generate
- php artisan db:seed --database=mysql --force
- php artisan snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
- php artisan serve --port=8000 --host=localhost &
- php artisan --env=testing-ci key:generate
- php artisan --env=testing-ci snipeit:travisci-install
- php artisan --env=testing-ci db:seed --database=mysql --force
- php artisan --env=testing-ci snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
- php artisan --env=testing-ci passport:install
- php artisan serve --env=testing-ci --port=8000 --host=localhost &
- sleep 5
- pip install --user codecov
- sleep 5
@@ -34,16 +50,24 @@ before_script:
# omitting "script:" will default to phpunit
# use the $DB env variable to determine the phpunit.xml to use
# script: ./vendor/bin/codecept run --env testing-ci - broken :(
script: ./vendor/bin/codecept run unit --env testing-ci
#script: ./vendor/bin/codecept run
# script: ./vendor/bin/codecept run --env testing-ci
script:
- ./vendor/bin/codecept run unit
# - ./vendor/bin/codecept run acceptance --env=testing-ci
- ./vendor/bin/codecept run functional --env=functional-travis -g func1
- ./vendor/bin/codecept run functional --env=functional-travis -g func2
- ./vendor/bin/codecept run api --env=functional-travis
after_script:
- vendor/bin/test-reporter
after_success:
- codecov
after_failure:
- cat tests/_output/AccessoriesCept.fail.html
- cat tests/_output/*.fail.html
- curl http://localhost:8000/login
- cat storage/logs/laravel.log
# configure notifications (email, IRC, campfire etc)
notifications:

View File

@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
reported by contacting the project team at abuse@snipeitapp.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.

6
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,6 @@
### Contributing
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.

View File

@@ -1,29 +1,38 @@
FROM debian:jessie-slim
MAINTAINER Brady Wetherington <uberbrady@gmail.com>
FROM ubuntu:xenial
LABEL maintainer="uberbrady, hinchk"
RUN apt-get update && apt-get install -y software-properties-common
RUN LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php
RUN apt-get update && apt-get install -y \
apache2 \
apache2-bin \
libapache2-mod-php5 \
php5-curl \
php5-ldap \
php5-mysql \
php5-mcrypt \
php5-gd \
libapache2-mod-php7.1 \
php7.1-curl \
php7.1-ldap \
php7.1-mysql \
php7.1-mcrypt \
php7.1-gd \
php7.1-xml \
php7.1-mbstring \
php7.1-zip \
php7.1-bcmath \
patch \
curl \
vim \
git \
mysql-client \
supervisor \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN php5enmod mcrypt
RUN php5enmod gd
RUN phpenmod mcrypt
RUN phpenmod gd
RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php5/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php5/cli/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.1/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.1/cli/php.ini
RUN useradd --uid 1000 --gid 50 docker
RUN useradd -m --uid 1000 --gid 50 docker
RUN echo export APACHE_RUN_USER=docker >> /etc/apache2/envvars
RUN echo export APACHE_RUN_GROUP=staff >> /etc/apache2/envvars
@@ -57,9 +66,13 @@ RUN chown -R docker /var/www/html
RUN \
rm -r "/var/www/html/storage/private_uploads" && ln -fs "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" \
&& mkdir -p "/var/lib/snipeit/data/uploads/{assets,avatars,barcodes,models,suppliers}" \
&& rm -rf "/var/www/html/public/uploads" && ln -fs "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" \
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups"
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
&& mkdir "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
&& chown docker "/var/lib/snipeit/keys/" \
&& chmod +x /var/www/html/artisan \
&& echo "Finished setting up application in /var/www/html"
############## DEPENDENCIES via COMPOSER ###################
@@ -67,7 +80,9 @@ RUN \
RUN cd /tmp;curl -sS https://getcomposer.org/installer | php;mv /tmp/composer.phar /usr/local/bin/composer
# Get dependencies
RUN cd /var/www/html;composer install
USER docker
RUN cd /var/www/html;composer install && rm -rf /home/docker/.composer/cache
USER root
############### APPLICATION INSTALL/INIT #################
@@ -84,10 +99,11 @@ VOLUME ["/var/lib/snipeit"]
##### START SERVER
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
COPY docker/startup.sh docker/supervisord.conf /
COPY docker/supervisor-exit-event-listener /usr/bin/supervisor-exit-event-listener
RUN chmod +x /startup.sh /usr/bin/supervisor-exit-event-listener
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/startup.sh"]
EXPOSE 80
EXPOSE 443

View File

@@ -1,15 +1,16 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=develop)](https://travis-ci.org/snipe/snipe-it) [![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready+for+dev&amp;title=Ready+for+development)](http://waffle.io/snipe/snipe-it) [![Maintenance](https://img.shields.io/maintenance/yes/2016.svg)]() [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.png)](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/snipeyhead.svg?style=social)](https://twitter.com/snipeyhead) [![Zenhub](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.io) [![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)
[![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-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
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
It is built on [Laravel 5.2](http://laravel.com).
It is built on [Laravel 5.5](http://laravel.com).
Snipe-IT is actively developed and we're [releasing quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
__This is web-based software__. This means there there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
__This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
-----
@@ -21,12 +22,14 @@ If you're having trouble with the installation, please check the [Common Issues]
-----
### User's Manual
For help using Snipe-IT, check out the [user's manual](https://snipe-it-manual.readme.io/docs).
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
-----
### Bug Reports & Feature Requests
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open and closed) to see if your question hasn't already been answered before opening a new issue.
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
**PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
-----
@@ -47,13 +50,72 @@ Please see the [translations documentation](https://snipe-it.readme.io/docs/tran
-----
### Contributing
### Libraries, Modules & Related Projects
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing).
Since the release of the JSON REST API, several third-party developers have been developing modules and libraries to work with Snipe-IT.
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [InQRy](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
- [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
- [Snipe-IT plugin for Jira Service Desk (beta)](https://marketplace.atlassian.com/apps/1220379/snipe-it-for-jira-service-desk-beta?hosting=cloud&tab=overview) - for the upcoming Snipe-IT v5 only
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
-----
### Security
To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.
-----
### Contributors
Thanks goes to all of these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)) who have helped Snipe-IT get this far:
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/5547470?v=3" width="110px;"/><br /><sub>Hereward</sub>](https://github.com/thehereward)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thehereward "Code") | [<img src="https://avatars0.githubusercontent.com/u/5802977?v=3" width="110px;"/><br /><sub>swoopdk</sub>](https://github.com/swoopdk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=swoopdk "Code") | [<img src="https://avatars1.githubusercontent.com/u/3470403?v=3" width="110px;"/><br /><sub>Abdullah Alansari</sub>](https://linkedin.com/in/ahimta)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Ahimta "Code") | [<img src="https://avatars0.githubusercontent.com/u/796443?v=3" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MicaelRodrigues "Code") | [<img src="https://avatars0.githubusercontent.com/u/614564?v=3" width="110px;"/><br /><sub>Patrick Gallagher</sub>](http://macadmincorner.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=patgmac "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/7165922?v=3" width="110px;"/><br /><sub>Miliamber</sub>](https://github.com/Miliamber)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Miliamber "Code") | [<img src="https://avatars3.githubusercontent.com/u/861766?v=3" width="110px;"/><br /><sub>hawk554</sub>](https://github.com/hawk554)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hawk554 "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1695622?v=3" width="110px;"/><br /><sub>Justin Kerr</sub>](http://jbirdkerr.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbirdkerr "Code") | [<img src="https://avatars3.githubusercontent.com/u/11426176?v=3" width="110px;"/><br /><sub>Ira W. Snyder</sub>](http://www.irasnyder.com/devel/)<br />[📖](https://github.com/snipe/snipe-it/commits?author=irasnyd "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2475759?v=3" width="110px;"/><br /><sub>Aladin Alaily</sub>](https://github.com/aalaily)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aalaily "Code") | [<img src="https://avatars0.githubusercontent.com/u/10247644?v=3" width="110px;"/><br /><sub>Chase Hansen</sub>](https://github.com/kobie-chasehansen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kobie-chasehansen "Code") [💬](#question-kobie-chasehansen "Answering Questions") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Akobie-chasehansen "Bug reports") | [<img src="https://avatars2.githubusercontent.com/u/13545400?v=3" width="110px;"/><br /><sub>IDM Helpdesk</sub>](https://github.com/IDM-Helpdesk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=IDM-Helpdesk "Code") | [<img src="https://avatars2.githubusercontent.com/u/614439?v=3" width="110px;"/><br /><sub>Kai</sub>](http://balticer.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balticer "Code") | [<img src="https://avatars1.githubusercontent.com/u/8762511?v=3" width="110px;"/><br /><sub>Michael Daniels</sub>](http://www.michaeldaniels.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mdaniels5757 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/1532660?v=3" width="110px;"/><br /><sub>Tom Castleman</sub>](http://tomcastleman.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tomcastleman "Code") | [<img src="https://avatars3.githubusercontent.com/u/10723243?v=3" width="110px;"/><br /><sub>Daniel Nemanic</sub>](https://github.com/DanielNemanic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielNemanic "Code") | [<img src="https://avatars0.githubusercontent.com/u/150648?v=3" width="110px;"/><br /><sub>SouthWolf</sub>](https://github.com/southwolf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=southwolf "Code") | [<img src="https://avatars2.githubusercontent.com/u/131616?v=3" width="110px;"/><br /><sub>Ivar Nesje</sub>](https://github.com/ivarne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ivarne "Code") | [<img src="https://avatars1.githubusercontent.com/u/62333?v=3" width="110px;"/><br /><sub>Jérémy Benoist</sub>](http://www.j0k3r.net)<br />[📖](https://github.com/snipe/snipe-it/commits?author=j0k3r "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/724344?v=3" width="110px;"/><br /><sub>Chris Leathley</sub>](https://github.com/cleathley)<br />[🚇](#infra-cleathley "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars0.githubusercontent.com/u/972498?v=3" width="110px;"/><br /><sub>splaer</sub>](https://github.com/splaer)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asplaer "Bug reports") [💻](https://github.com/snipe/snipe-it/commits?author=splaer "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/967362?v=3" width="110px;"/><br /><sub>Joe Ferguson</sub>](http://www.joeferguson.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=svpernova09 "Code") | [<img src="https://avatars3.githubusercontent.com/u/6108682?v=3" width="110px;"/><br /><sub>diwanicki</sub>](https://github.com/diwanicki)<br />[💻](https://github.com/snipe/snipe-it/commits?author=diwanicki "Code") [📖](https://github.com/snipe/snipe-it/commits?author=diwanicki "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/2527115?v=3" width="110px;"/><br /><sub>Lee Thoong Ching</sub>](https://github.com/pakkua80)<br />[📖](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Documentation") [💻](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Code") | [<img src="https://avatars1.githubusercontent.com/u/461491?v=3" width="110px;"/><br /><sub>Marek Šuppa</sub>](http://shu.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrshu "Code") | [<img src="https://avatars1.githubusercontent.com/u/8693762?v=3" width="110px;"/><br /><sub>Juan J. Martinez</sub>](https://github.com/mizar1616)<br />[🌍](#translation-mizar1616 "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1458388?v=3" width="110px;"/><br /><sub>R Ryan Dial</sub>](https://github.com/rrdial)<br />[🌍](#translation-rrdial "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2871745?v=3" width="110px;"/><br /><sub>Andrej Manduch</sub>](https://github.com/burlito)<br />[📖](https://github.com/snipe/snipe-it/commits?author=burlito "Documentation") |
| [<img src="https://avatars0.githubusercontent.com/u/8341172?v=3" width="110px;"/><br /><sub>Jay Richards</sub>](http://www.cordeos.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=technogenus "Code") | [<img src="https://avatars2.githubusercontent.com/u/7295127?v=3" width="110px;"/><br /><sub>Alexander Innes</sub>](https://necurity.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leostat "Code") | [<img src="https://avatars2.githubusercontent.com/u/334485?v=3" width="110px;"/><br /><sub>Danny Garcia</sub>](https://buzzedword.codes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=buzzedword "Code") | [<img src="https://avatars2.githubusercontent.com/u/366855?v=3" width="110px;"/><br /><sub>archpoint</sub>](https://github.com/archpoint)<br />[💻](https://github.com/snipe/snipe-it/commits?author=archpoint "Code") | [<img src="https://avatars1.githubusercontent.com/u/67991?v=3" width="110px;"/><br /><sub>Jake McGraw</sub>](http://www.jakemcgraw.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jakemcgraw "Code") | [<img src="https://avatars1.githubusercontent.com/u/1714374?v=3" width="110px;"/><br /><sub>FleischKarussel</sub>](https://github.com/FleischKarussel)<br />[📖](https://github.com/snipe/snipe-it/commits?author=FleischKarussel "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/319644?v=3" width="110px;"/><br /><sub>Dylan Yi</sub>](https://github.com/feeva)<br />[💻](https://github.com/snipe/snipe-it/commits?author=feeva "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/857740?v=3" width="110px;"/><br /><sub>Gil Rutkowski</sub>](http://FlashingCursor.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [<img src="https://avatars3.githubusercontent.com/u/129360?v=3" width="110px;"/><br /><sub>Desmond Morris</sub>](http://www.desmondmorris.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [<img src="https://avatars2.githubusercontent.com/u/52936?v=3" width="110px;"/><br /><sub>Nick Peelman</sub>](http://peelman.us)<br />[💻](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [<img src="https://avatars0.githubusercontent.com/u/53161?v=3" width="110px;"/><br /><sub>Abraham Vegh</sub>](https://abrahamvegh.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [<img src="https://avatars0.githubusercontent.com/u/2818680?v=3" width="110px;"/><br /><sub>Mohamed Rashid</sub>](https://github.com/rashivkp)<br />[📖](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1509456?v=3" width="110px;"/><br /><sub>Kasey</sub>](http://hinchk.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [<img src="https://avatars2.githubusercontent.com/u/10522541?v=3" width="110px;"/><br /><sub>Brett</sub>](https://github.com/BrettFagerlund)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") |
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
| [<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!
-----
### Contributing
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.

84
Vagrantfile vendored Normal file
View File

@@ -0,0 +1,84 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
Vagrant.configure("2") do |config|
config.vm.define "bionic" do |bionic|
bionic.vm.box = "ubuntu/bionic64"
bionic.vm.hostname = 'bionic'
bionic.vm.network "public_network", bridge: NETWORK_BRIDGE
bionic.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
bionic.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "xenial" do |xenial|
xenial.vm.box = "ubuntu/xenial64"
xenial.vm.hostname = 'xenial'
xenial.vm.network "public_network", bridge: NETWORK_BRIDGE
xenial.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
xenial.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty32"
trusty.vm.hostname = 'trusty'
trusty.vm.network "public_network", bridge: NETWORK_BRIDGE
trusty.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
trusty.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "centos7" do |centos7|
centos7.vm.box = "centos/7"
centos7.vm.hostname = 'centos7'
centos7.vm.network "public_network", bridge: NETWORK_BRIDGE
centos7.vm.provision :shell, :inline => "sudo yum -y update"
centos7.vm.provision :shell, :inline => "yum install -y wget"
centos7.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
centos7.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "centos6" do |centos6|
centos6.vm.box = "centos/6"
centos6.vm.hostname = 'centos6'
centos6.vm.network "public_network", bridge: NETWORK_BRIDGE
centos6.vm.provision :shell, :inline => "sudo yum -y update"
centos6.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
centos6.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "jessie" do |jessie|
jessie.vm.box = "debian/jessie64"
jessie.vm.hostname = 'debian8'
jessie.vm.network "public_network", bridge: NETWORK_BRIDGE
jessie.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
jessie.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "stretch" do |stretch|
stretch.vm.box = "debian/stretch64"
stretch.vm.hostname = 'debian9'
stretch.vm.network "public_network", bridge: NETWORK_BRIDGE
stretch.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
stretch.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "fedora27" do |fedora27|
fedora27.vm.box = "fedora/27-cloud-base"
fedora27.vm.hostname = 'fedora27'
fedora27.vm.network "public_network", bridge: NETWORK_BRIDGE
fedora27.vm.provision :shell, :inline => "dnf -y install wget"
fedora27.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
fedora27.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "fedora26" do |fedora26|
fedora26.vm.box = "fedora/26-cloud-base"
fedora26.vm.hostname = 'fedora26'
fedora26.vm.network "public_network", bridge: NETWORK_BRIDGE
fedora26.vm.provision :shell, :inline => "dnf -y install wget"
fedora26.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
fedora26.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
end

View File

@@ -1,463 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use App\Models\Location;
use App\Models\Category;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\Asset;
use App\Models\Manufacturer;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Illuminate\Console\Command;
use League\Csv\Reader;
class AssetImportCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'snipeit:asset-import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import Assets from CSV';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function fire()
{
$filename = $this->argument('filename');
if (!$this->option('testrun')=='true') {
$this->comment('======= Importing Assets from '.$filename.' =========');
} else {
$this->comment('====== TEST ONLY Asset Import for '.$filename.' ====');
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
}
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$csv = Reader::createFromPath($this->argument('filename'));
$csv->setNewline("\r\n");
$csv->setOffset(1);
$duplicates = '';
// Loop through the records
$nbInsert = $csv->each(function ($row) use ($duplicates) {
$status_id = 1;
// Let's just map some of these entries to more user friendly words
// User's name
if (array_key_exists('0', $row)) {
$user_name = trim($row[0]);
} else {
$user_name = '';
}
// User's email
if (array_key_exists('1', $row)) {
$user_email = trim($row[1]);
} else {
$user_email = '';
}
// User's email
if (array_key_exists('2', $row)) {
$user_username = trim($row[2]);
} else {
$user_username = '';
}
// Asset Name
if (array_key_exists('3', $row)) {
$user_asset_asset_name = trim($row[3]);
} else {
$user_asset_asset_name = '';
}
// Asset Category
if (array_key_exists('4', $row)) {
$user_asset_category = trim($row[4]);
} else {
$user_asset_category = '';
}
// Asset Name
if (array_key_exists('5', $row)) {
$user_asset_name = trim($row[5]);
} else {
$user_asset_name = '';
}
// Asset Manufacturer
if (array_key_exists('6', $row)) {
$user_asset_mfgr = trim($row[6]);
} else {
$user_asset_mfgr = '';
}
// Asset model number
if (array_key_exists('7', $row)) {
$user_asset_modelno = trim($row[7]);
} else {
$user_asset_modelno = '';
}
// Asset serial number
if (array_key_exists('8', $row)) {
$user_asset_serial = trim($row[8]);
} else {
$user_asset_serial = '';
}
// Asset tag
if (array_key_exists('9', $row)) {
$user_asset_tag = trim($row[9]);
} else {
$user_asset_tag = '';
}
// Asset location
if (array_key_exists('10', $row)) {
$user_asset_location = trim($row[10]);
} else {
$user_asset_location = '';
}
// Asset notes
if (array_key_exists('11', $row)) {
$user_asset_notes = trim($row[11]);
} else {
$user_asset_notes = '';
}
// Asset purchase date
if (array_key_exists('12', $row)) {
if ($row[12]!='') {
$user_asset_purchase_date = date("Y-m-d 00:00:01", strtotime($row[12]));
} else {
$user_asset_purchase_date = '';
}
} else {
$user_asset_purchase_date = '';
}
// Asset purchase cost
if (array_key_exists('13', $row)) {
if ($row[13]!='') {
$user_asset_purchase_cost = trim($row[13]);
} else {
$user_asset_purchase_cost = '';
}
} else {
$user_asset_purchase_cost = '';
}
// Asset Company Name
if (array_key_exists('14', $row)) {
if ($row[14]!='') {
$user_asset_company_name = trim($row[14]);
} else {
$user_asset_company_name= '';
}
} else {
$user_asset_company_name = '';
}
// A number was given instead of a name
if (is_numeric($user_name)) {
$this->comment('User '.$user_name.' is not a name - assume this user already exists');
$user_username = '';
$first_name = '';
$last_name = '';
// No name was given
} elseif ($user_name=='') {
$this->comment('No user data provided - skipping user creation, just adding asset');
$first_name = '';
$last_name = '';
//$user_username = '';
} else {
$user_email_array = User::generateFormattedNameFromFullName($this->option('email_format'), $user_name);
$first_name = $user_email_array['first_name'];
$last_name = $user_email_array['last_name'];
if ($user_email=='') {
$user_email = $user_email_array['username'].'@'.config('app.domain');
}
if ($user_username=='') {
if ($this->option('username_format')=='email') {
$user_username = $user_email;
} else {
$user_name_array = User::generateFormattedNameFromFullName($this->option('username_format'), $user_name);
$user_username = $user_name_array['username'];
}
}
}
$this->comment('Full Name: '.$user_name);
$this->comment('First Name: '.$first_name);
$this->comment('Last Name: '.$last_name);
$this->comment('Username: '.$user_username);
$this->comment('Email: '.$user_email);
$this->comment('Category Name: '.$user_asset_category);
$this->comment('Item: '.$user_asset_name);
$this->comment('Manufacturer ID: '.$user_asset_mfgr);
$this->comment('Model No: '.$user_asset_modelno);
$this->comment('Serial No: '.$user_asset_serial);
$this->comment('Asset Tag: '.$user_asset_tag);
$this->comment('Location: '.$user_asset_location);
$this->comment('Purchase Date: '.$user_asset_purchase_date);
$this->comment('Purchase Cost: '.$user_asset_purchase_cost);
$this->comment('Notes: '.$user_asset_notes);
$this->comment('Company Name: '.$user_asset_company_name);
$this->comment('------------- Action Summary ----------------');
if ($user_username!='') {
if ($user = User::MatchEmailOrUsername($user_username, $user_email)
->whereNotNull('username')->first()) {
$this->comment('User '.$user_username.' already exists');
} else {
$user = new \App\Models\User;
$password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$user->first_name = $first_name;
$user->last_name = $last_name;
$user->username = $user_username;
$user->email = $user_email;
$user->permissions = '{user":1}';
$user->password = bcrypt($password);
$user->activated = 1;
if ($user->save()) {
$this->comment('User '.$first_name.' created');
} else {
$this->error('ERROR CREATING User '.$first_name.' '.$last_name);
$this->error($user->getErrors());
}
}
} else {
$user = new User;
}
// Check for the location match and create it if it doesn't exist
if ($location = Location::where('name', e($user_asset_location))->first()) {
$this->comment('Location '.$user_asset_location.' already exists');
} else {
$location = new Location();
if ($user_asset_location!='') {
$location->name = e($user_asset_location);
$location->address = '';
$location->city = '';
$location->state = '';
$location->country = '';
$location->user_id = 1;
if (!$this->option('testrun')=='true') {
if ($location->save()) {
$this->comment('Location '.$user_asset_location.' was created');
} else {
$this->error('Something went wrong! Location '.$user_asset_location.' was NOT created');
$this->error($location->getErrors());
}
} else {
$this->comment('Location '.$user_asset_location.' was (not) created - test run only');
}
} else {
$this->comment('No location given, so none created.');
}
}
if (e($user_asset_category)=='') {
$category_name = 'Unnamed Category';
} else {
$category_name = e($user_asset_category);
}
// Check for the category match and create it if it doesn't exist
if ($category = Category::where('name', e($category_name))->where('category_type', 'asset')->first()) {
$this->comment('Category '.$category_name.' already exists');
} else {
$category = new Category();
$category->name = e($category_name);
$category->category_type = 'asset';
$category->user_id = 1;
if ($category->save()) {
$this->comment('Category '.$user_asset_category.' was created');
} else {
$this->error('Something went wrong! Category '.$user_asset_category.' was NOT created');
$this->error($category->getErrors());
}
}
// Check for the manufacturer match and create it if it doesn't exist
if ($manufacturer = Manufacturer::where('name', e($user_asset_mfgr))->first()) {
$this->comment('Manufacturer '.$user_asset_mfgr.' already exists');
} else {
$manufacturer = new Manufacturer();
$manufacturer->name = e($user_asset_mfgr);
$manufacturer->user_id = 1;
if ($manufacturer->save()) {
$this->comment('Manufacturer '.$user_asset_mfgr.' was created');
} else {
$this->error('Something went wrong! Manufacturer '.$user_asset_mfgr.' was NOT created: '. $manufacturer->getErrors()->first());
}
}
// Check for the asset model match and create it if it doesn't exist
if ($asset_model = AssetModel::where('name', e($user_asset_name))->where('modelno', e($user_asset_modelno))->where('category_id', $category->id)->where('manufacturer_id', $manufacturer->id)->first()) {
$this->comment('The Asset Model '.$user_asset_name.' with model number '.$user_asset_modelno.' already exists');
} else {
$asset_model = new AssetModel();
$asset_model->name = e($user_asset_name);
$asset_model->manufacturer_id = $manufacturer->id;
$asset_model->modelno = e($user_asset_modelno);
$asset_model->category_id = $category->id;
$asset_model->user_id = 1;
if ($asset_model->save()) {
$this->comment('Asset Model '.$user_asset_name.' with model number '.$user_asset_modelno.' was created');
} else {
$this->error('Something went wrong! Asset Model '.$user_asset_name.' was NOT created: '.$asset_model->getErrors()->first());
}
}
// Check for the asset company match and create it if it doesn't exist
if ($user_asset_company_name!='') {
if ($company = Company::where('name', e($user_asset_company_name))->first()) {
$this->comment('Company '.$user_asset_company_name.' already exists');
} else {
$company = new Company();
$company->name = e($user_asset_company_name);
if ($company->save()) {
$this->comment('Company '.$user_asset_company_name.' was created');
} else {
$this->error('Something went wrong! Company '.$user_asset_company_name.' was NOT created: '.$company->getErrors()->first());
}
}
} else {
$company = new Company();
}
// Check for the asset match and create it if it doesn't exist
if ($asset = Asset::where('asset_tag', e($user_asset_tag))->first()) {
$this->comment('The Asset with asset tag '.$user_asset_tag.' already exists');
} else {
$asset = new Asset();
$asset->name = e($user_asset_asset_name);
if ($user_asset_purchase_date!='') {
$asset->purchase_date = $user_asset_purchase_date;
} else {
$asset->purchase_date = null;
}
if ($user_asset_purchase_cost!='') {
$asset->purchase_cost = ParseFloat(e($user_asset_purchase_cost));
} else {
$asset->purchase_cost = 0.00;
}
$asset->serial = e($user_asset_serial);
$asset->asset_tag = e($user_asset_tag);
$asset->model_id = $asset_model->id;
$asset->assigned_to = $user->id;
$asset->rtd_location_id = $location->id;
$asset->user_id = 1;
$asset->status_id = $status_id;
$asset->company_id = $company->id;
if ($user_asset_purchase_date!='') {
$asset->purchase_date = $user_asset_purchase_date;
} else {
$asset->purchase_date = null;
}
$asset->notes = e($user_asset_notes);
if ($asset->save()) {
$this->comment('Asset '.$user_asset_name.' with serial number '.$user_asset_serial.' was created');
} else {
$this->error('Something went wrong! Asset '.$user_asset_name.' was NOT created: '.$asset->getErrors()->first());
}
}
$this->comment('=====================================');
return true;
});
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return array(
array('filename', InputArgument::REQUIRED, 'File for the CSV import.'),
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return array(
array('email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null),
array('username_format', null, InputOption::VALUE_REQUIRED, 'The format of the username that should be generated. Options are firstname.lastname, firstname, filastname, email', null),
array('testrun', null, InputOption::VALUE_REQUIRED, 'Test the output without writing to the database or not.', null),
);
}
}

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

@@ -0,0 +1,85 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class FixDoubleEscape extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:unescape';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This should be run to fix some double-escaping issues from earlier versions of Snipe-IT.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$tables = [
'\App\Models\Asset' => ['name'],
'\App\Models\License' => ['name'],
'\App\Models\Consumable' => ['name'],
'\App\Models\Accessory' => ['name'],
'\App\Models\Component' => ['name'],
'\App\Models\Company' => ['name'],
'\App\Models\Manufacturer' => ['name'],
'\App\Models\Supplier' => ['name'],
'\App\Models\Statuslabel' => ['name'],
'\App\Models\Depreciation' => ['name'],
'\App\Models\AssetModel' => ['name'],
'\App\Models\Group' => ['name'],
'\App\Models\Department' => ['name'],
'\App\Models\Location' => ['name'],
'\App\Models\User' => ['first_name', 'last_name'],
];
$count = array();
foreach ($tables as $classname => $fields) {
$count[$classname] = array();
$count[$classname]['classname'] = 0;
foreach($fields as $field) {
$count[$classname]['classname']++;
$count[$classname][$field] = 0;
foreach($classname::where("$field",'LIKE','%&%')->get() as $row) {
$this->info('Updating '.$field.' for '.$classname);
$row->{$field} = html_entity_decode($row->{$field});
$row->save();
$count[$classname][$field]++;
}
}
}
$this->info('Update complete');
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use League\Csv\Reader;
use App\Models\Location;
class ImportLocations extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:import-locations {filename}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import locations and their parents';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (!ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$filename = $this->argument('filename');
$csv = Reader::createFromPath(storage_path('private_uploads/imports/').$filename, 'r');
$this->info('Attempting to process: '.storage_path('private_uploads/imports/').$filename);
$csv->setHeaderOffset(0); //because we don't want to insert the header
$results = $csv->getRecords();
// Import parent location names first if they don't exist
foreach ($results as $parent_index => $parent_row) {
if (array_key_exists('Parent Name', $parent_row)) {
$parent_name = trim($parent_row['Parent Name']);
if (array_key_exists('Name', $parent_row)) {
$this->info('- Parent: ' . $parent_name . ' in row as: ' . trim($parent_row['Parent Name']));
}
// Save parent location name
// This creates a sort of name-stub that we'll update later on in this script
$parent_location = Location::firstOrCreate(array('name' => $parent_name));
if (array_key_exists('Name', $parent_row)) {
$this->info('Parent for ' . $parent_row['Name'] . ' is ' . $parent_name . '. Attempting to save ' . $parent_name . '.');
}
// Check if the record was updated or created.
// This is mostly for clearer debugging.
if ($parent_location->exists) {
$this->info('- Parent location '.$parent_name.' already exists.');
} else {
$this->info('- Parent location '.$parent_name.' was created.');
}
} else {
$this->info('- No Parent Name provided, so no parent location will be created.');
}
}
$this->info('----- Parents Created.... backfilling additional details... --------');
// Loop through ALL records and add/update them if there are additional fields
// besides name
foreach ($results as $index => $row) {
if (array_key_exists('Parent Name', $row)) {
$parent_name = trim($row['Parent Name']);
}
// Set the location attributes to save
if (array_key_exists('Name', $row)) {
$location = Location::firstOrNew(array('name' => trim($row['Name'])));
$location->name = trim($row['Name']);
$this->info('Checking location: '.$location->name);
} else {
$this->error('Location name is required and is missing from at least one row in this dataset. Check your CSV for extra trailing rows and try again.');
return false;
}
if (array_key_exists('Currency', $row)) {
$location->currency = trim($row['Currency']);
}
if (array_key_exists('Address 1', $row)) {
$location->address = trim($row['Address 1']);
}
if (array_key_exists('Address 2', $row)) {
$location->address2 = trim($row['Address 2']);
}
if (array_key_exists('City', $row)) {
$location->city = trim($row['City']);
}
if (array_key_exists('State', $row)) {
$location->state = trim($row['State']);
}
if (array_key_exists('Zip', $row)) {
$location->zip = trim($row['Zip']);
}
if (array_key_exists('Country', $row)) {
$location->country = trim($row['Country']);
}
if (array_key_exists('Country', $row)) {
$location->ldap_ou = trim($row['OU']);
}
// If a parent name is provided, we created it earlier in the script,
// so let's grab that ID
if ($parent_name) {
$this->info('-- Searching for Parent Name: '.$parent_name);
$parent = Location::where('name', '=', $parent_name)->first();
$location->parent_id = $parent->id;
$this->info('Parent: '.$parent_name.' - ID: '.$parent->id);
}
// Make sure the more advanced (non-name) fields pass validation
if (($location->isValid()) && ($location->save())) {
// Check if the record was updated or created.
// This is mostly for clearer debugging.
if ($location->exists) {
$this->info('Location ' . $location->name . ' already exists. Updating...');
} else {
$this->info('- Location '.$location->name.' was created. ');
}
// If there's a validation error, display that
} else {
$this->error('- Non-parent Location '.$location->name.' could not be created: '.$location->getErrors() );
}
}
}
}

196
app/Console/Commands/LdapSync.php Normal file → Executable file
View File

@@ -16,7 +16,7 @@ class LdapSync extends Command
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--summary}';
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
/**
* The console command description.
@@ -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;
@@ -55,41 +54,116 @@ class LdapSync extends Command
try {
$ldapconn = Ldap::connectToLdap();
} catch (\Exception $e) {
LOG::error($e);
}
try {
Ldap::bindAdminToLdap($ldapconn);
} catch (\Exception $e) {
LOG::error($e);
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
$summary = array();
$results = Ldap::findLdapUsers();
try {
if ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
} else {
$search_base = null;
}
$results = Ldap::findLdapUsers($search_base);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
/* Determine which location to assign users to by default. */
$location = NULL;
if ($this->option('location')!='') {
$location = Location::where('name','=',$this->option('location'))->first();
$location = Location::where('name', '=', $this->option('location'))->first();
LOG::debug('Location name '.$this->option('location').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
} elseif ($this->option('location_id')!='') {
$location = Location::where('id','=',$this->option('location_id'))->first();
$location = Location::where('id', '=', $this->option('location_id'))->first();
LOG::debug('Location ID '.$this->option('location_id').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
} else {
$location = new Location;
}
if (!isset($location)) {
LOG::debug('That location is invalid, so no location will be assigned.');
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
/* Process locations with explicitly defined OUs, if doing a full import. */
if ($this->option('base_dn')=='') {
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
$ldap_ou_lengths = array();
foreach ($ldap_ou_locations as $location) {
$ldap_ou_lengths[] = strlen($location["ldap_ou"]);
}
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
}
// Inject location information fields
for ($i = 0; $i < $results["count"]; $i++) {
$results[$i]["ldap_location_override"] = false;
$results[$i]["location_id"] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
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++) {
if (array_key_exists($ldap_result_username, $location_users[$i])) {
$location_users[$i]["ldap_location_override"] = true;
$location_users[$i]["location_id"] = $ldap_loc["id"];
$usernames[] = $location_users[$i][$ldap_result_username][0];
}
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
}
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
}
/* Create user account entries in Snipe-IT */
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$pass = bcrypt($tmp_pass);
for ($i = 0; $i < $results["count"]; $i++) {
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
@@ -99,30 +173,77 @@ class LdapSync extends Command
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
// User exists
$item["createorupdate"] = 'updated';
if (!$user = User::where('username', $item["username"])->first()) {
$user = User::where('username', $item["username"])->first();
if ($user) {
// Updating an existing user.
$item["createorupdate"] = 'updated';
} else {
// Creating a new user.
$user = new User;
$user->password = $pass;
$user->activated = 0;
$item["createorupdate"] = 'created';
}
// Create the user if they don't exist.
$user->first_name = e($item["firstname"]);
$user->last_name = e($item["lastname"]);
$user->username = e($item["username"]);
$user->email = e($item["email"]);
$user->first_name = $item["firstname"];
$user->last_name = $item["lastname"];
$user->username = $item["username"];
$user->email = $item["email"];
$user->employee_num = e($item["employee_number"]);
$user->activated = 1;
if ($location) {
$user->location_id = e($location->id);
// 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', // 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;
}
// If we're not using AD, and there isn't an activated flag set, activate all users
elseif (empty($ldap_result_active_flag)) {
$user->activated = 1;
}
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (!empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
$user->notes = 'Imported from LDAP';
$user->ldap_import = 1;
$errors = '';
@@ -146,19 +267,16 @@ class LdapSync extends Command
if ($this->option('summary')) {
for ($x = 0; $x < count($summary); $x++) {
if ($summary[$x]['status']=='error') {
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was not imported: '.$summary[$x]['note'] );
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was '.strtoupper($summary[$x]['createorupdate']).'.');
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} else if ($this->option('json_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

@@ -1,383 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use League\Csv\Reader;
use App\Models\User;
use App\Models\Supplier;
use App\Models\License;
use App\Models\LicenseSeat;
class LicenseImportCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'snipeit:license-import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import Licenses from CSV';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function fire()
{
$filename = $this->argument('filename');
if (!$this->option('testrun')=='true') {
$this->comment('======= Importing Licenses from '.$filename.' =========');
} else {
$this->comment('====== TEST ONLY License Import for '.$filename.' ====');
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
}
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$csv = Reader::createFromPath($this->argument('filename'));
$csv->setNewline("\r\n");
$csv->setOffset(1);
$duplicates = '';
// Loop through the records
$nbInsert = $csv->each(function ($row) use ($duplicates) {
$status_id = 1;
// Let's just map some of these entries to more user friendly words
if (array_key_exists('0', $row)) {
$user_name = trim($row[0]);
} else {
$user_name = '';
}
if (array_key_exists('1', $row)) {
$user_email = trim($row[1]);
} else {
$user_email = '';
}
if (array_key_exists('2', $row)) {
$user_username = trim($row[2]);
} else {
$user_username = '';
}
if (array_key_exists('3', $row)) {
$user_license_name = trim($row[3]);
} else {
$user_license_name = '';
}
if (array_key_exists('4', $row)) {
$user_license_serial = trim($row[4]);
} else {
$user_license_serial = '';
}
if (array_key_exists('5', $row)) {
$user_licensed_to_name = trim($row[5]);
} else {
$user_licensed_to_name = '';
}
if (array_key_exists('6', $row)) {
$user_licensed_to_email = trim($row[6]);
} else {
$user_licensed_to_email = '';
}
if (array_key_exists('7', $row)) {
$user_license_seats = trim($row[7]);
} else {
$user_license_seats = '';
}
if (array_key_exists('8', $row)) {
$user_license_reassignable = trim($row[8]);
if ($user_license_reassignable!='') {
if ((strtolower($user_license_reassignable)=='yes') || (strtolower($user_license_reassignable)=='true') || ($user_license_reassignable=='1')) {
$user_license_reassignable = 1;
}
} else {
$user_license_reassignable = 0;
}
} else {
$user_license_reassignable = 0;
}
if (array_key_exists('9', $row)) {
$user_license_supplier = trim($row[9]);
} else {
$user_license_supplier = '';
}
if (array_key_exists('10', $row)) {
$user_license_maintained = trim($row[10]);
if ($user_license_maintained!='') {
if ((strtolower($user_license_maintained)=='yes') || (strtolower($user_license_maintained)=='true') || ($user_license_maintained=='1')) {
$user_license_maintained = 1;
}
} else {
$user_license_maintained = 0;
}
} else {
$user_license_maintained = '';
}
if (array_key_exists('11', $row)) {
$user_license_notes = trim($row[11]);
} else {
$user_license_notes = '';
}
if (array_key_exists('12', $row)) {
if ($row[12]!='') {
$user_license_purchase_date = date("Y-m-d 00:00:01", strtotime($row[12]));
} else {
$user_license_purchase_date = '';
}
} else {
$user_license_purchase_date = 0;
}
// A number was given instead of a name
if (is_numeric($user_name)) {
$this->comment('User '.$user_name.' is not a name - assume this user already exists');
$user_username = '';
// No name was given
} elseif ($user_name=='') {
$this->comment('No user data provided - skipping user creation, just adding license');
$first_name = '';
$last_name = '';
$user_username = '';
} else {
$name = explode(" ", $user_name);
$first_name = $name[0];
$email_last_name = '';
$email_prefix = $first_name;
if (!array_key_exists(1, $name)) {
$last_name='';
$email_last_name = $last_name;
$email_prefix = $first_name;
} else {
$last_name = str_replace($first_name, '', $user_name);
if ($this->option('email_format')=='filastname') {
$email_last_name.=str_replace(' ', '', $last_name);
$email_prefix = $first_name[0].$email_last_name;
} elseif ($this->option('email_format')=='firstname.lastname') {
$email_last_name.=str_replace(' ', '', $last_name);
$email_prefix = $first_name.'.'.$email_last_name;
} elseif ($this->option('email_format')=='firstname') {
$email_last_name.=str_replace(' ', '', $last_name);
$email_prefix = $first_name;
}
}
$user_username = $email_prefix;
// Generate an email based on their name if no email address is given
if ($user_email=='') {
if ($first_name=='Unknown') {
$status_id = 7;
}
$email = strtolower($email_prefix).'@'.$this->option('domain');
$user_email = str_replace("'", '', $email);
}
}
$this->comment('Full Name: '.$user_name);
$this->comment('First Name: '.$first_name);
$this->comment('Last Name: '.$last_name);
$this->comment('Username: '.$user_username);
$this->comment('Email: '.$user_email);
$this->comment('License Name: '.$user_license_name);
$this->comment('Serial No: '.$user_license_serial);
$this->comment('Licensed To Name: '.$user_licensed_to_name);
$this->comment('Licensed To Email: '.$user_licensed_to_email);
$this->comment('Seats: '.$user_license_seats);
$this->comment('Reassignable: '.$user_license_reassignable);
$this->comment('Supplier: '.$user_license_supplier);
$this->comment('Maintained: '.$user_license_maintained);
$this->comment('Notes: '.$user_license_notes);
$this->comment('Purchase Date: '.$user_license_purchase_date);
$this->comment('------------- Action Summary ----------------');
if ($user_username!='') {
if ($user = User::where('username', $user_username)->whereNotNull('username')->first()) {
$this->comment('User '.$user_username.' already exists');
} else {
$user = new \App\Models\User;
$password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$user->first_name = $first_name;
$user->last_name = $last_name;
$user->username = $user_username;
$user->email = $user_email;
$user->permissions = '{user":1}';
$user->password = bcrypt($password);
$user->activated = 1;
if ($user->save()) {
$this->comment('User '.$first_name.' created');
} else {
$this->error('ERROR CREATING User '.$first_name.' '.$last_name);
$this->error($user->getErrors());
}
$this->comment('User '.$first_name.' created');
}
} else {
$user = new User;
$user->user_id = null;
}
// Check for the supplier match and create it if it doesn't exist
if ($supplier = Supplier::where('name', $user_license_supplier)->first()) {
$this->comment('Supplier '.$user_license_supplier.' already exists');
} else {
$supplier = new Supplier();
$supplier->name = e($user_license_supplier);
$supplier->user_id = 1;
if ($supplier->save()) {
$this->comment('Supplier '.$user_license_supplier.' was created');
} else {
$this->comment('Something went wrong! Supplier '.$user_license_supplier.' was NOT created');
}
}
// Add the license
$license = new License();
$license->name = e($user_license_name);
if ($user_license_purchase_date!='') {
$license->purchase_date = $user_license_purchase_date;
} else {
$license->purchase_date = null;
}
$license->serial = e($user_license_serial);
$license->seats = e($user_license_seats);
$license->supplier_id = $supplier->id;
$license->user_id = 1;
if ($user_license_purchase_date!='') {
$license->purchase_date = $user_license_purchase_date;
} else {
$license->purchase_date = null;
}
$license->license_name = $user_licensed_to_name;
$license->license_email = $user_licensed_to_email;
$license->notes = e($user_license_notes);
if ($license->save()) {
$this->comment('License '.$user_license_name.' with serial number '.$user_license_serial.' was created');
$license_seat_created = 0;
for ($x = 0; $x < $user_license_seats; $x++) {
// Create the license seat entries
$license_seat = new LicenseSeat();
$license_seat->license_id = $license->id;
// Only assign the first seat to the user
if ($x==0) {
$license_seat->assigned_to = $user->id;
} else {
$license_seat->assigned_to = null;
}
if ($license_seat->save()) {
$license_seat_created++;
}
}
if ($license_seat_created > 0) {
$this->comment($license_seat_created.' seats were created');
} else {
$this->comment('Something went wrong! NO seats for '.$user_license_name.' were created');
}
} else {
$this->comment('Something went wrong! License '.$user_license_name.' was NOT created');
}
$this->comment('=====================================');
return true;
});
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return array(
array('filename', InputArgument::REQUIRED, 'File for the CSV import.'),
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return array(
array('domain', null, InputOption::VALUE_REQUIRED, 'Email domain for generated email addresses.', null),
array('email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null),
array('testrun', null, InputOption::VALUE_REQUIRED, 'Test the output without writing to the database or not.', null),
);
}
}

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();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,6 @@
namespace App\Console\Commands;
use Illuminate\Console\Command;
use DB;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\AssetModel;
@@ -12,14 +9,18 @@ use App\Models\Category;
use App\Models\Company;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\Department;
use App\Models\Depreciation;
use App\Models\Group;
use App\Models\Import;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Manufacturer;
use App\Models\Statuslabel;
use App\Models\Supplier;
use DB;
use Illuminate\Console\Command;
class PaveIt extends Command
{
@@ -63,6 +64,7 @@ class PaveIt extends Command
Company::getQuery()->delete();
Component::getQuery()->delete();
Consumable::getQuery()->delete();
Department::getQuery()->delete();
Depreciation::getQuery()->delete();
License::getQuery()->delete();
LicenseSeat::getQuery()->delete();
@@ -72,10 +74,12 @@ class PaveIt extends Command
Statuslabel::getQuery()->delete();
Supplier::getQuery()->delete();
Group::getQuery()->delete();
Import::getQuery()->delete();
DB::statement('delete from accessories_users');
DB::statement('delete from asset_logs');
DB::statement('delete from asset_maintenances');
DB::statement('delete from login_attempts');
DB::statement('delete from asset_uploads');
DB::statement('delete from action_logs');
DB::statement('delete from checkout_requests');
@@ -107,6 +111,7 @@ class PaveIt extends Command
\DB::statement('drop table IF EXISTS custom_fields');
\DB::statement('drop table IF EXISTS custom_fieldsets');
\DB::statement('drop table IF EXISTS depreciations');
\DB::statement('drop table IF EXISTS departments');
\DB::statement('drop table IF EXISTS groups');
\DB::statement('drop table IF EXISTS history');
\DB::statement('drop table IF EXISTS components');
@@ -117,6 +122,11 @@ class PaveIt extends Command
\DB::statement('drop table IF EXISTS manufacturers');
\DB::statement('drop table IF EXISTS models');
\DB::statement('drop table IF EXISTS migrations');
\DB::statement('drop table IF EXISTS oauth_access_tokens');
\DB::statement('drop table IF EXISTS oauth_auth_codes');
\DB::statement('drop table IF EXISTS oauth_clients');
\DB::statement('drop table IF EXISTS oauth_personal_access_clients');
\DB::statement('drop table IF EXISTS oauth_refresh_tokens');
\DB::statement('drop table IF EXISTS password_resets');
\DB::statement('drop table IF EXISTS requested_assets');
\DB::statement('drop table IF EXISTS requests');
@@ -126,6 +136,7 @@ class PaveIt extends Command
\DB::statement('drop table IF EXISTS throttle');
\DB::statement('drop table IF EXISTS users_groups');
\DB::statement('drop table IF EXISTS users');
\DB::statement('drop table IF EXISTS imports');
}
}
}

View File

@@ -33,7 +33,7 @@ class Purge extends Command
*
* @var string
*/
protected $description = 'Purge all soft-deleted deleted records in the database. This will rewrite history for items that have been edited, or checked in or out. It will also reqrite history for users associated with deleted items.';
protected $description = 'Purge all soft-deleted deleted records in the database. This will rewrite history for items that have been edited, or checked in or out. It will also rewrite history for users associated with deleted items.';
/**
* Create a new command instance.
@@ -65,7 +65,7 @@ class Purge extends Command
$asset_maintenances = 0;
foreach ($assets as $asset) {
$this->info('- Asset "'.$asset->showAssetName().'" deleted.');
$this->info('- Asset "'.$asset->present()->name().'" deleted.');
$asset_assoc += $asset->assetlog()->count();
$asset->assetlog()->forceDelete();
$asset_maintenances += $asset->assetmaintenances()->count();

View File

@@ -0,0 +1,126 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomField;
use Illuminate\Console\Command;
class ReEncodeCustomFieldNames extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:regenerate-fieldnames';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This utility will regenerate the column names for custom fields. It should typically only be needed when a PHP upgrade changed the behavior of the unicode conversion between versions.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* All three of these things must match for the custom fields system to work as expected:
*
* - what the system thinks the output of $field->convertUnicodeDbSlug() is
* - the actual db_column name in the customfields table
* - the physical column name that was created on the assets table
*
* For some people who upgraded their version of PHP, the unicode converter now behaves
* differently in than it did when their custom fields were first created, specifically as it
* relates to handling slashes, ampersands, etc. This can result in the field names no longer
* matching up, as an older version of the PHP extension simply dropped slashes, etc, while the
* newer version of the PHP extension will convert them to underscores.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm('This will regenerate all of the custom field database fieldnames in your database. THIS WILL CHANGE YOUR SCHEMA AND SHOULD NOT BE DONE WITHOUT MAKING A BACKUP FIRST. Do you wish to continue?'))
{
/** Get all of the custom fields */
$fields = CustomField::get();
$asset_columns = \DB::getSchemaBuilder()->getColumnListing('assets');
$custom_field_columns = array();
/** Loop through the columns on the assets table */
foreach ($asset_columns as $asset_column) {
/** Add ones that start with _snipeit_ to an array for handling */
if (strpos($asset_column, '_snipeit_') === 0) {
/**
* Get the ID of the custom field based on the fieldname.
* For example, in _snipeit_mac_address_1, we grab the 1 because we know
* that's the ID of the custom field that created the column.
* Then use that ID as the array key for use comparing the actual assets field name
* and the db_column value from the custom fields table.
*/
$last_part = substr(strrchr($asset_column, "_snipeit_"), 1);
$custom_field_columns[$last_part] = $asset_column;
}
}
foreach ($fields as $field) {
$this->info($field->name .' ('.$field->id.') column should be '. $field->convertUnicodeDbSlug().'');
/** The assets table has the column it should have, all is well */
if (\Schema::hasColumn('assets', $field->convertUnicodeDbSlug()))
{
$this->info('-- ✓ This field exists - all good');
/**
* There is a mismatch between the fieldname on the assets table and
* what $field->convertUnicodeDbSlug() is *now* expecting.
*/
} else {
$this->warn('-- X Field mismatch: updating... ');
/** Make sure the custom_field_columns array has the ID */
if (array_key_exists($field->id, $custom_field_columns)) {
/**
* Update the asset schema to the corrected fieldname that will be recognized by the
* system elsewhere that we use $field->convertUnicodeDbSlug()
*/
\Schema::table('assets', function($table) use ($custom_field_columns, $field) {
$table->renameColumn($custom_field_columns[$field->id], $field->convertUnicodeDbSlug());
});
$this->warn('-- ✓ Field updated from '.$custom_field_columns[$field->id].' to '.$field->convertUnicodeDbSlug());
} else {
$this->warn('-- X WARNING: There is no field on the assets table ending in '.$field->id.'. This may require more in-depth investigation and may mean the schema was altered manually.');
}
}
/** Update the db_column property in the custom fields table, just in case it doesn't match the other
* things.
*/
$field->db_column = $field->convertUnicodeDbSlug();
$field->save();
}
}
}
}

View File

@@ -0,0 +1,167 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomField;
use Illuminate\Console\Command;
use App\LegacyEncrypter\McryptEncrypter;
use App\Models\Setting;
use App\Models\Asset;
use Illuminate\Support\Facades\Storage;
class RecryptFromMcrypt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows upgrading users to de-encrypt their deprecated mcrypt encrypted fields and re-encrypt them using the current OpenSSL encryption.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Check and see if they have a legacy app key listed in their .env
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
$errors = array();
if (!$legacy_key) {
$this->error('ERROR: You do not have a LEGACY_APP_KEY set in your .env file. Please locate your old APP_KEY and ADD a line to your .env file like: LEGACY_APP_KEY=YOUR_OLD_APP_KEY');
return false;
}
// Do some basic legacy app key length checks
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1])==44)) {
$legacy_key = base64_decode($key_parts[1],true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
}
// Check that the app key is 32 characters
if ($legacy_length_check === true) {
$this->comment('INFO: Your LEGACY_APP_KEY looks correct. Okay to continue.');
} else {
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters or base64 followed by 44 characters for later versions). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
return false;
}
$this->error('================================!!!! WARNING !!!!================================');
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm("Are you SURE you wish to continue?"))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
try {
Storage::disk('local')->put($backup_file, 'APP_KEY: '.config('app.key'));
Storage::disk('local')->append($backup_file, 'LEGACY_APP_KEY: '.$legacy_key);
} catch (\Exception $e) {
$this->info('WARNING: Could not backup app keys');
}
if ($legacy_cipher){
$mcrypter = new McryptEncrypter($legacy_key,$legacy_cipher);
}else{
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_pword=='') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = \Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted','=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
$query = Asset::withTrashed();
foreach ($custom_fields as $custom_field) {
$this->comment('FIELD TO RECRYPT: '.$custom_field->name .' ('.$custom_field->db_column.')');
$query->orWhereNotNull($custom_field->db_column);
}
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$columnName}!='') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = \Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
}
}
$asset->save();
$bar->advance();
}
$bar->finish();
if (count($errors) > 0) {
$this->comment("\n\n");
$this->error("The decrypter encountered some errors: \n");
foreach ($errors as $error) {
$this->error($error);
}
}
}
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Asset;
use App\Models\Setting;
use DB;
use Artisan;
class RegenerateAssetTags extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:regenerate-tags {--start=} {--output= : info|warn|error|all} ';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This utility will regenerate all asset tags. THIS IS DATA-DESTRUCTIVE AND SHOULD BE USED WITH CAUTION. ';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm('This will regenerate all of the asset tags within your system. This action is data-destructive and should be used with caution. Do you wish to continue?'))
{
$output['info'] = [];
$output['warn'] = [];
$output['error'] = [];
$settings = Setting::getSettings();
$start_tag = ($this->option('start')) ? $this->option('start') : (($settings->next_auto_tag_base) ? Setting::getSettings()->next_auto_tag_base : 1) ;
$this->info('Starting at '.$start_tag);
$total_assets = Asset::orderBy('id','asc')->get();
$bar = $this->output->createProgressBar(count($total_assets));
try {
Artisan::call('backup:run');
} catch (\Exception $e) {
$output['error'][] = $e;
}
foreach ($total_assets as $asset) {
$start_tag++;
$output['info'][] = 'Asset tag:'.$asset->asset_tag;
$asset->asset_tag = $settings->auto_increment_prefix.$settings->auto_increment_prefix.$start_tag;
if ($settings->zerofill_count > 0) {
$asset->asset_tag = $settings->auto_increment_prefix.Asset::zerofill($start_tag, $settings->zerofill_count);
}
$output['info'][] = 'New Asset tag:'.$asset->asset_tag;
// Use forceSave here to override model level validation
$asset->forceSave();
}
$bar->finish();
$this->info("\n");
if (($this->option('output')=='all') || ($this->option('output')=='info')) {
foreach ($output['info'] as $key => $output_text) {
$this->info($output_text);
}
}
if (($this->option('output')=='all') || ($this->option('output')=='warn')) {
foreach ($output['warn'] as $key => $output_text) {
$this->warn($output_text);
}
}
if (($this->option('output')=='all') || ($this->option('output')=='error')) {
foreach ($output['error'] as $key => $output_text) {
$this->error($output_text);
}
}
}
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Setting;
use App\Models\User;
class ResetDemoSettings extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:demo-settings';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This will reset the Snipe-IT demo settings back to default. ';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Resetting the demo settings.');
$settings = Setting::first();
$settings->per_page = 20;
$settings->site_name = 'Snipe-IT Asset Management Demo';
$settings->auto_increment_assets = 1;
$settings->logo = 'snipe-logo.png';
$settings->alert_email = 'service@snipe-it.io';
$settings->header_color = null;
$settings->barcode_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 3;
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 1;
$settings->alt_barcode = 'C128';
$settings->skin = '';
$settings->email_domain = 'snipeitapp.com';
$settings->email_format = 'filastname';
$settings->username_format = 'filastname';
$settings->date_display_format = 'D M d, Y';
$settings->time_display_format = 'g:iA';
$settings->thumbnail_max_h = '30';
$settings->locale = 'en';
$settings->version_footer = 'on';
$settings->support_footer = 'on';
$settings->save();
if ($user = User::where('username', '=', 'admin')->first()) {
$user->locale = 'en';
$user->save();
}
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Consumable;
use App\Models\Accessory;
use App\Models\LicenseSeat;
use App\Models\License;
use DB;
use Artisan;
class RestoreDeletedUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:restore-users {--start_date=} {--end_date=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Restore users, and any associated assets and license checkouts.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$start_date = $this->option('start_date');
$end_date = $this->option('end_date');
$asset_totals = 0;
$license_totals = 0;
$user_count = 0;
if (($start_date=='') || ($end_date=='')) {
$this->info('ERROR: All fields are required.');
return false;
}
$users = User::whereBetween('deleted_at', [$start_date, $end_date])->withTrashed()->get();
$this->info('There are '.$users->count().' users deleted between '.$start_date.' and '.$end_date);
$this->warn('Making a backup!');
Artisan::call('backup:run');
foreach ($users as $user) {
$user_count++;
$user_logs = Actionlog::where('target_id', $user->id)->where('target_type',User::class)
->where('action_type','checkout')->with('item')->get();
$this->info($user_count.'. '.$user->username.' ('.$user->id.') was deleted at '.$user->deleted_at. ' and has '.$user_logs->count().' checkouts associated.');
foreach ($user_logs as $user_log) {
$this->info(' * '.$user_log->item_type.': '.$user_log->item->name.' - item_id: '.$user_log->item_id);
if ($user_log->item_type==Asset::class) {
$asset_totals++;
DB::table('assets')
->where('id', $user_log->item_id)
->update(['assigned_to' => $user->id, 'assigned_type'=> User::class]);
$this->info(' ** Asset '.$user_log->item->id.' ('.$user_log->item->asset_tag.') restored to user '.$user->id.'');
} elseif ($user_log->item_type==License::class) {
$license_totals++;
$avail_seat = DB::table('license_seats')->where('license_id','=',$user_log->item->id)
->whereNull('assigned_to')->whereNull('asset_id')->whereBetween('updated_at', [$start_date, $end_date])->first();
if ($avail_seat) {
$this->info(' ** Allocating seat '.$avail_seat->id.' for this License');
DB::table('license_seats')
->where('id', $avail_seat->id)
->update(['assigned_to' => $user->id]);
} else {
$this->warn('ERROR: No available seats for '.$user_log->item->name);
}
}
}
$this->warn('Restoring user '.$user->username.'!');
$user->restore();
}
$this->info($asset_totals.' assets affected');
$this->info($license_totals.' licenses affected');
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Artisan;
use App\Models\CustomField;
use App\Models\Asset;
use App\Models\Setting;
use \Illuminate\Encryption\Encrypter;
class RotateAppKey extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:rotate-key';
/**
* 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()
{
if ($this->confirm("\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ")) {
// Get the existing app_key and ciphers
// We put them in a variable since we clear the cache partway through here.
$old_app_key = config('app.key');
$cipher = config('app.cipher');
// Generate a new one
Artisan::call('key:generate', ['--show' => true]);
$new_app_key = Artisan::output();
// Clear the config cache
Artisan::call('config:clear');
$this->warn('Your app cipher is: '.$cipher);
$this->warn('Your old APP_KEY is: '.$old_app_key);
$this->warn('Your new APP_KEY is: '.$new_app_key);
// Write the new app key to the .env file
$this->writeNewEnvironmentFileWith($new_app_key);
// Manually create an old encrypter instance using the old app key
// and also create a new encrypter instance so we can re-crypt the field
// using the newly generated app key
$oldEncrypter = new Encrypter(base64_decode(substr($old_app_key, 7)), $cipher);
$newEncrypter = new Encrypter(base64_decode(substr($new_app_key, 7)), $cipher);
$fields = CustomField::where('field_encrypted', '1')->get();
foreach ($fields as $field) {
$assets = Asset::whereNotNull($field->db_column)->get();
foreach ($assets as $asset) {
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
$this->line('DECRYPTED: '. $field->db_column);
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
$this->line('ENCRYPTED: '.$field->db_column);
$asset->save();
}
}
// Handle the LDAP password if one is provided
$setting = Setting::first();
if ($setting->ldap_pword!='') {
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
$setting->save();
$this->warn('LDAP password has been re-encrypted.');
}
} else {
$this->info('This operation has been canceled. No changes have been made.');
}
}
/**
* Write a new environment file with the given key.
*
* @param string $key
* @return void
*/
protected function writeNewEnvironmentFileWith($key)
{
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$this->keyReplacementPattern(),
'APP_KEY='.$key,
file_get_contents($this->laravel->environmentFilePath())
));
}
/**
* Get a regex pattern that will match env APP_KEY with any random key.
*
* @return string
*/
protected function keyReplacementPattern()
{
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
return "/^APP_KEY{$escaped}/m";
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\Setting;
use Illuminate\Console\Command;
use App\Notifications\ExpectedCheckinNotification;
use App\Notifications\ExpectedCheckinAdminNotification;
use Carbon\Carbon;
class SendExpectedCheckinAlerts extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'snipeit:expected-checkin';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Check for overdue or upcoming expected checkins.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$settings = Setting::getSettings();
$whenNotify = Carbon::now()->addDays(7);
$assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
$this->info($whenNotify.' is deadline');
$this->info($assets->count().' assets');
foreach ($assets as $asset) {
if ($asset->assigned && $asset->checkedOutToUser()) {
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
}
}
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new \App\Models\Recipients\AlertRecipient($item);
});
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
}
}
}

View File

@@ -6,6 +6,8 @@ use App\Models\Asset;
use App\Models\License;
use App\Models\Setting;
use DB;
use App\Notifications\ExpiringLicenseNotification;
use App\Notifications\ExpiringAssetsNotification;
use Illuminate\Console\Command;
@@ -41,91 +43,44 @@ class SendExpirationAlerts extends Command
*
* @return mixed
*/
public function fire()
public function handle()
{
// Expiring Assets
$expiring_assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
$this->info(count($expiring_assets).' expiring assets');
$asset_data['count'] = count($expiring_assets);
$asset_data['email_content'] ='';
$now = date("Y-m-d");
$settings = Setting::getSettings();
$threshold = $settings->alert_interval;
foreach ($expiring_assets as $asset) {
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
$expires = $asset->warrantee_expires();
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
if ($difference > 30) {
$asset_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
} else {
$asset_data['email_content'] .= '<tr style="background-color:#d9534f;">';
}
$asset_data['email_content'] .= '<td><a href="'.config('app.url').'/hardware/'.e($asset->id).'/view">';
$asset_data['email_content'] .= $asset->showAssetName().'</a></td><td>'.e($asset->asset_tag).'</td>';
$asset_data['email_content'] .= '<td>'.e($asset->warrantee_expires()).'</td>';
$asset_data['email_content'] .= '<td>'.$difference.' '.trans('mail.days').'</td>';
$asset_data['email_content'] .= '<td>'.($asset->supplier ? e($asset->supplier->name) : '').'</td>';
$asset_data['email_content'] .= '<td>'.($asset->assigneduser ? e($asset->assigneduser->fullName()) : '').'</td>';
$asset_data['email_content'] .= '</tr>';
}
// Expiring licenses
$expiring_licenses = License::getExpiringLicenses(Setting::getSettings()->alert_interval);
$this->info(count($expiring_licenses).' expiring licenses');
$license_data['count'] = count($expiring_licenses);
$license_data['email_content'] = '';
foreach ($expiring_licenses as $license) {
$expires = $license->expiration_date;
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
if ($difference > 30) {
$license_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
} else {
$license_data['email_content'] .= '<tr style="background-color:#d9534f;">';
}
$license_data['email_content'] .= '<td><a href="'.config('app.url').'/admin/licenses/'.$license->id.'/view">';
$license_data['email_content'] .= $license->name.'</a></td>';
$license_data['email_content'] .= '<td>'.$license->expiration_date.'</td>';
$license_data['email_content'] .= '<td>'.$difference.' days</td>';
$license_data['email_content'] .= '</tr>';
}
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
if (count($expiring_assets) > 0) {
$this->info('Report sent to '.Setting::getSettings()->alert_email);
\Mail::send('emails.expiring-assets-report', $asset_data, function ($m) {
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Expiring_Assets_Report'));
});
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new \App\Models\Recipients\AlertRecipient($item);
});
// Expiring Assets
$assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
if ($assets->count() > 0) {
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(),
['count' => $assets->count(), 'threshold' => $threshold]));
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
}
if (count($expiring_licenses) > 0) {
$this->info('Report sent to '.Setting::getSettings()->alert_email);
\Mail::send('emails.expiring-licenses-report', $license_data, function ($m) {
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Expiring_Licenses_Report'));
});
// Expiring licenses
$licenses = License::getExpiringLicenses($threshold);
if ($licenses->count() > 0) {
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
}
} else {
if (Setting::getSettings()->alert_email=='') {
echo "Could not send email. No alert email configured in settings. \n";
} elseif (Setting::getSettings()->alerts_enabled!=1) {
echo "Alerts are disabled in the settings. No mail will be sent. \n";
if ($settings->alert_email=='') {
$this->error('Could not send email. No alert email configured in settings');
} elseif ($settings->alerts_enabled!=1) {
$this->info('Alerts are disabled in the settings. No mail will be sent');
}
}

View File

@@ -6,6 +6,7 @@ use App\Models\Setting;
use DB;
use Mail;
use App\Helpers\Helper;
use App\Notifications\InventoryAlert;
use Illuminate\Console\Command;
@@ -42,25 +43,28 @@ class SendInventoryAlerts extends Command
*/
public function handle()
{
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
$settings = Setting::getSettings();
$data['data'] = Helper::checkLowInventory();
$data['count'] = count($data['data']);
if (($settings->alert_email!='') && ($settings->alerts_enabled==1)) {
if (count($data['data']) > 0) {
\Mail::send('emails.low-inventory', $data, function ($m) {
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Low_Inventory_Report'));
$items = Helper::checkLowInventory();
// Send a rollup to the admin, if settings dictate
if (($items) && (count($items) > 0)) {
$this->info(trans_choice('mail.low_inventory_alert', count($items)));
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new \App\Models\Recipients\AlertRecipient($item);
});
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
}
} else {
if (Setting::getSettings()->alert_email=='') {
echo "Could not send email. No alert email configured in settings. \n";
$this->error('Could not send email. No alert email configured in settings');
} elseif (Setting::getSettings()->alerts_enabled!=1) {
echo "Alerts are disabled in the settings. No mail will be sent. \n";
$this->info('Alerts are disabled in the settings. No mail will be sent');
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\License;
use App\Models\Setting;
use App\Notifications\ExpiringAssetsNotification;
use App\Models\Recipients;
use DB;
use Illuminate\Console\Command;
use App\Notifications\SendUpcomingAuditNotification;
use Carbon\Carbon;
class SendUpcomingAuditReport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:upcoming-audits';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send email/slack notifications for upcoming asset audits.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$settings = Setting::getSettings();
if (($settings->alert_email != '') && ($settings->audit_warning_days) && ($settings->alerts_enabled == 1)) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new \App\Models\Recipients\AlertRecipient($item);
});
// Assets due for auditing
$assets = Asset::whereNotNull('next_audit_date')
->DueOrOverdueForAudit($settings)
->orderBy('last_audit_date', 'asc')->get();
if ($assets->count() > 0) {
$this->info(trans_choice('mail.upcoming-audits', $assets->count(),
['count' => $assets->count(), 'threshold' => $settings->audit_warning_days]));
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
$this->info('Audit report sent to '.$settings->alert_email);
} else {
$this->info('No assets to be audited. No report sent.');
}
} elseif ($settings->alert_email=='') {
$this->error('Could not send email. No alert email configured in settings');
} elseif (!$settings->audit_warning_days) {
$this->error('No audit warning days set in Admin Notifications. No mail will be sent.');
} elseif ($settings->alerts_enabled!=1) {
$this->info('Alerts are disabled in the settings. No mail will be sent');
} else {
$this->error('Something went wrong. :( ');
$this->error('Admin Notifications Email Setting: '.$settings->alert_email);
$this->error('Admin Audit Warning Setting: '.$settings->audit_warning_days);
$this->error('Admin Alerts Emnabled: '.$settings->alerts_enabled);
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Asset;
class SyncAssetCounters extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:counter-sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Syncs checkedout, checked in, and requested counters for assets';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$start = microtime(true);
$assets = Asset::withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')
->withTrashed()->get();
if ($assets) {
if ($assets->count() > 0) {
$bar = $this->output->createProgressBar($assets->count());
foreach ($assets as $asset) {
$asset->checkin_counter = (int) $asset->checkins_count;
$asset->checkout_counter = (int) $asset->checkouts_count;
$asset->requests_counter = (int) $asset->user_requests_count;
$asset->unsetEventDispatcher();
$asset->save();
$output['info'][] = 'Asset: ' . $asset->id . ' has ' . $asset->checkin_counter . ' checkins, ' . $asset->checkout_counter . ' checkouts, and ' . $asset->requests_counter . ' requests';
$bar->advance();
}
$bar->finish();
foreach ($output['info'] as $key => $output_text) {
$this->info($output_text);
}
$time_elapsed_secs = microtime(true) - $start;
$this->info('Sync executed in ' . $time_elapsed_secs . ' seconds');
} else {
$this->info('No assets to sync');
}
}
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomField;
use Illuminate\Console\Command;
use App\Models\Asset;
use Illuminate\Support\Facades\Storage;
class SyncAssetLocations extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:sync-asset-locations {--output= : info|warn|error|all} ';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This utility will sync the location_id of assets based on current state. It should not normally be needed, but is a safeguard in case we missed something in the Great Migration when flattening the assets to location relationship.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$output['info'] = [];
$output['warn'] = [];
$output['error'] = [];
$total_assets = Asset::whereNull('deleted_at')->get();
$bar = $this->output->createProgressBar(count($total_assets));
// Unassigned
$rtd_assets = Asset::whereNull('assigned_to')->whereNull('deleted_at')->with('defaultLoc')->get();
$output['info'][] = 'There are '.$rtd_assets->count().' unassigned assets.';
foreach ($rtd_assets as $rtd_asset) {
$output['info'][] = 'Setting Unassigned Asset ' . $rtd_asset->id . ' ('.$rtd_asset->asset_tag.') to location: ' . $rtd_asset->rtd_location_id . " because their default location is: " . $rtd_asset->rtd_location_id;
$rtd_asset->location_id=$rtd_asset->rtd_location_id;
$rtd_asset->unsetEventDispatcher();
$rtd_asset->save();
$bar->advance();
}
$assigned_user_assets = Asset::where('assigned_type','App\Models\User')->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
$output['info'][] = 'There are '.$assigned_user_assets->count().' assets checked out to users.';
foreach ($assigned_user_assets as $assigned_user_asset) {
if (($assigned_user_asset->assignedTo) && ($assigned_user_asset->assignedTo->userLoc)) {
$new_location = $assigned_user_asset->assignedTo->userLoc->id;
$output['info'][] ='Setting User Asset ' . $assigned_user_asset->id . ' ('.$assigned_user_asset->asset_tag.') to ' . $assigned_user_asset->assignedTo->userLoc->name . ' which is id: ' . $new_location;
} else {
$output['warn'][] ='Asset ' . $assigned_user_asset->id . ' ('.$assigned_user_asset->asset_tag.') still has no location! ';
$new_location = $assigned_user_asset->rtd_location_id;
}
$assigned_user_asset->location_id=$new_location;
$assigned_user_asset->unsetEventDispatcher();
$assigned_user_asset->save();
$bar->advance();
}
$assigned_location_assets = Asset::where('assigned_type','App\Models\Location')
->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
$output['info'][] = 'There are '.$assigned_location_assets->count().' assets checked out to locations.';
foreach ($assigned_location_assets as $assigned_location_asset) {
if ($assigned_location_asset->assignedTo) {
$assigned_location_asset->location_id = $assigned_location_asset->assignedTo->id;
$output['info'][] ='Setting Location Assigned asset ' . $assigned_location_asset->id . ' ('.$assigned_location_asset->asset_tag.') that is checked out to '.$assigned_location_asset->assignedTo->name.' (#'.$assigned_location_asset->assignedTo->id.') to location: ' . $assigned_location_asset->assetLoc()->id;
$assigned_location_asset->unsetEventDispatcher();
$assigned_location_asset->save();
} else {
$output['warn'][] ='Asset ' . $assigned_location_asset->id . ' ('.$assigned_location_asset->asset_tag.') did not return a valid associated location - perhaps it was deleted?';
}
$bar->advance();
}
// Assigned to assets
$assigned_asset_assets = Asset::where('assigned_type','App\Models\Asset')
->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
$output['info'][] ='Asset-assigned assets: '.$assigned_asset_assets->count();
foreach ($assigned_asset_assets as $assigned_asset_asset) {
// Check to make sure there aren't any invalid relationships
if ($assigned_asset_asset->assetLoc()) {
$assigned_asset_asset->location_id = $assigned_asset_asset->assetLoc()->id;
$output['info'][] ='Setting Asset Assigned asset ' . $assigned_asset_asset->assetLoc()->id. ' ('.$assigned_asset_asset->asset_tag.') location to: ' . $assigned_asset_asset->assetLoc()->id;
$assigned_asset_asset->unsetEventDispatcher();
$assigned_asset_asset->save();
} else {
$output['warn'][] ='Asset Assigned asset ' . $assigned_asset_asset->id. ' ('.$assigned_asset_asset->asset_tag.') does not seem to have a valid location';
}
$bar->advance();
}
$unlocated_assets = Asset::whereNull("location_id")->whereNull('deleted_at')->get();
$output['info'][] ='Assets still without a location: '.$unlocated_assets->count();
foreach($unlocated_assets as $unlocated_asset) {
$output['warn'][] ='Asset: '.$unlocated_asset->id.' still has no location. ';
$bar->advance();
}
$bar->finish();
$this->info("\n");
if (($this->option('output')=='all') || ($this->option('output')=='info')) {
foreach ($output['info'] as $key => $output_text) {
$this->info($output_text);
}
}
if (($this->option('output')=='all') || ($this->option('output')=='warn')) {
foreach ($output['warn'] as $key => $output_text) {
$this->warn($output_text);
}
}
if (($this->option('output')=='all') || ($this->option('output')=='error')) {
foreach ($output['error'] as $key => $output_text) {
$this->error($output_text);
}
}
}
}

View File

@@ -36,7 +36,7 @@ class SystemBackup extends Command
*
* @return mixed
*/
public function fire()
public function handle()
{
//
$this->call('backup:run');

View File

@@ -0,0 +1,135 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class Version extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'version:update {--branch=master} {--type=patch}';
/**
* 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()
{
$use_branch = $this->option('branch');
$use_type = $this->option('type');
$git_branch = trim(shell_exec('git rev-parse --abbrev-ref HEAD'));
$build_version = trim(shell_exec('git rev-list --count '.$use_branch));
$versionFile = 'config/version.php';
$full_hash_version = str_replace("\n", '', shell_exec('git describe master --tags'));
$version = explode('-', $full_hash_version);
$app_version = $current_app_version = $version[0];
$hash_version = (array_key_exists('2', $version)) ? $version[2] : '';
$prerelease_version = '';
$this->line('Branch is: '.$use_branch);
$this->line('Type is: '.$use_type);
$this->line('Current version is: '.$full_hash_version);
if (count($version)==3) {
$this->line('This does not look like an alpha/beta release.');
} else {
if (array_key_exists('3',$version)) {
$this->line('The current version looks like a beta release.');
$prerelease_version = $version[1];
$hash_version = $version[3];
}
}
$app_version_raw = explode('.', $app_version);
$maj = str_replace('v', '', $app_version_raw[0]);
$min = $app_version_raw[1];
$patch = '';
// This is a major release that might not have a third .0
if (array_key_exists(2, $app_version_raw)) {
$patch = $app_version_raw[2];
}
if ($use_type=='major') {
$app_version = "v".($maj + 1).".$min.$patch";
} elseif ($use_type=='minor') {
$app_version = "v"."$maj.".($min + 1).".$patch";
} elseif ($use_type=='pre') {
$pre_raw = str_replace('beta','', $prerelease_version);
$pre_raw = str_replace('alpha','', $pre_raw);
$pre_raw = str_ireplace('rc','', $pre_raw);
$pre_raw = $pre_raw++;
$this->line('Setting the pre-release to '. $prerelease_version.'-'.$pre_raw);
$app_version = "v"."$maj.".($min + 1).".$patch";
} elseif ($use_type=='patch') {
$app_version = "v" . "$maj.$min." . ($patch + 1);
// If nothing is passed, leave the version as it is, just increment the build
} else {
$app_version = "v" . "$maj.$min." . $patch;
}
// Determine if this tag already exists, or if this prior to a release
$this->line('Running: git rev-parse master '.$current_app_version);
// $pre_release = trim(shell_exec('git rev-parse '.$use_branch.' '.$current_app_version.' 2>&1 1> /dev/null'));
if ($use_branch=='develop') {
$app_version = $app_version.'-pre';
}
$full_app_version = $app_version.' - build '.$build_version.'-'.$hash_version;
$array = var_export(
array(
'app_version' => $app_version,
'full_app_version' => $full_app_version,
'build_version' => $build_version,
'prerelease_version' => $prerelease_version,
'hash_version' => $hash_version,
'full_hash' => $full_hash_version,
'branch' => $git_branch),
true
);
// Construct our file content
$content = <<<CON
<?php
return $array;
CON;
// And finally write the file and output the current version
\File::put($versionFile, $content);
$this->info('Setting NEW version: '. $full_app_version.' ('.$git_branch.')');
}
}

View File

@@ -1,90 +0,0 @@
<?php
namespace App\Console\Commands;
use Symfony\Component\Console\Input\InputArgument;
use Illuminate\Console\Command;
class Versioning extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'versioning:update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate and update app\'s version via git.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$versionFile = 'config/version.php';
$hash_version = str_replace("\n",'',shell_exec('git describe --tags'));
$version = explode('-', $hash_version);
$array = var_export(
array(
'app_version' => $version[0],
'build_version' => $version[1],
'hash_version' => $version[2],
'full_hash' => $hash_version),
true);
// Construct our file content
$content = <<<CON
<?php
return $array;
CON;
// And finally write the file and output the current version
\File::put($versionFile, $content);
$this->line('Setting version: '. config('version.app_version').' build '.config('version.build_version').' ('.config('version.hash_version').')');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return array(
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return array(
);
}
}

View File

@@ -2,29 +2,14 @@
namespace App\Console;
use App\Console\Commands\ImportLocations;
use App\Console\Commands\ReEncodeCustomFieldNames;
use App\Console\Commands\RestoreDeletedUsers;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
Commands\PaveIt::class,
Commands\CreateAdmin::class,
Commands\SendExpirationAlerts::class,
Commands\SendInventoryAlerts::class,
Commands\LicenseImportCommand::class,
Commands\ObjectImportCommand::class,
Commands\Versioning::class,
Commands\SystemBackup::class,
Commands\DisableLDAP::class,
Commands\Purge::class,
Commands\LdapSync::class,
];
/**
* Define the application's command schedule.
@@ -37,7 +22,15 @@ class Kernel extends ConsoleKernel
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
$schedule->command('snipeit:backup')->weekly();
$schedule->command('backup:clean')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
}
protected function commands()
{
require base_path('routes/console.php');
$this->load(__DIR__.'/Commands');
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Exceptions;
use Exception;
class CheckoutNotAllowed extends Exception
{
private $errorMessage;
function __construct($errorMessage = null)
{
$this->errorMessage = $errorMessage;
parent::__construct($errorMessage);
}
public function __toString()
{
return is_null($this->errorMessage) ? "A checkout is not allowed under these circumstances" : $this->errorMessage;
}
}

View File

@@ -3,11 +3,11 @@
namespace App\Exceptions;
use Exception;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use App\Helpers\Helper;
use Illuminate\Validation\ValidationException;
use Log;
class Handler extends ExceptionHandler
{
@@ -17,10 +17,13 @@ class Handler extends ExceptionHandler
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Session\TokenMismatchException::class,
\Illuminate\Validation\ValidationException::class,
\Intervention\Image\Exception\NotSupportedException::class,
];
/**
@@ -28,12 +31,15 @@ class Handler extends ExceptionHandler
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
* @param \Exception $exception
* @return void
*/
public function report(Exception $e)
public function report(Exception $exception)
{
parent::report($e);
if ($this->shouldReport($exception)) {
Log::error($exception);
return parent::report($exception);
}
}
/**
@@ -45,23 +51,74 @@ class Handler extends ExceptionHandler
*/
public function render($request, Exception $e)
{
// CSRF token mismatch error
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
return redirect()->back()->with('error', trans('general.token_expired'));
return redirect()->back()->with('error', trans('general.token_expired'));
}
if ($this->isHttpException($e)) {
$statusCode = $e->getStatusCode();
// Handle Ajax requests that fail because the model doesn't exist
if ($request->ajax() || $request->wantsJson()) {
switch ($statusCode) {
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$className = last(explode('\\', $e->getModel()));
return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200);
}
case '404':
return response()->view('layouts/basic', [
'content' => view('errors/404')
]);
}
if ($this->isHttpException($e)) {
$statusCode = $e->getStatusCode();
switch ($e->getStatusCode()) {
case '404':
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
case '405':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), 405);
}
}
}
if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) {
return response()->view('layouts/basic', [
'content' => view('errors/404')
],$statusCode);
}
return parent::render($request, $e);
}
/**
* Convert an authentication exception into an unauthenticated response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Auth\AuthenticationException $exception
* @return \Illuminate\Http\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthorized.'], 401);
}
return redirect()->guest('login');
}
/**
* Convert a validation exception into a JSON response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Validation\ValidationException $exception
* @return \Illuminate\Http\JsonResponse
*/
protected function invalidJson($request, ValidationException $exception)
{
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors(), 400));
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Helpers;
use DB;
use App\Models\Statuslabel;
use App\Models\Location;
use App\Models\Department;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\User;
@@ -17,6 +18,7 @@ use App\Models\Component;
use App\Models\Accessory;
use App\Models\Consumable;
use App\Models\Asset;
use App\Models\Setting;
use Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
@@ -31,7 +33,8 @@ class Helper
* @since [v2.0]
* @return String
*/
public static function parseEscapedMarkedown($str) {
public static function parseEscapedMarkedown($str)
{
$Parsedown = new \Parsedown();
if ($str) {
@@ -123,77 +126,17 @@ class Helper
$LocaleInfo = localeconv();
$floatString = str_replace(",", "", $floatString);
$floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString);
// Strip Currency symbol
// If no currency symbol is set, default to $ because Murica
$currencySymbol = $LocaleInfo['currency_symbol'];
if (empty($currencySymbol)) {
$currencySymbol = '$';
}
$floatString = str_replace($currencySymbol, '', $floatString);
return floatval($floatString);
}
/**
* Get the list of models in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function modelList()
{
$models = AssetModel::with('manufacturer')->get();
$model_array[''] = trans('general.select_model');
foreach ($models as $model) {
$model_array[$model->id] = $model->displayModelName();
}
return $model_array;
}
/**
* Get the list of companies in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function companyList()
{
$company_list = array('0' => trans('general.select_company')) + DB::table('companies')
->orderBy('name', 'asc')
->pluck('name', 'id');
return $company_list;
}
/**
* Get the list of categories in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function categoryList($category_type = null)
{
$categories = Category::orderBy('name', 'asc')
->whereNull('deleted_at')
->orderBy('name', 'asc');
if(!empty($category_type))
$categories = $categories->where('category_type', '=', $category_type);
$category_list = array('' => trans('general.select_category')) + $categories->pluck('name', 'id')->toArray();
return $category_list;
}
/**
* Get the list of suppliers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function suppliersList()
{
$supplier_list = array('' => trans('general.select_supplier')) + Supplier::orderBy('name', 'asc')
->orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $supplier_list;
}
/**
* Get the list of status labels in an array to make a dropdown menu
*
@@ -203,42 +146,11 @@ class Helper
*/
public static function statusLabelList()
{
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('name', 'asc')
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('default_label', 'desc')->orderBy('name','asc')->orderBy('deployable','desc')
->pluck('name', 'id')->toArray();
return $statuslabel_list;
}
/**
* Get the list of locations in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function locationsList()
{
$location_list = array('' => trans('general.select_location')) + Location::orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $location_list;
}
/**
* Get the list of manufacturers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function manufacturerList()
{
$manufacturer_list = array('' => trans('general.select_manufacturer')) +
Manufacturer::orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $manufacturer_list;
}
/**
* Get the list of status label types in an array to make a dropdown menu
*
@@ -248,28 +160,15 @@ class Helper
*/
public static function statusTypeList()
{
$statuslabel_types = array('' => trans('admin/hardware/form.select_statustype')) + array('undeployable' => trans('admin/hardware/general.undeployable')) + array('pending' => trans('admin/hardware/general.pending')) + array('archived' => trans('admin/hardware/general.archived')) + array('deployable' => trans('admin/hardware/general.deployable'));
$statuslabel_types =
array('' => trans('admin/hardware/form.select_statustype'))
+ array('deployable' => trans('admin/hardware/general.deployable'))
+ array('pending' => trans('admin/hardware/general.pending'))
+ array('undeployable' => trans('admin/hardware/general.undeployable'))
+ array('archived' => trans('admin/hardware/general.archived'));
return $statuslabel_types;
}
/**
* Get the list of managers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function managerList()
{
$manager_list = array('' => trans('general.select_user')) +
User::where('deleted_at', '=', null)
->orderBy('last_name', 'asc')
->orderBy('first_name', 'asc')->get()
->lists('complete_name', 'id')->toArray();
return $manager_list;
}
/**
* Get the list of depreciations in an array to make a dropdown menu
*
@@ -293,58 +192,17 @@ class Helper
*/
public static function categoryTypeList()
{
$category_types = array('' => '','accessory' => 'Accessory', 'asset' => 'Asset', 'consumable' => 'Consumable','component' => 'Component');
$category_types = array(
'' => '',
'accessory' => 'Accessory',
'asset' => 'Asset',
'consumable' => 'Consumable',
'component' => 'Component',
'license' => 'License'
);
return $category_types;
}
/**
* Get the list of users in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function usersList()
{
$users_list = array( '' => trans('general.select_user')) +
Company::scopeCompanyables(User::where('deleted_at', '=', null))
->where('show_in_list','=',1)
->orderBy('last_name', 'asc')
->orderBy('first_name', 'asc')->get()
->lists('complete_name', 'id')->toArray();
return $users_list;
}
/**
* Get the list of assets in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function assetsList()
{
$assets_list = array('' => trans('general.select_asset')) + Asset::orderBy('name', 'asc')
->whereNull('deleted_at')
->pluck('name', 'id')->toArray();
return $assets_list;
}
/**
* Get the detailed list of assets in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function detailedAssetList()
{
$assets = array('' => trans('general.select_asset')) + Company::scopeCompanyables(Asset::with('assignedUser', 'model'), 'assets.company_id')->get()->lists('detailed_name', 'id')->toArray();
return $assets;
}
/**
* Get the list of custom fields in an array to make a dropdown menu
*
@@ -367,9 +225,9 @@ class Helper
*/
public static function predefined_formats()
{
$keys=array_keys(CustomField::$PredefinedFormats);
$stuff=array_combine($keys, $keys);
return $stuff+["" => trans('admin/custom_fields/general.custom_format')];
$keys = array_keys(CustomField::$PredefinedFormats);
$stuff = array_combine($keys, $keys);
return $stuff;
}
/**
@@ -423,9 +281,9 @@ class Helper
*/
public static function checkLowInventory()
{
$consumables = Consumable::with('users')->whereNotNull('min_amt')->get();
$accessories = Accessory::with('users')->whereNotNull('min_amt')->get();
$components = Component::with('assets')->whereNotNull('min_amt')->get();
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
$components = Component::withCount('assets as assets_count')->whereNotNull('min_amt')->get();
$avail_consumables = 0;
$items_array = array();
@@ -435,7 +293,7 @@ class Helper
$avail = $consumable->numRemaining();
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($consumable->qty > 0) {
$percent = number_format((($consumable->numRemaining() / $consumable->qty) * 100), 0);
$percent = number_format((($avail / $consumable->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -444,7 +302,7 @@ class Helper
$items_array[$all_count]['name'] = $consumable->name;
$items_array[$all_count]['type'] = 'consumables';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$consumable->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$consumable->min_amt;
$all_count++;
}
@@ -453,11 +311,11 @@ class Helper
}
foreach ($accessories as $accessory) {
$avail = $accessory->numRemaining();
$avail = $accessory->qty - $accessory->users_count;
if ($avail < ($accessory->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($accessory->qty > 0) {
$percent = number_format((($accessory->numRemaining() / $accessory->qty) * 100), 0);
$percent = number_format((($avail / $accessory->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -466,7 +324,7 @@ class Helper
$items_array[$all_count]['name'] = $accessory->name;
$items_array[$all_count]['type'] = 'accessories';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$accessory->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$accessory->min_amt;
$all_count++;
}
@@ -474,10 +332,10 @@ class Helper
}
foreach ($components as $component) {
$avail = $component->numRemaining();
$avail = $component->qty - $component->assets_count;
if ($avail < ($component->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($component->qty > 0) {
$percent = number_format((($component->numRemaining() / $component->qty) * 100), 0);
$percent = number_format((($avail / $component->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -486,7 +344,7 @@ class Helper
$items_array[$all_count]['name'] = $component->name;
$items_array[$all_count]['type'] = 'components';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$component->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$component->min_amt;
$all_count++;
}
@@ -553,7 +411,7 @@ class Helper
if ($permission[$x]['display'] === true) {
if ($selected_arr) {
if (array_key_exists($permission_name,$selected_arr)) {
if (array_key_exists($permission_name, $selected_arr)) {
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
} else {
$permissions_arr[$permission_name] = '0';
@@ -584,7 +442,8 @@ class Helper
* @since [v3.0]
* @return boolean
*/
public static function checkIfRequired($class, $field) {
public static function checkIfRequired($class, $field)
{
$rules = $class::rules();
foreach ($rules as $rule_name => $rule) {
if ($rule_name == $field) {
@@ -611,28 +470,10 @@ class Helper
*/
public static function array_smart_fetch(array $array, $key, $default = '')
{
array_change_key_case($array, CASE_LOWER);
array_change_key_case($array, CASE_LOWER);
return array_key_exists(strtolower($key), array_change_key_case($array)) ? e(trim($array[ $key ])) : $default;
}
/**
* Check to see if the given key exists in the array, and trim excess white space before returning it
*
* @author A. Gianotto
* @since 3.2
* @param $array array
* @return string
*/
public static function getLastDateFromHistoryArray(array $array)
{
foreach ($array as $key => $value) {
// echo '<pre>';
// echo 'last:'.$key;
// print_r($array);
// echo '</pre>';
}
}
/**
* Gracefully handle decrypting the legacy data (encrypted via mcrypt) and use the new
@@ -646,7 +487,8 @@ class Helper
* @param String $string
* @return string
*/
public static function gracefulDecrypt(CustomField $field, $string) {
public static function gracefulDecrypt(CustomField $field, $string)
{
if ($field->isFieldDecryptable($string)) {
@@ -663,26 +505,171 @@ class Helper
}
/**
* Strip HTML out of returned JSON. This is pretty gross, and I'd like to find a better way
* to handle this, but the REST API will solve some of these problems anyway.
*
* This is not currently used, but will be.
*
* @author A. Gianotto
* @since 3.4
* @param $array array
* @return Array
*/
public static function stripTagsFromJSON(Array $array) {
foreach ($array as $key => $value) {
$clean_value = strip_tags($value);
$clean_array[$key] = $clean_value;
public static function formatStandardApiResponse($status, $payload = null, $messages = null) {
$array['status'] = $status;
$array['messages'] = $messages;
if (($messages) && (is_array($messages)) && (count($messages) > 0)) {
$array['messages'] = $messages;
}
return $clean_array;
($payload) ? $array['payload'] = $payload : $array['payload'] = null;
return $array;
}
/*
Possible solution for unicode fieldnames
*/
public static function make_slug($string) {
return preg_replace('/\s+/u', '_', trim($string));
}
public static function getFormattedDateObject($date, $type = 'datetime', $array = true) {
if ($date=='') {
return null;
}
$settings = Setting::getSettings();
$tmp_date = new \Carbon($date);
if ($type == 'datetime') {
$dt['datetime'] = $tmp_date->format('Y-m-d H:i:s');
$dt['formatted'] = $tmp_date->format($settings->date_display_format .' '. $settings->time_display_format);
} else {
$dt['date'] = $tmp_date->format('Y-m-d');
$dt['formatted'] = $tmp_date->format($settings->date_display_format);
}
if ($array == 'true') {
return $dt;
}
return $dt['formatted'];
}
// Nicked from Drupal :)
// Returns a file size limit in bytes based on the PHP upload_max_filesize
// and post_max_size
public static function file_upload_max_size() {
static $max_size = -1;
if ($max_size < 0) {
// Start with post_max_size.
$post_max_size = Helper::parse_size(ini_get('post_max_size'));
if ($post_max_size > 0) {
$max_size = $post_max_size;
}
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}
}
return $max_size;
}
public static function file_upload_max_size_readable() {
static $max_size = -1;
if ($max_size < 0) {
// Start with post_max_size.
$post_max_size = Helper::parse_size(ini_get('post_max_size'));
if ($post_max_size > 0) {
$max_size = ini_get('post_max_size');
}
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = ini_get('upload_max_filesize');
}
}
return $max_size;
}
public static function parse_size($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
}
public static function filetype_icon($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return "fa fa-file-image-o";
break;
case 'doc':
case 'docx':
return "fa fa-file-word-o";
break;
case 'xls':
case 'xlsx':
return "fa fa-file-excel-o";
break;
case 'zip':
case 'rar':
return "fa fa-file-archive-o";
break;
case 'pdf':
return "fa fa-file-pdf-o";
break;
case 'txt':
return "fa fa-file-text-o";
break;
case 'lic':
return "fa fa-floppy-o";
break;
default:
return "fa fa-file-o";
}
}
return "fa fa-file-o";
}
public static function show_file_inline($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return true;
break;
default:
return false;
}
}
return false;
}
}

View File

@@ -3,9 +3,7 @@ namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use Auth;
use Carbon\Carbon;
@@ -14,12 +12,13 @@ use DB;
use Gate;
use Input;
use Lang;
use Mail;
use Redirect;
use Request;
use Illuminate\Http\Request;
use Slack;
use Str;
use View;
use Image;
use App\Http\Requests\ImageUploadRequest;
/** This controller handles all actions related to Accessories for
* the Snipe-IT Asset Management application.
@@ -38,9 +37,10 @@ class AccessoriesController extends Controller
* @since [v1.0]
* @return View
*/
public function getIndex(Request $request)
public function index(Request $request)
{
return View::make('accessories/index');
$this->authorize('index', Accessory::class);
return view('accessories/index');
}
@@ -50,15 +50,12 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return View
*/
public function getCreate(Request $request)
public function create(Request $request)
{
// Show the page
return View::make('accessories/edit')
->with('item', new Accessory)
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
$this->authorize('create', Accessory::class);
$category_type = 'accessory';
return view('accessories/edit')->with('category_type', $category_type)
->with('item', new Accessory);
}
@@ -68,45 +65,34 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return Redirect
*/
public function postCreate(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize(Accessory::class);
// create a new model instance
$accessory = new Accessory();
// Update the accessory data
$accessory->name = e(Input::get('name'));
$accessory->category_id = e(Input::get('category_id'));
$accessory->location_id = e(Input::get('location_id'));
$accessory->min_amt = e(Input::get('min_amt'));
$accessory->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$accessory->order_number = e(Input::get('order_number'));
$accessory->manufacturer_id = e(Input::get('manufacturer_id'));
$accessory->model_number = e(Input::get('model_number'));
if (e(Input::get('purchase_date')) == '') {
$accessory->purchase_date = null;
} else {
$accessory->purchase_date = e(Input::get('purchase_date'));
}
if (e(Input::get('purchase_cost')) == '0.00') {
$accessory->purchase_cost = null;
} else {
$accessory->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
}
$accessory->qty = e(Input::get('qty'));
$accessory->name = request('name');
$accessory->category_id = request('category_id');
$accessory->location_id = request('location_id');
$accessory->min_amt = request('min_amt');
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
$accessory->order_number = request('order_number');
$accessory->manufacturer_id = request('manufacturer_id');
$accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost'));
$accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id');
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
// Was the accessory created?
if ($accessory->save()) {
$accessory->logCreate();
// Redirect to the new accessory page
return redirect()->to("admin/accessories")->with('success', trans('admin/accessories/message.create.success'));
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
}
@@ -117,21 +103,17 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @return View
*/
public function getEdit(Request $request, $accessoryId = null)
public function edit(Request $request, $accessoryId = null)
{
// Check if the accessory exists
if (is_null($item = Accessory::find($accessoryId))) {
// Redirect to the blogs management page
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($item)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
if ($item = Accessory::find($accessoryId)) {
$this->authorize($item);
$category_type = 'accessory';
return view('accessories/edit', compact('item'))->with('category_type', $category_type);
}
return View::make('accessories/edit', compact('item'))
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
@@ -142,54 +124,36 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @return Redirect
*/
public function postEdit(Request $request, $accessoryId = null)
public function update(ImageUploadRequest $request, $accessoryId = null)
{
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
// Redirect to the accessory index page
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
// Update the accessory data
$accessory->name = e(Input::get('name'));
$this->authorize($accessory);
if (e(Input::get('location_id')) == '') {
$accessory->location_id = null;
} else {
$accessory->location_id = e(Input::get('location_id'));
}
$accessory->min_amt = e(Input::get('min_amt'));
$accessory->category_id = e(Input::get('category_id'));
$accessory->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$accessory->manufacturer_id = e(Input::get('manufacturer_id'));
$accessory->order_number = e(Input::get('order_number'));
$accessory->model_number = e(Input::get('model_number'));
// Update the accessory data
$accessory->name = request('name');
$accessory->location_id = request('location_id');
$accessory->min_amt = request('min_amt');
$accessory->category_id = request('category_id');
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
$accessory->manufacturer_id = request('manufacturer_id');
$accessory->order_number = request('order_number');
$accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id');
if (e(Input::get('purchase_date')) == '') {
$accessory->purchase_date = null;
} else {
$accessory->purchase_date = e(Input::get('purchase_date'));
}
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
if (e(Input::get('purchase_cost')) == '0.00') {
$accessory->purchase_cost = null;
} else {
$accessory->purchase_cost = e(Input::get('purchase_cost'));
}
$accessory->qty = e(Input::get('qty'));
// Was the accessory updated?
// Was the accessory updated?
if ($accessory->save()) {
// Redirect to the updated accessory page
return redirect()->to("admin/accessories")->with('success', trans('admin/accessories/message.update.success'));
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
}
/**
@@ -199,26 +163,20 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @return Redirect
*/
public function getDelete(Request $request, $accessoryId)
public function destroy(Request $request, $accessoryId)
{
// Check if the blog post exists
if (is_null($accessory = Accessory::find($accessoryId))) {
// Redirect to the blogs management page
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
$this->authorize($accessory);
if ($accessory->hasUsers() > 0) {
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers())));
} else {
$accessory->delete();
// Redirect to the locations management page
return redirect()->to('admin/accessories')->with('success', trans('admin/accessories/message.delete.success'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers())));
}
$accessory->delete();
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.delete.success'));
}
@@ -228,31 +186,19 @@ class AccessoriesController extends Controller
* the content for the accessory detail view, which is generated in getDataView.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @param int $accessoryID
* @see AccessoriesController::getDataView() method that generates the JSON response
* @since [v1.0]
* @return View
*/
public function getView(Request $request, $accessoryID = null)
public function show(Request $request, $accessoryID = null)
{
$accessory = Accessory::find($accessoryID);
$this->authorize('view', $accessory);
if (isset($accessory->id)) {
if (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
} else {
return View::make('accessories/view', compact('accessory'));
}
} else {
// Prepare the error message
$error = trans('admin/accessories/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('accessories')->with('error', $error);
return view('accessories/view', compact('accessory'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**
@@ -267,15 +213,20 @@ class AccessoriesController extends Controller
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
// Redirect to the accessory management page with error
return redirect()->to('accessories')->with('error', trans('admin/accessories/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
// Get the dropdown of users and then pass it to the checkout view
$users_list = Helper::usersList();
if ($accessory->category) {
$this->authorize('checkout', $accessory);
// Get the dropdown of users and then pass it to the checkout view
return view('accessories/checkout', compact('accessory'));
}
return redirect()->back()->with('error', 'The category type for this accessory is not valid. Edit the accessory and select a valid accessory category.');
return View::make('accessories/checkout', compact('accessory'))->with('users_list', $users_list);
}
@@ -294,64 +245,28 @@ class AccessoriesController extends Controller
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
// Redirect to the accessory management page with error
return redirect()->to('accessories')->with('error', trans('admin/accessories/message.user_not_found'));
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.user_not_found'));
}
$this->authorize('checkout', $accessory);
if (!$user = User::find(Input::get('assigned_to'))) {
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
return redirect()->route('checkout/accessory', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
}
// Update the accessory data
$accessory->assigned_to = e(Input::get('assigned_to'));
$accessory->assigned_to = e(Input::get('assigned_to'));
$accessory->users()->attach($accessory->id, array(
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::user()->id,
'assigned_to' => e(Input::get('assigned_to'))));
$accessory->users()->attach($accessory->id, [
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to')
]);
$logaction = $accessory->logCheckout(e(Input::get('note')));
$logaction = $accessory->logCheckout(e(Input::get('note')), $user);
$admin_user = Auth::user();
$settings = Setting::getSettings();
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => $settings->botname,
'channel' => $settings->slack_channel,
'link_names' => true
];
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
try {
$client->attach([
'color' => 'good',
'fields' => [
[
'title' => 'Checked Out:',
'value' => 'Accessory <'.config('app.url').'/admin/accessories/'.$accessory->id.'/view'.'|'.$accessory->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
],
[
'title' => 'Note:',
'value' => e(Input::get('note'))
],
]
])->send('Accessory Checked Out');
} catch (Exception $e) {
}
}
$accessory_user = DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
$data['log_id'] = $logaction->id;
$data['eula'] = $accessory->getEula();
@@ -363,319 +278,83 @@ class AccessoriesController extends Controller
$data['note'] = $logaction->note;
$data['require_acceptance'] = $accessory->requireAcceptance();
if (($accessory->requireAcceptance()=='1') || ($accessory->getEula())) {
Mail::send('emails.accept-accessory', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_accessory_delivery'));
});
}
// Redirect to the new accessory page
return redirect()->to("admin/accessories")->with('success', trans('admin/accessories/message.checkout.success'));
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
}
/**
* Check the accessory back into inventory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return View
**/
/**
* Check the accessory back into inventory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @param integer $accessoryUserId
* @param string $backto
* @return View
* @internal param int $accessoryId
*/
public function getCheckin(Request $request, $accessoryUserId = null, $backto = null)
{
// Check if the accessory exists
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
// Redirect to the accessory management page with error
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
$accessory = Accessory::find($accessory_user->accessory_id);
if (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
} else {
return View::make('accessories/checkin', compact('accessory'))->with('backto', $backto);
}
$this->authorize('checkin', $accessory);
return view('accessories/checkin', compact('accessory'))->with('backto', $backto);
}
/**
* Check in the item so that it can be checked out again to someone else
*
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return Redirect
**/
/**
* Check in the item so that it can be checked out again to someone else
*
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @param integer $accessoryUserId
* @param string $backto
* @return Redirect
* @internal param int $accessoryId
*/
public function postCheckin(Request $request, $accessoryUserId = null, $backto = null)
{
// Check if the accessory exists
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
// Redirect to the accessory management page with error
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
$accessory = Accessory::find($accessory_user->accessory_id);
if (!Company::isCurrentUserHasAccess($accessory)) {
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
}
$this->authorize('checkin', $accessory);
$return_to = e($accessory_user->assigned_to);
$logaction = $accessory->logCheckin(User::find($return_to), e(Input::get('note')));
$admin_user = Auth::user();
// Was the accessory updated?
// Was the accessory updated?
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
$settings = Setting::getSettings();
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => e($settings->botname),
'channel' => e($settings->slack_channel),
'link_names' => true
];
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
try {
$client->attach([
'color' => 'good',
'fields' => [
[
'title' => 'Checked In:',
'value' => class_basename(strtoupper($logaction->item_type)).' <'.config('app.url').'/admin/accessories/'.e($accessory->id).'/view'.'|'.e($accessory->name).'> checked in by <'.config('app.url').'/admin/users/'.e($admin_user->id).'/view'.'|'.e($admin_user->fullName()).'>.'
],
[
'title' => 'Note:',
'value' => e($logaction->note)
],
]
])->send('Accessory Checked In');
} catch (Exception $e) {
}
}
if (!is_null($accessory_user->assigned_to)) {
$user = User::find($accessory_user->assigned_to);
}
$data['log_id'] = $logaction->id;
$data['first_name'] = e($user->first_name);
$data['last_name'] = e($user->last_name);
$data['item_name'] = e($accessory->name);
$data['checkin_date'] = e($logaction->created_at);
$data['item_tag'] = '';
$data['note'] = e($logaction->note);
if (($accessory->checkin_email()=='1')) {
Mail::send('emails.checkin-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_Accessory_Checkin'));
});
}
if ($backto=='user') {
return redirect()->to("admin/users/".$return_to.'/view')->with('success', trans('admin/accessories/message.checkin.success'));
} else {
return redirect()->to("admin/accessories/".$accessory->id."/view")->with('success', trans('admin/accessories/message.checkin.success'));
return redirect()->route("users.show", $return_to)->with('success', trans('admin/accessories/message.checkin.success'));
}
return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success'));
}
// Redirect to the accessory management page with error
return redirect()->to("admin/accessories")->with('error', trans('admin/accessories/message.checkin.error'));
}
/**
* Generates the JSON response for accessories listing view.
*
* Example:
* {
* "actions": "(links to available actions)",
* "category": "(link to category)",
* "companyName": "My Company",
* "location": "My Location",
* "min_amt": 2,
* "name": "(link to accessory),
* "numRemaining": 6,
* "order_number": null,
* "purchase_cost": "0.00",
* "purchase_date": null,
* "qty": 7
* },
*
* The names of the fields in the returns JSON correspond directly to the the
* names of the fields in the bootstrap-tables in the view.
*
* For debugging, see at /api/accessories/list
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return string JSON containing accessories and their associated atrributes.
**/
public function getDatatable(Request $request)
{
$accessories = Company::scopeCompanyables(
Accessory::select('accessories.*')
->whereNull('accessories.deleted_at')
->with('category', 'company', 'manufacturer', 'users', 'location')
);
if (Input::has('search')) {
$accessories = $accessories->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['name','min_amt','order_number','purchase_date','purchase_cost','companyName','category','model_number'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
switch ($sort) {
case 'category':
$accessories = $accessories->OrderCategory($order);
break;
case 'companyName':
$accessories = $accessories->OrderCompany($order);
break;
default:
$accessories = $accessories->orderBy($sort, $order);
break;
}
$accessCount = $accessories->count();
$accessories = $accessories->skip($offset)->take($limit)->get();
$rows = array();
foreach ($accessories as $accessory) {
$actions = '<nobr>';
if (Gate::allows('accessories.checkout')) {
$actions .= '<a href="' . route('checkout/accessory',
$accessory->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($accessory->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
}
if (Gate::allows('accessories.edit')) {
$actions .= '<a href="' . route('update/accessory',
$accessory->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
}
if (Gate::allows('accessories.delete')) {
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/accessory',
$accessory->id) . '" data-content="' . trans('admin/accessories/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($accessory->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
}
$actions .= '</nobr>';
$company = $accessory->company;
$rows[] = array(
'name' => '<a href="'.url('admin/accessories/'.$accessory->id).'/view">'. $accessory->name.'</a>',
'category' => ($accessory->category) ? (string)link_to('admin/settings/categories/'.$accessory->category->id.'/view', $accessory->category->name) : '',
'model_number' => e($accessory->model_number),
'qty' => e($accessory->qty),
'order_number' => e($accessory->order_number),
'min_amt' => e($accessory->min_amt),
'location' => ($accessory->location) ? e($accessory->location->name): '',
'purchase_date' => e($accessory->purchase_date),
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
'numRemaining' => $accessory->numRemaining(),
'actions' => $actions,
'companyName' => is_null($company) ? '' : e($company->name),
'manufacturer' => $accessory->manufacturer ? (string) link_to('/admin/settings/manufacturers/'.$accessory->manufacturer_id.'/view', $accessory->manufacturer->name) : ''
);
}
$data = array('total'=>$accessCount, 'rows'=>$rows);
return $data;
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
}
/**
* Generates the JSON response for accessory detail view.
*
* Example:
* <code>
* {
* "rows": [
* {
* "actions": "(link to available actions)",
* "name": "(link to user)"
* }
* ],
* "total": 1
* }
* </code>
*
* The names of the fields in the returns JSON correspond directly to the the
* names of the fields in the bootstrap-tables in the view.
*
* For debugging, see at /api/accessories/$accessoryID/view
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return string JSON containing accessories and their associated atrributes.
**/
public function getDataView(Request $request, $accessoryID)
{
$accessory = Accessory::find($accessoryID);
if (!Company::isCurrentUserHasAccess($accessory)) {
return ['total' => 0, 'rows' => []];
}
$accessory_users = $accessory->users;
$count = $accessory_users->count();
$rows = array();
foreach ($accessory_users as $user) {
$actions = '';
if (Gate::allows('accessories.checkin')) {
$actions .= '<a href="' . route('checkin/accessory',
$user->pivot->id) . '" class="btn btn-info btn-sm">Checkin</a>';
}
if (Gate::allows('users.view')) {
$name = (string) link_to('/admin/users/'.$user->id.'/view', e($user->fullName()));
} else {
$name = e($user->fullName());
}
$rows[] = array(
'name' => $name,
'actions' => $actions
);
}
$data = array('total'=>$count, 'rows'=>$rows);
return $data;
}
}

View File

@@ -9,7 +9,7 @@ class ActionlogController extends Controller
{
public function displaySig($filename)
{
$this->authorize('view', \App\Models\Asset::class);
$file = config('app.private_uploads') . '/signatures/' . $filename;
$filetype = Helper::checkUploadIsImage($file);
$contents = file_get_contents($file);

View File

@@ -0,0 +1,293 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Http\Transformers\AccessoriesTransformer;
use App\Models\Company;
use App\Models\User;
use Carbon\Carbon;
use Auth;
use DB;
class AccessoriesController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Accessory::class);
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search'));
}
if ($request->filled('company_id')) {
$accessories->where('company_id','=',$request->input('company_id'));
}
if ($request->filled('category_id')) {
$accessories->where('category_id','=',$request->input('category_id'));
}
if ($request->filled('manufacturer_id')) {
$accessories->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
if ($request->filled('supplier_id')) {
$accessories->where('supplier_id','=',$request->input('supplier_id'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($sort) {
case 'category':
$accessories = $accessories->OrderCategory($order);
break;
case 'company':
$accessories = $accessories->OrderCompany($order);
break;
default:
$accessories = $accessories->orderBy($sort, $order);
break;
}
$accessories->orderBy($sort, $order);
$total = $accessories->count();
$accessories = $accessories->skip($offset)->take($limit)->get();
return (new AccessoriesTransformer)->transformAccessories($accessories, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Accessory::class);
$accessory = new Accessory;
$accessory->fill($request->all());
if ($accessory->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id);
return (new AccessoriesTransformer)->transformAccessory($accessory);
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function accessory_detail($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id);
return (new AccessoriesTransformer)->transformAccessory($accessory);
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function checkedout($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id);
if (!Company::isCurrentUserHasAccess($accessory)) {
return ['total' => 0, 'rows' => []];
}
$accessory_users = $accessory->users;
$total = $accessory_users->count();
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_users, $total);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Accessory::class);
$accessory = Accessory::findOrFail($id);
$accessory->fill($request->all());
if ($accessory->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Accessory::class);
$accessory = Accessory::findOrFail($id);
$this->authorize($accessory);
if ($accessory->hasUsers() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers()))));
}
$accessory->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.delete.success')));
}
/**
* Save the Accessory checkout information.
*
* If Slack is enabled and/or asset acceptance is enabled, it will also
* trigger a Slack message and send an email.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return Redirect
*/
public function checkout(Request $request, $accessoryId)
{
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
}
$this->authorize('checkout', $accessory);
if ($accessory->numRemaining() > 0) {
if (!$user = User::find($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist')));
}
// Update the accessory data
$accessory->assigned_to = $request->input('assigned_to');
$accessory->users()->attach($accessory->id, [
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to')
]);
$accessory->logCheckout($request->input('note'), $user);
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining'));
}
/**
* Check in the item so that it can be checked out again to someone else
*
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @param integer $accessoryUserId
* @param string $backto
* @return Redirect
* @internal param int $accessoryId
*/
public function checkin(Request $request, $accessoryUserId = null)
{
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
}
$accessory = Accessory::find($accessory_user->accessory_id);
$this->authorize('checkin', $accessory);
$logaction = $accessory->logCheckin(User::find($accessoryUserId), $request->input('note'));
// Was the accessory updated?
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
if (!is_null($accessory_user->assigned_to)) {
$user = User::find($accessory_user->assigned_to);
}
$data['log_id'] = $logaction->id;
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['item_name'] = $accessory->name;
$data['checkin_date'] = $logaction->created_at;
$data['item_tag'] = '';
$data['note'] = $logaction->note;
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkin.error')));
}
}

View File

@@ -0,0 +1,252 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\AssetMaintenancesTransformer;
use App\Models\Asset;
use App\Models\AssetMaintenance;
use App\Models\Company;
use Auth;
use Carbon\Carbon;
use Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
/**
* This controller handles all actions related to Asset Maintenance for
* the Snipe-IT Asset Management application.
*
* @version v2.0
*/
class AssetMaintenancesController extends Controller
{
/**
* Generates the JSON response for asset maintenances listing view.
*
* @see AssetMaintenancesController::getIndex() method that generates view
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return String JSON
*/
public function index(Request $request)
{
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company', 'admin');
if (Input::has('search')) {
$maintenances = $maintenances->TextSearch(e($request->input('search')));
}
if ($request->filled('asset_id')) {
$maintenances->where('asset_id', '=', $request->input('asset_id'));
}
// 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');
$allowed_columns = [
'id',
'title',
'asset_maintenance_time',
'asset_maintenance_type',
'cost',
'start_date',
'completion_date',
'notes',
'asset_tag',
'asset_name',
'user_id'
];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
switch ($sort) {
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
case 'asset_tag':
$maintenances = $maintenances->OrderByTag($order);
break;
case 'asset_name':
$maintenances = $maintenances->OrderByAssetName($order);
break;
default:
$maintenances = $maintenances->orderBy($sort, $order);
break;
}
$total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
}
/**
* Validates and stores the new asset maintenance
*
* @see AssetMaintenancesController::getCreate() method for the form
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return String JSON
*/
public function store(Request $request)
{
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = e($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
if (!Company::isCurrentUserHasAccess($asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot add a maintenance for that asset'));
}
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->user_id = Auth::id();
if (( $assetMaintenance->completion_date !== null )
&& ( $assetMaintenance->start_date !== "" )
&& ( $assetMaintenance->start_date !== "0000-00-00" )
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
}
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
}
/**
* Validates and stores an update to an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $assetMaintenanceId
* @param int $request
* @version v1.0
* @since [v4.0]
* @return String JSON
*/
public function update(Request $request, $assetMaintenanceId = null)
{
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
}
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost')));
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(request('asset_id'));
if (!Company::isCurrentUserHasAccess($asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
}
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
if (( $assetMaintenance->completion_date == null )
) {
if (( $assetMaintenance->asset_maintenance_time !== 0 )
|| ( !is_null($assetMaintenance->asset_maintenance_time) )
) {
$assetMaintenance->asset_maintenance_time = null;
}
}
if (( $assetMaintenance->completion_date !== null )
&& ( $assetMaintenance->start_date !== "" )
&& ( $assetMaintenance->start_date !== "0000-00-00" )
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
}
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
}
/**
* Delete an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
* @return String JSON
*/
public function destroy($assetMaintenanceId)
{
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot delete a maintenance for that asset'));
}
$assetMaintenance->delete();
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
}
/**
* View an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
* @return String JSON
*/
public function show($assetMaintenanceId)
{
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot view a maintenance for that asset'));
}
return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\AssetModel;
use App\Models\Asset;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use Illuminate\Http\Request;
use App\Http\Transformers\AssetModelsTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\SelectlistTransformer;
/**
* This class controls all actions related to asset models for
* the Snipe-IT Asset Management application.
*
* @version v4.0
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
class AssetModelsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', AssetModel::class);
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','assets_count'];
$assetmodels = AssetModel::select([
'models.id',
'models.image',
'models.name',
'model_number',
'eol',
'models.notes',
'models.created_at',
'category_id',
'manufacturer_id',
'depreciation_id',
'fieldset_id',
'models.deleted_at',
'models.updated_at',
])
->with('category','depreciation', 'manufacturer','fieldset')
->withCount('assets as assets_count');
if ($request->filled('status')) {
$assetmodels->onlyTrashed();
}
if ($request->filled('search')) {
$assetmodels->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at';
switch ($sort) {
case 'manufacturer':
$assetmodels->OrderManufacturer($order);
break;
default:
$assetmodels->orderBy($sort, $order);
break;
}
$total = $assetmodels->count();
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', AssetModel::class);
$assetmodel = new AssetModel;
$assetmodel->fill($request->all());
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', AssetModel::class);
$assetmodel = AssetModel::withCount('assets as assets_count')->findOrFail($id);
return (new AssetModelsTransformer)->transformAssetModel($assetmodel);
}
/**
* Display the specified resource's assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function assets($id)
{
$this->authorize('view', AssetModel::class);
$assets = Asset::where('model_id','=',$id)->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', AssetModel::class);
$assetmodel = AssetModel::findOrFail($id);
$assetmodel->fill($request->all());
$assetmodel->fieldset_id = $request->get("custom_fieldset_id");
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', AssetModel::class);
$assetmodel = AssetModel::findOrFail($id);
$this->authorize('delete', $assetmodel);
if ($assetmodel->assets()->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.assoc_users')));
}
if ($assetmodel->image) {
try {
unlink(public_path().'/uploads/models/'.$assetmodel->image);
} catch (\Exception $e) {
\Log::info($e);
}
}
$assetmodel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$assetmodels = AssetModel::select([
'models.id',
'models.name',
'models.image',
'models.model_number',
'models.manufacturer_id',
'models.category_id',
])->with('manufacturer','category');
$settings = \App\Models\Setting::getSettings();
if ($request->filled('search')) {
$assetmodels = $assetmodels->SearchByManufacturerOrCat($request->input('search'));
}
$assetmodels = $assetmodels->OrderCategory('ASC')->OrderManufacturer('ASC')->orderby('models.name', 'asc')->orderby('models.model_number', 'asc')->paginate(50);
foreach ($assetmodels as $assetmodel) {
$assetmodel->use_text = '';
if ($settings->modellistCheckedValue('category')) {
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
}
if ($settings->modellistCheckedValue('manufacturer')) {
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
}
$assetmodel->use_text .= e($assetmodel->name);
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
}
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? url('/').'/uploads/models/'.$assetmodel->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($assetmodels);
}
}

View File

@@ -0,0 +1,811 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\AssetRequest;
use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Artisan;
use Auth;
use Carbon\Carbon;
use Config;
use DB;
use Gate;
use Illuminate\Http\Request;
use Input;
use Lang;
use Log;
use Mail;
use Paginator;
use Response;
use Slack;
use Str;
use TCPDF;
use Validator;
use View;
use App\Http\Transformers\SelectlistTransformer;
/**
* This class controls all actions related to assets for
* the Snipe-IT Asset Management application.
*
* @version v1.0
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
class AssetsController extends Controller
{
/**
* Returns JSON listing of all assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function index(Request $request, $audit = null)
{
$this->authorize('index', Asset::class);
$settings = Setting::getSettings();
$allowed_columns = [
'id',
'name',
'asset_tag',
'serial',
'model_number',
'last_checkout',
'notes',
'expected_checkin',
'order_number',
'image',
'assigned_to',
'created_at',
'updated_at',
'purchase_date',
'purchase_cost',
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkout_counter',
'checkin_counter',
'requests_counter',
];
$filter = array();
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
}
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[]=$field->db_column_name();
}
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// These are used by the API to query against specific ID numbers.
// They are also used by the individual searches on detail pages like
// locations, etc.
if ($request->filled('status_id')) {
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
if ($request->input('requestable')=='true') {
$assets->where('assets.requestable', '=', '1');
}
if ($request->filled('model_id')) {
$assets->InModelList([$request->input('model_id')]);
}
if ($request->filled('category_id')) {
$assets->InCategory($request->input('category_id'));
}
if ($request->filled('location_id')) {
$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'));
}
if (($request->filled('assigned_to')) && ($request->filled('assigned_type'))) {
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
->where('assets.assigned_type', '=', $request->input('assigned_type'));
}
if ($request->filled('company_id')) {
$assets->where('assets.company_id', '=', $request->input('company_id'));
}
if ($request->filled('manufacturer_id')) {
$assets->ByManufacturer($request->input('manufacturer_id'));
}
if ($request->filled('depreciation_id')) {
$assets->ByDepreciationId($request->input('depreciation_id'));
}
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
// This is used by the audit reporting routes
if (Gate::allows('audit', Asset::class)) {
switch ($audit) {
case 'due':
$assets->DueOrOverdueForAudit($settings);
break;
case 'overdue':
$assets->overdueForAudit($settings);
break;
}
}
// This is used by the sidenav, mostly
// We switched from using query scopes here because of a Laravel bug
// related to fulltext searches on complex queries.
// I am sad. :(
switch ($request->input('status')) {
case 'Deleted':
$assets->withTrashed()->Deleted();
break;
case 'Pending':
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',0)
->where('status_alias.pending','=',1)
->where('status_alias.archived', '=', 0);
});
break;
case 'RTD':
$assets->whereNull('assets.assigned_to')
->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',1)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Undeployable':
$assets->Undeployable();
break;
case 'Archived':
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',0)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 1);
});
break;
case 'Requestable':
$assets->where('assets.requestable', '=', 1)
->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',1)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Deployed':
// more sad, horrible workarounds for laravel bugs when doing full text searches
$assets->where('assets.assigned_to', '>', '0');
break;
default:
if ((!$request->filled('status_id')) && ($settings->show_archived_in_list!='1')) {
// terrible workaround for complex-query Laravel bug in fulltext
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.archived', '=', 0);
});
// If there is a status ID, don't take show_archived_in_list into consideration
} else {
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id");
});
}
}
if ((!is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter);
} elseif ($request->filled('search')) {
$assets->TextSearch($request->input('search'));
}
// This is kinda gross, but we need to do this because the Bootstrap Tables
// API passes custom field ordering as custom_fields.fieldname, and we have to strip
// that out to let the default sorter below order them correctly on the assets table.
$sort_override = str_replace('custom_fields.','', $request->input('sort')) ;
// This handles all of the pivot sorting (versus the assets.* fields
// in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
case 'company':
$assets->OrderCompany($order);
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
break;
case 'supplier':
$assets->OrderSupplier($order);
break;
case 'assigned_to':
$assets->OrderAssigned($order);
break;
default:
$assets->orderBy($column_sort, $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformAssets($assets, $total);
}
/**
* Returns JSON with information about an asset (by tag) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $tag
* @since [v4.2.1]
* @return JsonResponse
*/
public function showByTag($tag)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->where('asset_tag',$tag)->first()) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
}
/**
* Returns JSON with information about an asset (by serial) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $serial
* @since [v4.2.1]
* @return JsonResponse
*/
public function showBySerial($serial)
{
$this->authorize('index', Asset::class);
if ($assets = Asset::with('assetstatus')->with('assignedTo')
->withTrashed()->where('serial',$serial)->get()) {
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
}
/**
* Returns JSON with information about an asset for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function show($id)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as userRequests_count')->findOrFail($id)) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$assets = Company::scopeCompanyables(Asset::select([
'assets.id',
'assets.name',
'assets.asset_tag',
'assets.model_id',
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id'
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(),'company_id', 'assets');
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
}
if ($request->filled('search')) {
$assets = $assets->AssignedSearch($request->input('search'));
}
$assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($assets as $asset) {
$asset->use_text = $asset->present()->fullName;
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
}
if ($asset->assetstatus->getStatuslabelType()=='pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
}
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
}
return (new SelectlistTransformer)->transformSelectlist($assets);
}
/**
* Accepts a POST request to create a new asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @since [v4.0]
* @return JsonResponse
*/
public function store(AssetRequest $request)
{
$this->authorize('create', Asset::class);
$asset = new Asset();
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
$asset->name = $request->get('name');
$asset->serial = $request->get('serial');
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id'));
$asset->model_id = $request->get('model_id');
$asset->order_number = $request->get('order_number');
$asset->notes = $request->get('notes');
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
$asset->user_id = Auth::id();
$asset->archived = '0';
$asset->physical = '1';
$asset->depreciate = '0';
$asset->status_id = $request->get('status_id', 0);
$asset->warranty_months = $request->get('warranty_months', null);
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
$asset->purchase_date = $request->get('purchase_date', null);
$asset->assigned_to = $request->get('assigned_to', null);
$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
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
}
}
}
if ($asset->save()) {
if ($request->get('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif ($request->get('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
} elseif ($request->get('assigned_location')) {
$target = Location::find(request('assigned_location'));
}
if (isset($target)) {
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')));
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
/**
* Accepts a POST request to update an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @since [v4.0]
* @return JsonResponse
*/
public function update(Request $request, $id)
{
$this->authorize('update', Asset::class);
if ($asset = Asset::find($id)) {
$asset->fill($request->all());
($request->filled('model_id')) ?
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
($request->filled('company_id')) ?
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : null;
($request->filled('rtd_location_id')) ?
$asset->location_id = $request->get('rtd_location_id') : null;
// Update custom fields
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
if ($request->has($field->convertUnicodeDbSlug())) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
}
}
}
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$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;
}
if (isset($target)) {
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Delete a given asset (mark as deleted).
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function destroy($id)
{
$this->authorize('delete', Asset::class);
if ($asset = Asset::find($id)) {
$this->authorize('delete', $asset);
DB::table('assets')
->where('id', $asset->id)
->update(array('assigned_to' => null));
$asset->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Checkout an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function checkout(AssetCheckoutRequest $request, $asset_id)
{
$this->authorize('checkout', Asset::class);
$asset = Asset::findOrFail($asset_id);
if (!$asset->availableForCheckout()) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
}
$this->authorize('checkout', $asset);
$error_payload = [];
$error_payload['asset'] = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
// This item is checked out to a location
if (request('checkout_to_type')=='location') {
$target = Location::find(request('assigned_location'));
$asset->location_id = ($target) ? $target->id : '';
$error_payload['target_id'] = $request->input('assigned_location');
$error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type')=='asset') {
$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
$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) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_user');
$error_payload['target_type'] = 'user';
}
if (!isset($target)) {
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
}
$checkout_at = request('checkout_at', date("Y-m-d H:i:s"));
$expected_checkin = request('expected_checkin', null);
$note = request('note', null);
$asset_name = request('name', null);
// Set the location ID to the RTD location id if there is one
// 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;
// }
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
}
/**
* Checkin an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function checkin(Request $request, $asset_id)
{
$this->authorize('checkin', Asset::class);
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkin', $asset);
$user = $asset->assignedUser;
if (is_null($target = $asset->assignedTo)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in')));
}
$asset->expected_checkin = null;
$asset->last_checkout = null;
$asset->assigned_to = null;
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
if ($request->filled('name')) {
$asset->name = $request->input('name');
}
$asset->location_id = $asset->rtd_location_id;
if ($request->filled('location_id')) {
$asset->location_id = $request->input('location_id');
}
if (Input::has('status_id')) {
$asset->status_id = Input::get('status_id');
}
if ($asset->save()) {
$asset->logCheckin($target, e(request('note')));
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
}
/**
* Mark an asset as audited
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v4.0]
* @return JsonResponse
*/
public function audit(Request $request) {
$this->authorize('audit', Asset::class);
$rules = array(
'asset_tag' => 'required',
'location_id' => 'exists:locations,id|nullable|numeric',
'next_audit_date' => 'date|nullable'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
}
$asset = Asset::where('asset_tag','=', $request->input('asset_tag'))->first();
if ($asset) {
// We don't want to log this as a normal update, so let's bypass that
$asset->unsetEventDispatcher();
$asset->next_audit_date = $request->input('next_audit_date');
$asset->last_audit_date = date('Y-m-d h:i:s');
if ($asset->save()) {
$log = $asset->logAudit(request('note'),request('location_id'));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag'=> e($asset->asset_tag),
'note'=> e($request->input('note')),
'next_audit_date' => Helper::getFormattedDateObject($log->calcNextAuditDate())
], trans('admin/hardware/message.audit.success')));
}
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found'));
}
/**
* Returns JSON listing of all requestable assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return JsonResponse
*/
public function requestable(Request $request)
{
$this->authorize('viewRequestable', Asset::class);
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$assets->TextSearch($request->input('search'));
switch ($request->input('sort')) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
default:
$assets->orderBy('assets.created_at', $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Category;
use App\Http\Transformers\CategoriesTransformer;
use App\Http\Transformers\SelectlistTransformer;
class CategoriesController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Category::class);
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count','licenses_count', 'image'];
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email','image'])
->withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count','licenses as licenses_count');
if ($request->filled('search')) {
$categories = $categories->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
$categories->orderBy($sort, $order);
$total = $categories->count();
$categories = $categories->skip($offset)->take($limit)->get();
return (new CategoriesTransformer)->transformCategories($categories, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Category::class);
$category = new Category;
$category->fill($request->all());
if ($category->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $category->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Category::class);
$category = Category::findOrFail($id);
return (new CategoriesTransformer)->transformCategory($category);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Category::class);
$category = Category::findOrFail($id);
$category->fill($request->all());
if ($category->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $category->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Category::class);
$category = Category::findOrFail($id);
if ($category->has_models() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'model'])));
} elseif ($category->accessories()->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory'])));
} elseif ($category->consumables()->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable'])));
} elseif ($category->components()->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'component'])));
}
$category->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request, $category_type = 'asset')
{
$categories = Category::select([
'id',
'name',
'image',
]);
if ($request->filled('search')) {
$categories = $categories->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$categories = $categories->where('category_type', $category_type)->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($categories as $category) {
$category->use_image = ($category->image) ? url('/').'/uploads/categories/'.$category->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($categories);
}
}

View File

@@ -0,0 +1,191 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Transformers\CompaniesTransformer;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Company;
use App\Http\Transformers\SelectlistTransformer;
class CompaniesController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Company::class);
$allowed_columns = [
'id',
'name',
'created_at',
'updated_at',
'users_count',
'assets_count',
'licenses_count',
'accessories_count',
'consumables_count',
'components_count',
];
$companies = Company::withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count','components as components_count','users as users_count');
if ($request->filled('search')) {
$companies->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$companies->orderBy($sort, $order);
$total = $companies->count();
$companies = $companies->skip($offset)->take($limit)->get();
return (new CompaniesTransformer)->transformCompanies($companies, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Company::class);
$company = new Company;
$company->fill($request->all());
if ($company->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new CompaniesTransformer)->transformCompany($company), trans('admin/companies/message.create.success')));
}
return response()
->json(Helper::formatStandardApiResponse('error', null, $company->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Company::class);
$company = Company::findOrFail($id);
return (new CompaniesTransformer)->transformCompany($company);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Company::class);
$company = Company::findOrFail($id);
$company->fill($request->all());
if ($company->save()) {
return response()
->json(Helper::formatStandardApiResponse('success', (new CompaniesTransformer)->transformCompany($company), trans('admin/companies/message.update.success')));
}
return response()
->json(Helper::formatStandardApiResponse('error', null, $company->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Company::class);
$company = Company::findOrFail($id);
$this->authorize('delete', $company);
try {
$company->delete();
return response()
->json(Helper::formatStandardApiResponse('success', null, trans('admin/companies/message.delete.success')));
} catch (\Illuminate\Database\QueryException $exception) {
/*
* NOTE: This happens when there's a foreign key constraint violation
* For example when rows in other tables are referencing this company
*/
if ($exception->getCode() == 23000) {
return response()
->json(Helper::formatStandardApiResponse('error', null, trans('admin/companies/message.assoc_users')));
} else {
throw $exception;
}
}
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$companies = Company::select([
'companies.id',
'companies.name',
'companies.image',
]);
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}
$companies = $companies->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($companies as $company) {
$company->use_image = ($company->image) ? url('/').'/uploads/companies/'.$company->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($companies);
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\ComponentsTransformer;
use App\Http\Transformers\ComponentsAssetsTransformer;
use App\Models\Component;
use App\Models\Company;
use App\Helpers\Helper;
class ComponentsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Component::class);
$components = Company::scopeCompanyables(Component::select('components.*')
->with('company', 'location', 'category'));
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
}
if ($request->filled('company_id')) {
$components->where('company_id','=',$request->input('company_id'));
}
if ($request->filled('category_id')) {
$components->where('category_id','=',$request->input('category_id'));
}
if ($request->filled('location_id')) {
$components->where('location_id','=',$request->input('location_id'));
}
// 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');
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location','image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($sort) {
case 'category':
$components = $components->OrderCategory($order);
break;
case 'location':
$components = $components->OrderLocation($order);
break;
case 'company':
$components = $components->OrderCompany($order);
break;
default:
$components = $components->orderBy($sort, $order);
break;
}
$total = $components->count();
$components = $components->skip($offset)->take($limit)->get();
return (new ComponentsTransformer)->transformComponents($components, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Component::class);
$component = new Component;
$component->fill($request->all());
if ($component->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $component->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Component::class);
$component = Component::findOrFail($id);
if ($component) {
return (new ComponentsTransformer)->transformComponent($component);
}
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Component::class);
$component = Component::findOrFail($id);
$component->fill($request->all());
if ($component->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $component->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Component::class);
$component = Component::findOrFail($id);
$this->authorize('delete', $component);
$component->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.delete.success')));
}
/**
* Display all assets attached to a component
*
* @author [A. Bergamasco] [@vjandrea]
* @since [v4.0]
* @param Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function getAssets(Request $request, $id)
{
$this->authorize('view', \App\Models\Asset::class);
$component = Component::findOrFail($id);
$assets = $component->assets();
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
}
}

View File

@@ -0,0 +1,251 @@
<?php
namespace App\Http\Controllers\Api;
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;
class ConsumablesController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('index', Consumable::class);
$consumables = Company::scopeCompanyables(
Consumable::select('consumables.*')
->with('company', 'location', 'category', 'users', 'manufacturer')
);
if ($request->filled('search')) {
$consumables = $consumables->TextSearch(e($request->input('search')));
}
if ($request->filled('company_id')) {
$consumables->where('company_id','=',$request->input('company_id'));
}
if ($request->filled('category_id')) {
$consumables->where('category_id','=',$request->input('category_id'));
}
if ($request->filled('manufacturer_id')) {
$consumables->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
// 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');
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty','image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($sort) {
case 'category':
$consumables = $consumables->OrderCategory($order);
break;
case 'location':
$consumables = $consumables->OrderLocation($order);
break;
case 'manufacturer':
$consumables = $consumables->OrderManufacturer($order);
break;
case 'company':
$consumables = $consumables->OrderCompany($order);
break;
default:
$consumables = $consumables->orderBy($sort, $order);
break;
}
$total = $consumables->count();
$consumables = $consumables->skip($offset)->take($limit)->get();
return (new ConsumablesTransformer)->transformConsumables($consumables, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Consumable::class);
$consumable = new Consumable;
$consumable->fill($request->all());
if ($consumable->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $consumable->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Consumable::class);
$consumable = Consumable::findOrFail($id);
return (new ConsumablesTransformer)->transformConsumable($consumable);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Consumable::class);
$consumable = Consumable::findOrFail($id);
$consumable->fill($request->all());
if ($consumable->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $consumable->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Consumable::class);
$consumable = Consumable::findOrFail($id);
$this->authorize('delete', $consumable);
$consumable->delete();
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>]
* @see ConsumablesController::getView() method that returns the form.
* @since [v1.0]
* @param int $consumableId
* @return array
*/
public function getDataView($consumableId)
{
$consumable = Consumable::with(array('consumableAssignments'=>
function ($query) {
$query->orderBy($query->getModel()->getTable().'.created_at', 'DESC');
},
'consumableAssignments.admin'=> function ($query) {
},
'consumableAssignments.user'=> function ($query) {
},
))->find($consumableId);
if (!Company::isCurrentUserHasAccess($consumable)) {
return ['total' => 0, 'rows' => []];
}
$this->authorize('view', Consumable::class);
$rows = array();
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
];
}
$consumableCount = $consumable->users->count();
$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

@@ -0,0 +1,192 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\CustomFieldsTransformer;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Http\Request;
use Validator;
use Illuminate\Validation\Rule;
class CustomFieldsController extends Controller
{
/**
* Reorder the custom fields within a fieldset
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v3.0]
* @return Array
*/
public function index()
{
$this->authorize('index', CustomField::class);
$fields = CustomField::get();
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
}
/**
* Shows the given field
* @author [V. Cordes] [<volker@fdatek.de>]
* @param int $id
* @since [v4.1.10]
* @return View
*/
public function show($id)
{
$this->authorize('view', CustomField::class);
if ($field = CustomField::find($id)) {
return (new CustomFieldsTransformer)->transformCustomField($field);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/custom_fields/message.field.invalid')), 200);
}
/**
* Update the specified field
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', CustomField::class);
$field = CustomField::findOrFail($id);
/**
* Updated values for the field,
* without the "field_encrypted" flag, preventing the change of encryption status
* @var array
*/
$data = $request->except(['field_encrypted']);
$validator = Validator::make($data, $field->validationRules());
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
$field->fill($data);
if ($field->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
}
/**
* Store a newly created field.
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', CustomField::class);
$field = new CustomField;
$data = $request->all();
$validator = Validator::make($data, $field->validationRules());
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
$field->fill($data);
if ($field->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
}
public function postReorder(Request $request, $id)
{
$fieldset = CustomFieldset::find($id);
$this->authorize('update', $fieldset);
$fields = array();
$order_array = array();
$items = $request->input('item');
foreach ($items as $order => $field_id) {
$order_array[$field_id] = $order;
}
foreach ($fieldset->fields as $field) {
$fields[$field->id] = ['required' => $field->pivot->required, 'order' => $order_array[$field->id]];
}
return $fieldset->fields()->sync($fields);
}
public function associate(Request $request, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset_id = $request->input('fieldset_id');
foreach ($field->fieldset as $fieldset) {
if ($fieldset->id == $fieldset_id) {
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
}
$fieldset = CustomFieldset::findOrFail($fieldset_id);
$fieldset->fields()->attach($field->id, ["required" => ($request->input('required') == "on"), "order" => $request->input('order', $fieldset->fields->count())]);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
public function disassociate(Request $request, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset_id = $request->input('fieldset_id');
foreach ($field->fieldset as $fieldset) {
if ($fieldset->id == $fieldset_id) {
$fieldset->fields()->detach($field->id);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
}
$fieldset = CustomFieldset::findOrFail($fieldset_id);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
/**
* Delete a custom field.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return Redirect
*/
public function destroy($field_id)
{
$field = CustomField::findOrFail($field_id);
$this->authorize('delete', $field);
if ($field->fieldset->count() >0) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Field is in use.'));
}
$field->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\Api;
use View;
use App\Models\CustomFieldset;
use App\Models\CustomField;
use Input;
use Validator;
use Redirect;
use App\Models\AssetModel;
use Lang;
use Auth;
use Illuminate\Http\Request;
use Log;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Http\Transformers\CustomFieldsTransformer;
use App\Http\Transformers\CustomFieldsetsTransformer;
use App\Http\Requests\AssetRequest;
/**
* This controller handles all actions related to Custom Asset Fieldsets for
* the Snipe-IT Asset Management application.
*
* @todo Improve documentation here.
* @todo Check for raw DB queries and try to convert them to query builder statements
* @version v2.0
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @author [Josh Gibson]
*/
class CustomFieldsetsController extends Controller
{
/**
* Shows the given fieldset and its fields
* @author [A. Gianotto] [<snipe@snipe.net>]
* @author [Josh Gibson]
* @param int $id
* @since [v1.8]
* @return View
*/
public function index()
{
$this->authorize('index', CustomFieldset::class);
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
}
/**
* Shows the given fieldset and its fields
* @author [A. Gianotto] [<snipe@snipe.net>]
* @author [Josh Gibson]
* @param int $id
* @since [v1.8]
* @return View
*/
public function show($id)
{
$this->authorize('view', CustomFieldset::class);
if ($fieldset = CustomFieldset::find($id)) {
return (new CustomFieldsetsTransformer)->transformCustomFieldset($fieldset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/custom_fields/message.fieldset.does_not_exist')), 200);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', CustomFieldset::class);
$fieldset = CustomFieldset::findOrFail($id);
$fieldset->fill($request->all());
if ($fieldset->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $fieldset->getErrors()));
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', CustomFieldset::class);
$fieldset = new CustomFieldset;
$fieldset->fill($request->all());
if ($fieldset->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $fieldset->getErrors()));
}
/**
* Delete a custom fieldset.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return Redirect
*/
public function destroy($id)
{
$this->authorize('delete', CustomFieldset::class);
$fieldset = CustomFieldset::findOrFail($id);
$modelsCount = $fieldset->models->count();
$fieldsCount = $fieldset->fields->count();
if (($modelsCount > 0) || ($fieldsCount > 0) ){
return response()->json(Helper::formatStandardApiResponse('error', null, 'Fieldset is in use.'));
}
if ($fieldset->delete()) {
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.fieldset.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Unspecified error'));
}
/**
* Return JSON containing a list of fields belonging to a fieldset.
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param $fieldsetId
* @return string JSON
*/
public function fields($id)
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($id);
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
}
/**
* Return JSON containing a list of fields belonging to a fieldset with the
* default values for a given model
*
* @param $modelId
* @param $fieldsetId
* @return string JSON
*/
public function fieldsWithDefaultValues($fieldsetId, $modelId)
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($fieldsetId);
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFieldsWithDefaultValues($fields, $modelId, $fields->count());
}
}

View File

@@ -0,0 +1,190 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Department;
use App\Http\Transformers\DepartmentsTransformer;
use App\Helpers\Helper;
use Auth;
use App\Http\Transformers\SelectlistTransformer;
class DepartmentsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [Godfrey Martinez] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Department::class);
$allowed_columns = ['id','name','image','users_count'];
$departments = Department::select([
'departments.id',
'departments.name',
'departments.location_id',
'departments.company_id',
'departments.manager_id',
'departments.created_at',
'departments.updated_at',
'departments.image'
])->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
if ($request->filled('search')) {
$departments = $departments->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($request->input('sort')) {
case 'location':
$departments->OrderLocation($order);
break;
case 'manager':
$departments->OrderManager($order);
break;
default:
$departments->orderBy($sort, $order);
break;
}
$total = $departments->count();
$departments = $departments->skip($offset)->take($limit)->get();
return (new DepartmentsTransformer)->transformDepartments($departments, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Department::class);
$department = new Department;
$department->fill($request->all());
$department->user_id = Auth::user()->id;
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
if ($department->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Department::class);
$department = Department::findOrFail($id);
return (new DepartmentsTransformer)->transformDepartment($department);
}
/**
* Validates and deletes selected location.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $locationId
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($id)
{
$department = Department::findOrFail($id);
$this->authorize('delete', $department);
if ($department->users->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/departments/message.assoc_users')));
}
$department->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/departments/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$departments = Department::select([
'id',
'name',
'image',
]);
if ($request->filled('search')) {
$departments = $departments->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$departments = $departments->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($departments as $department) {
$department->use_image = ($department->image) ? url('/').'/uploads/departments/'.$department->image : null;
}
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

@@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Depreciation;
use App\Http\Transformers\DepreciationsTransformer;
class DepreciationsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Depreciation::class);
$allowed_columns = ['id','name','created_at'];
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');
if ($request->filled('search')) {
$depreciations = $depreciations->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$depreciations->orderBy($sort, $order);
$total = $depreciations->count();
$depreciations = $depreciations->skip($offset)->take($limit)->get();
return (new DepreciationsTransformer)->transformDepreciations($depreciations, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Depreciation::class);
$depreciation = new Depreciation;
$depreciation->fill($request->all());
if ($depreciation->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $depreciation, trans('admin/depreciations/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $depreciation->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Depreciation::class);
$depreciation = Depreciation::findOrFail($id);
return (new DepreciationsTransformer)->transformDepreciation($depreciation);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Depreciation::class);
$depreciation = Depreciation::findOrFail($id);
$depreciation->fill($request->all());
if ($depreciation->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $depreciation, trans('admin/depreciations/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $depreciation->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Depreciation::class);
$depreciation = Depreciation::findOrFail($id);
$this->authorize('delete', $depreciation);
if ($depreciation->has_models() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', trans('admin/depreciations/message.assoc_users')));
}
$depreciation->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/depreciations/message.delete.success')));
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Group;
use App\Http\Transformers\GroupsTransformer;
class GroupsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Group::class);
$allowed_columns = ['id','name','created_at', 'users_count'];
$groups = Group::select('id','name','permissions','created_at','updated_at')->withCount('users as users_count');
if ($request->filled('search')) {
$groups = $groups->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$groups->orderBy($sort, $order);
$total = $groups->count();
$groups = $groups->skip($offset)->take($limit)->get();
return (new GroupsTransformer)->transformGroups($groups, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Group::class);
$group = new Group;
$group->fill($request->all());
if ($group->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Group::class);
$group = Group::findOrFail($id);
return (new GroupsTransformer)->transformGroup($group);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Group::class);
$group = Group::findOrFail($id);
$group->fill($request->all());
if ($group->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Group::class);
$group = Group::findOrFail($id);
$this->authorize('delete', $group);
$group->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/groups/message.delete.success')));
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ItemImportRequest;
use App\Http\Transformers\ImportsTransformer;
use App\Models\Company;
use App\Models\Import;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Session;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Artisan;
use App\Models\Asset;
class ImportController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$this->authorize('import');
$imports = Import::latest()->get();
return (new ImportsTransformer)->transformImports($imports);
}
/**
* Process and store a CSV upload file.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store()
{
$this->authorize('import');
if (!config('app.lock_passwords')) {
$files = Input::file('files');
$path = config('app.private_uploads').'/imports';
$results = [];
$import = new Import;
foreach ($files as $file) {
if (!in_array($file->getMimeType(), array(
'application/vnd.ms-excel',
'text/csv',
'text/plain',
'text/comma-separated-values',
'text/tsv'))) {
$results['error']='File type must be CSV';
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
}
//TODO: is there a lighter way to do this?
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
$import->header_row = $reader->fetchOne(0);
//duplicate headers check
$duplicate_headers = [];
for($i = 0; $i<count($import->header_row); $i++) {
$header = $import->header_row[$i];
if(in_array($header, $import->header_row)) {
$found_at = array_search($header, $import->header_row);
if($i > $found_at) {
//avoid reporting duplicates twice, e.g. "1 is same as 17! 17 is same as 1!!!"
//as well as "1 is same as 1!!!" (which is always true)
//has to be > because otherwise the first result of array_search will always be $i itself(!)
array_push($duplicate_headers,"Duplicate header '$header' detected, first at column: ".($found_at+1).", repeats at column: ".($i+1));
}
}
}
if(count($duplicate_headers) > 0) {
return response()->json(Helper::formatStandardApiResponse('error',null, implode("; ",$duplicate_headers)), 500); //should this be '4xx'?
}
// Grab the first row to display via ajax as the user picks fields
$import->first_row = $reader->fetchOne(1);
$date = date('Y-m-d-his');
$fixed_filename = str_slug($file->getClientOriginalName());
try {
$file->move($path, $date.'-'.$fixed_filename);
} catch (FileException $exception) {
$results['error']=trans('admin/hardware/message.upload.error');
if (config('app.debug')) {
$results['error'].= ' ' . $exception->getMessage();
}
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
}
$file_name = date('Y-m-d-his').'-'.$fixed_filename;
$import->file_path = $file_name;
$import->filesize = filesize($path.'/'.$file_name);
$import->save();
$results[] = $import;
}
$results = (new ImportsTransformer)->transformImports($results);
return [
'files' => $results,
];
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.feature_disabled')), 500);
}
/**
* Processes the specified Import.
*
* @param \App\Import $import
* @return \Illuminate\Http\Response
*/
public function process(ItemImportRequest $request, $import_id)
{
$this->authorize('import');
// Run a backup immediately before processing
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')) {
case "asset":
$redirectTo = "hardware.index";
break;
case "accessory":
$redirectTo = "accessories.index";
break;
case "consumable":
$redirectTo = "consumables.index";
break;
case "component":
$redirectTo = "components.index";
break;
case "license":
$redirectTo = "licenses.index";
break;
case "user":
$redirectTo = "users.index";
break;
}
if ($errors) { //Failure
return response()->json(Helper::formatStandardApiResponse('import-errors', null, $errors), 500);
}
//Flash message before the redirect
Session::flash('success', trans('admin/hardware/message.import.success'));
return response()->json(Helper::formatStandardApiResponse('success', null, ['redirect_url' => route($redirectTo)]));
}
/**
* Remove the specified resource from storage.
*
* @param \App\Import $import
* @return \Illuminate\Http\Response
*/
public function destroy($import_id)
{
$this->authorize('import');
$import = Import::find($import_id);
try {
unlink(config('app.private_uploads').'/imports/'.$import->file_path);
$import->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.import.file_delete_success')));
} catch (\Exception $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.import.file_delete_error')), 500);
}
}
}

View File

@@ -0,0 +1,261 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Models\Company;
use App\Models\License;
use App\Models\LicenseSeat;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class LicensesController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', License::class);
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats as free_seats_count'));
if ($request->filled('company_id')) {
$licenses->where('company_id','=',$request->input('company_id'));
}
if ($request->filled('name')) {
$licenses->where('licenses.name','=',$request->input('name'));
}
if ($request->filled('product_key')) {
$licenses->where('licenses.serial','=',$request->input('product_key'));
}
if ($request->filled('order_number')) {
$licenses->where('order_number','=',$request->input('order_number'));
}
if ($request->filled('purchase_order')) {
$licenses->where('purchase_order','=',$request->input('purchase_order'));
}
if ($request->filled('license_name')) {
$licenses->where('license_name','=',$request->input('license_name'));
}
if ($request->filled('license_email')) {
$licenses->where('license_email','=',$request->input('license_email'));
}
if ($request->filled('manufacturer_id')) {
$licenses->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
if ($request->filled('supplier_id')) {
$licenses->where('supplier_id','=',$request->input('supplier_id'));
}
if ($request->filled('category_id')) {
$licenses->where('category_id','=',$request->input('category_id'));
}
if ($request->filled('depreciation_id')) {
$licenses->where('depreciation_id','=',$request->input('depreciation_id'));
}
if ($request->filled('supplier_id')) {
$licenses->where('supplier_id','=',$request->input('supplier_id'));
}
if ($request->filled('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
switch ($request->input('sort')) {
case 'manufacturer':
$licenses = $licenses->leftJoin('manufacturers', 'licenses.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
break;
case 'supplier':
$licenses = $licenses->leftJoin('suppliers', 'licenses.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
break;
case 'category':
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
break;
case 'company':
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
break;
default:
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$licenses = $licenses->orderBy($sort, $order);
break;
}
$total = $licenses->count();
$licenses = $licenses->skip($offset)->take($limit)->get();
return (new LicensesTransformer)->transformLicenses($licenses, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
$this->authorize('create', License::class);
$license = new License;
$license->fill($request->all());
if($license->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $license->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', License::class);
$license = License::findOrFail($id);
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
return (new LicensesTransformer)->transformLicense($license);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
$this->authorize('update', License::class);
$license = License::findOrFail($id);
$license->fill($request->all());
if ($license->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.update.success')));
}
return Helper::formatStandardApiResponse('error', null, $license->getErrors());
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
$license = License::findOrFail($id);
$this->authorize('delete', $license);
if($license->assigned_seats_count == 0) {
// Delete the license and the associated license seats
DB::table('license_seats')
->where('id', $license->id)
->update(array('assigned_to' => null,'asset_id' => null));
$licenseSeats = $license->licenseseats();
$licenseSeats->delete();
$license->delete();
// Redirect to the licenses management page
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/licenses/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
}
/**
* Get license seat listing
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $licenseId
* @return \Illuminate\Contracts\View\View
*/
public function seats(Request $request, $licenseId)
{
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::where('license_seats.license_id', $licenseId)
->with('license', 'user', 'asset', 'user.department');
$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) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Location;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
class LocationsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Location::class);
$allowed_columns = [
'id','name','address','address2','city','state','country','zip','created_at',
'updated_at','manager_id','image',
'assigned_assets_count','users_count','assets_count','currency'];
$locations = Location::with('parent', 'manager', 'children')->select([
'locations.id',
'locations.name',
'locations.address',
'locations.address2',
'locations.city',
'locations.state',
'locations.zip',
'locations.country',
'locations.parent_id',
'locations.manager_id',
'locations.created_at',
'locations.updated_at',
'locations.image',
'locations.currency'
])->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('users as users_count');
if ($request->filled('search')) {
$locations = $locations->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($request->input('sort')) {
case 'parent':
$locations->OrderParent($order);
break;
case 'manager':
$locations->OrderManager($order);
break;
default:
$locations->orderBy($sort, $order);
break;
}
$total = $locations->count();
$locations = $locations->skip($offset)->take($limit)->get();
return (new LocationsTransformer)->transformLocations($locations, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Location::class);
$location = new Location;
$location->fill($request->all());
if ($location->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Location::class);
$location = Location::with('parent', 'manager', 'children')
->select([
'locations.id',
'locations.name',
'locations.address',
'locations.address2',
'locations.city',
'locations.state',
'locations.zip',
'locations.country',
'locations.parent_id',
'locations.manager_id',
'locations.created_at',
'locations.updated_at',
'locations.image',
'locations.currency'
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('users as users_count')->findOrFail($id);
return (new LocationsTransformer)->transformLocation($location);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Location::class);
$location = Location::findOrFail($id);
if ($request->input('parent_id') == $id) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'A location cannot be its own parent. Please select a different parent ID.'));
}
$location->fill($request->all());
if ($location->save()) {
return response()->json(
Helper::formatStandardApiResponse(
'success',
(new LocationsTransformer)->transformLocation($location),
trans('admin/locations/message.update.success')
)
);
}
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Location::class);
$location = Location::findOrFail($id);
$this->authorize('delete', $location);
$location->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* This is handled slightly differently as of ~4.7.8-pre, as
* we have to do some recursive magic to get the hierarchy to display
* properly when looking at the parent/child relationship in the
* rich menus.
*
* This means we can't use the normal pagination that we use elsewhere
* in our selectlists, since we have to get the full set before we can
* determine which location is parent/child/grandchild, etc.
*
* This also means that hierarchy display gets a little funky when people
* use the Select2 search functionality, but there's not much we can do about
* that right now.
*
* As a result, instead of paginating as part of the query, we have to grab
* the entire data set, and then invoke a paginator manually and pass that
* through to the SelectListTransformer.
*
* Many thanks to @uberbrady for the help getting this working better.
* Recursion still sucks, but I guess he doesn't have to get in the
* sea... this time.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$locations = Location::select([
'locations.id',
'locations.name',
'locations.parent_id',
'locations.image',
]);
$page = 1;
if ($request->filled('page')) {
$page = $request->input('page');
}
if ($request->filled('search')) {
$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)) {
$locations_with_children[$location->parent_id] = [];
}
$locations_with_children[$location->parent_id][] = $location;
}
if ($request->filled('search')) {
$locations_formatted = $locations;
} else {
$location_options = Location::indenter($locations_with_children);
$locations_formatted = new Collection($location_options);
}
$paginated_results = new LengthAwarePaginator($locations_formatted->forPage($page, 500), $locations_formatted->count(), 500, $page, []);
//return [];
return (new SelectlistTransformer)->transformSelectlist($paginated_results);
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Manufacturer;
use App\Http\Transformers\DatatablesTransformer;
use App\Http\Transformers\ManufacturersTransformer;
use App\Http\Transformers\SelectlistTransformer;
class ManufacturersController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Manufacturer::class);
$allowed_columns = ['id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count'];
$manufacturers = Manufacturer::select(
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'deleted_at')
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count');
if ($request->input('deleted')=='true') {
$manufacturers->onlyTrashed();
}
if ($request->filled('search')) {
$manufacturers = $manufacturers->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$manufacturers->orderBy($sort, $order);
$total = $manufacturers->count();
$manufacturers = $manufacturers->skip($offset)->take($limit)->get();
return (new ManufacturersTransformer)->transformManufacturers($manufacturers, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Manufacturer::class);
$manufacturer = new Manufacturer;
$manufacturer->fill($request->all());
if ($manufacturer->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $manufacturer->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Manufacturer::class);
$manufacturer = Manufacturer::withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count')->findOrFail($id);
return (new ManufacturersTransformer)->transformManufacturer($manufacturer);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Manufacturer::class);
$manufacturer = Manufacturer::findOrFail($id);
$manufacturer->fill($request->all());
if ($manufacturer->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $manufacturer->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Manufacturer::class);
$manufacturer = Manufacturer::findOrFail($id);
$this->authorize('delete', $manufacturer);
$manufacturer->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$manufacturers = Manufacturer::select([
'id',
'name',
'image',
]);
if ($request->filled('search')) {
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$manufacturers = $manufacturers->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($manufacturers as $manufacturer) {
$manufacturer->use_text = $manufacturer->name;
$manufacturer->use_image = ($manufacturer->image) ? url('/').'/uploads/manufacturers/'.$manufacturer->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($manufacturers);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\CheckoutRequest;
use App\Http\Controllers\Controller;
use Auth;
use App\Helpers\Helper;
class ProfileController extends Controller
{
/**
* Display a listing of requested assets.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.3.0]
*
* @return Array
*/
public function requestedAssets()
{
$checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
$results = [];
$results['total'] = $checkoutRequests->count();
foreach ($checkoutRequests as $checkoutRequest) {
// Make sure the asset and request still exist
if ($checkoutRequest && $checkoutRequest->itemRequested()) {
$results['rows'][] = [
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
'name' => $checkoutRequest->itemRequested()->present()->name(),
'type' => $checkoutRequest->itemType(),
'qty' => $checkoutRequest->quantity,
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
];
}
}
return $results;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Actionlog;
use App\Http\Transformers\ActionlogsTransformer;
class ReportsController extends Controller
{
/**
* Returns Activity Report JSON.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return View
*/
public function index(Request $request)
{
$this->authorize('reports.view');
$actionlogs = Actionlog::with('item', 'user', 'target','location');
if ($request->filled('search')) {
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
}
if (($request->filled('target_type')) && ($request->filled('target_id'))) {
$actionlogs = $actionlogs->where('target_id','=',$request->input('target_id'))
->where('target_type','=',"App\\Models\\".ucwords($request->input('target_type')));
}
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
$actionlogs = $actionlogs->where('item_id','=',$request->input('item_id'))
->where('item_type','=',"App\\Models\\".ucwords($request->input('item_type')));
}
if ($request->filled('action_type')) {
$actionlogs = $actionlogs->where('action_type','=',$request->input('action_type'))->orderBy('created_at', 'desc');
}
if ($request->filled('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
}
$allowed_columns = [
'id',
'created_at',
'target_id',
'user_id',
'action_type',
'note'
];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
$offset = request('offset', 0);
$limit = request('limit', 50);
$total = $actionlogs->count();
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Ldap;
use Validator;
use App\Models\Setting;
use Mail;
use App\Notifications\SlackTest;
use Notification;
use App\Notifications\MailTest;
class SettingsController extends Controller
{
public function ldaptest()
{
if (Setting::getSettings()->ldap_enabled!='1') {
\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');
try {
$connection = Ldap::connectToLdap();
try {
\Log::debug('attempting to bind to LDAP for LDAP test');
Ldap::bindAdminToLdap($connection);
return response()->json(['message' => 'It worked!'], 200);
} catch (\Exception $e) {
\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::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);
}
}
public function ldaptestlogin(Request $request)
{
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);
}
$rules = array(
'ldaptest_user' => 'required',
'ldaptest_password' => 'required'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
\Log::debug('LDAP Validation test failed.');
$validation_errors = implode(' ',$validator->errors()->all());
return response()->json(['message' => $validator->errors()->all()], 400);
}
\Log::debug('Preparing to test LDAP login');
try {
$connection = Ldap::connectToLdap();
try {
Ldap::bindAdminToLdap($connection);
\Log::debug('Attempting to bind to LDAP for LDAP test');
try {
$ldap_user = Ldap::findAndBindUserLdap($request->input('ldaptest_user'), $request->input('ldaptest_password'));
if ($ldap_user) {
\Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
}
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
} catch (\Exception $e) {
\Log::debug('LDAP login failed');
return response()->json(['message' => $e->getMessage()], 400);
}
} catch (\Exception $e) {
\Log::debug('Bind failed');
return response()->json(['message' => $e->getMessage()], 400);
//return response()->json(['message' => $e->getMessage()], 500);
}
} catch (\Exception $e) {
\Log::debug('Connection failed');
return response()->json(['message' => $e->getMessage()], 500);
}
}
public function slacktest()
{
if ($settings = Setting::getSettings()->slack_channel=='') {
\Log::debug('Slack is not enabled. Cannot test.');
return response()->json(['message' => 'Slack is not enabled, cannot test.'], 400);
}
\Log::debug('Preparing to test slack connection');
try {
Notification::send($settings = Setting::getSettings(), new SlackTest());
return response()->json(['message' => 'Success'], 200);
} catch (\Exception $e) {
\Log::debug('Slack connection failed');
return response()->json(['message' => $e->getMessage()], 400);
}
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
if (!config('app.lock_passwords')) {
try {
Notification::send(Setting::first(), new MailTest());
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
return response()->json(['message' => 'Mail would have been sent, but this application is in demo mode! '], 200);
}
}

View File

@@ -0,0 +1,254 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Statuslabel;
use App\Models\Asset;
use App\Http\Transformers\StatuslabelsTransformer;
use App\Http\Transformers\AssetsTransformer;
class StatuslabelsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Statuslabel::class);
$allowed_columns = ['id','name','created_at', 'assets_count','color','default_label'];
$statuslabels = Statuslabel::withCount('assets as assets_count');
if ($request->filled('search')) {
$statuslabels = $statuslabels->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$statuslabels->orderBy($sort, $order);
$total = $statuslabels->count();
$statuslabels = $statuslabels->skip($offset)->take($limit)->get();
return (new StatuslabelsTransformer)->transformStatuslabels($statuslabels, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Statuslabel::class);
$request->except('deployable', 'pending','archived');
if (!$request->filled('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]),500);
}
$statuslabel = new Statuslabel;
$statuslabel->fill($request->all());
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
$statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived'];
if ($statuslabel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $statuslabel->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
return (new StatuslabelsTransformer)->transformStatuslabel($statuslabel);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$request->except('deployable', 'pending','archived');
if (!$request->filled('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Status label type is required.'));
}
$statuslabel->fill($request->all());
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
$statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived'];
if ($statuslabel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $statuslabel->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$this->authorize('delete', $statuslabel);
// Check that there are no assets associated
if ($statuslabel->assets()->count() == 0) {
$statuslabel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets')));
}
/**
* Show a count of assets by status label for pie chart
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return \Illuminate\Http\Response
*/
public function getAssetCountByStatuslabel()
{
$this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets as assets_count')->get();
$labels=[];
$points=[];
$colors=[];
foreach ($statuslabels as $statuslabel) {
if ($statuslabel->assets_count > 0) {
$labels[]=$statuslabel->name. ' ('.number_format($statuslabel->assets_count).')';
$points[]=$statuslabel->assets_count;
if ($statuslabel->color!='') {
$colors[]=$statuslabel->color;
}
}
}
$colors_array = array_merge($colors, Helper::chartColors());
$result= [
"labels" => $labels,
"datasets" => [ [
"data" => $points,
"backgroundColor" => $colors_array,
"hoverBackgroundColor" => $colors_array
]]
];
return $result;
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function assets(Request $request, $id)
{
$this->authorize('view', Statuslabel::class);
$this->authorize('index', Asset::class);
$assets = Asset::where('status_id','=',$id)->with('assignedTo');
$allowed_columns = [
'id',
'name'
];
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$assets->orderBy($sort, $order);
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformAssets($assets, $total);
}
/**
* Returns a boolean response based on whether the status label
* is one that is deployable.
*
* This is used by the hardware create/edit view to determine whether
* we should provide a dropdown of users for them to check the asset out to.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return Bool
*/
public function checkIfDeployable($id) {
$statuslabel = Statuslabel::findOrFail($id);
$this->authorize('view', Asset::class);
if ($statuslabel->getStatuslabelType()=='deployable') {
return '1';
}
return '0';
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Supplier;
use App\Http\Transformers\SuppliersTransformer;
use App\Http\Transformers\SelectlistTransformer;
class SuppliersController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count'];
$suppliers = Supplier::select(
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
if ($request->filled('search')) {
$suppliers = $suppliers->TextSearch($request->input('search'));
}
// 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');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$suppliers->orderBy($sort, $order);
$total = $suppliers->count();
$suppliers = $suppliers->skip($offset)->take($limit)->get();
return (new SuppliersTransformer)->transformSuppliers($suppliers, $total);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', Supplier::class);
$supplier = new Supplier;
$supplier->fill($request->all());
if ($supplier->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $supplier->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Supplier::class);
$supplier = Supplier::findOrFail($id);
return (new SuppliersTransformer)->transformSupplier($supplier);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', Supplier::class);
$supplier = Supplier::findOrFail($id);
$supplier->fill($request->all());
if ($supplier->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $supplier->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', Supplier::class);
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances as asset_maintenances_count','assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
$this->authorize('delete', $supplier);
if ($supplier->assets_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
}
if ($supplier->asset_maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count])));
}
if ($supplier->licenses_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
}
$supplier->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$suppliers = Supplier::select([
'id',
'name',
'image',
]);
if ($request->filled('search')) {
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->get('search').'%');
}
$suppliers = $suppliers->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($suppliers as $supplier) {
$supplier->use_text = $supplier->name;
$supplier->use_image = ($supplier->image) ? url('/').'/uploads/suppliers/'.$supplier->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($suppliers);
}
}

View File

@@ -0,0 +1,456 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Transformers\UsersTransformer;
use App\Models\Company;
use App\Models\User;
use App\Helpers\Helper;
use App\Http\Requests\SaveUserRequest;
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
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$this->authorize('view', User::class);
$users = User::select([
'users.activated',
'users.address',
'users.avatar',
'users.city',
'users.company_id',
'users.country',
'users.created_at',
'users.deleted_at',
'users.department_id',
'users.email',
'users.employee_num',
'users.first_name',
'users.id',
'users.jobtitle',
'users.last_login',
'users.last_name',
'users.location_id',
'users.manager_id',
'users.notes',
'users.permissions',
'users.phone',
'users.state',
'users.two_factor_enrolled',
'users.two_factor_optin',
'users.updated_at',
'users.username',
'users.zip',
])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables')
->withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count');
$users = Company::scopeCompanyables($users);
if (($request->filled('deleted')) && ($request->input('deleted')=='true')) {
$users = $users->GetDeleted();
}
if ($request->filled('company_id')) {
$users = $users->where('users.company_id', '=', $request->input('company_id'));
}
if ($request->filled('location_id')) {
$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'));
}
if ($request->filled('department_id')) {
$users = $users->where('users.department_id','=',$request->input('department_id'));
}
if ($request->filled('search')) {
$users = $users->TextSearch($request->input('search'));
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
// 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');
switch ($request->input('sort')) {
case 'manager':
$users = $users->OrderManager($order);
break;
case 'location':
$users = $users->OrderLocation($order);
break;
case 'department':
$users = $users->OrderDepartment($order);
break;
case 'company':
$users = $users->OrderCompany($order);
break;
default:
$allowed_columns =
[
'last_name','first_name','email','jobtitle','username','employee_num',
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
'country', 'zip', 'id'
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
$users = $users->orderBy($sort, $order);
break;
}
$total = $users->count();
$users = $users->skip($offset)->take($limit)->get();
return (new UsersTransformer)->transformUsers($users, $total);
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$users = User::select(
[
'users.id',
'users.username',
'users.employee_num',
'users.first_name',
'users.last_name',
'users.gravatar',
'users.avatar',
'users.email',
]
)->where('show_in_list', '=', '1');
$users = Company::scopeCompanyables($users);
if ($request->filled('search')) {
$users = $users->SimpleNameSearch($request->get('search'))
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
}
$users = $users->orderBy('last_name', 'asc')->orderBy('first_name', 'asc');
$users = $users->paginate(50);
foreach ($users as $user) {
$name_str = '';
if ($user->last_name!='') {
$name_str .= e($user->last_name).', ';
}
$name_str .= e($user->first_name);
if ($user->username!='') {
$name_str .= ' ('.e($user->username).')';
}
if ($user->employee_num!='') {
$name_str .= ' - #'.e($user->employee_num);
}
$user->use_text = $name_str;
$user->use_image = ($user->present()->gravatar) ? $user->present()->gravatar : null;
}
return (new SelectlistTransformer)->transformSelectlist($users);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(SaveUserRequest $request)
{
$this->authorize('create', User::class);
$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));
if ($user->save()) {
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
} else {
$user->groups()->sync(array());
}
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', User::class);
$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);
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(SaveUserRequest $request, $id)
{
$this->authorize('update', User::class);
$user = User::findOrFail($id);
$user->fill($request->all());
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
if ($request->filled('password')) {
$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)]);
if ($user->save()) {
// Sync group memberships:
// This was changed in Snipe-IT v4.6.x to 4.7, since we upgraded to Laravel 5.5
// which changes the behavior of has vs filled.
// The $request->has method will now return true even if the input value is an empty string or null.
// A new $request->filled method has was added that provides the previous behavior of the has method.
// Check if the request has groups passed and has a value
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
// The groups field has been passed but it is null, so we should blank it out
} elseif ($request->has('groups')) {
$user->groups()->sync(array());
}
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$this->authorize('delete', User::class);
$user = User::findOrFail($id);
$this->authorize('delete', $user);
if (($user->assets) && ($user->assets->count() > 0)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete_has_assets')));
}
if (($user->licenses) && ($user->licenses->count() > 0)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->licenses->count() . ' license(s) associated with them and cannot be deleted.'));
}
if (($user->accessories) && ($user->accessories->count() > 0)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->accessories->count() . ' accessories associated with them.'));
}
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->managedLocations()->count() . ' locations that they manage.'));
}
if ($user->delete()) {
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete')));
}
/**
* Return JSON containing a list of assets assigned to a user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $userId
* @return string JSON
*/
public function assets($id)
{
$this->authorize('view', User::class);
$this->authorize('view', Asset::class);
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
/**
* Return JSON containing a list of accessories assigned to a user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.6.14]
* @param $userId
* @return string JSON
*/
public function accessories($id)
{
$this->authorize('view', User::class);
$user = User::findOrFail($id);
$this->authorize('view', Accessory::class);
$accessories = $user->accessories;
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
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $userId
* @return string JSON
*/
public function postTwoFactorReset(Request $request)
{
$this->authorize('update', User::class);
if ($request->filled('id')) {
try {
$user = User::find($request->get('id'));
$user->two_factor_secret = null;
$user->two_factor_enrolled = 0;
$user->save();
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
} catch (\Exception $e) {
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
}
}
return response()->json(['message' => 'No ID provided'], 500);
}
/**
* Get info on the current user.
*
* @author [Juan Font] [<juanfontalonso@gmail.com>]
* @since [v4.4.2]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function getCurrentUserInfo(Request $request)
{
return (new UsersTransformer)->transformUser($request->user());
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\AssetCheckinRequest;
use App\Models\Asset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AssetCheckinController extends Controller
{
/**
* Returns a view that presents a form to check an asset back into inventory.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @param string $backto
* @since [v1.0]
* @return View
*/
public function create($assetId, $backto = null)
{
// Check if the asset exists
if (is_null($asset = Asset::find($assetId))) {
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('checkin', $asset);
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto);
}
/**
* Validate and process the form data to check an asset back into inventory.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param AssetCheckinRequest $request
* @param int $assetId
* @param null $backto
* @return Redirect
* @since [v1.0]
*/
public function store(AssetCheckinRequest $request, $assetId = null, $backto = null)
{
// Check if the asset exists
if (is_null($asset = Asset::find($assetId))) {
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('checkin', $asset);
if ($asset->assignedType() == Asset::USER) {
$user = $asset->assignedTo;
}
if (is_null($target = $asset->assignedTo)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
}
$asset->expected_checkin = null;
$asset->last_checkout = null;
$asset->assigned_to = null;
$asset->assignedTo()->disassociate($asset);
$asset->assigned_type = null;
$asset->accepted = null;
$asset->name = $request->get('name');
if ($request->filled('status_id')) {
$asset->status_id = e($request->get('status_id'));
}
$asset->location_id = $asset->rtd_location_id;
if ($request->filled('location_id')) {
$asset->location_id = e($request->get('location_id'));
}
// Was the asset updated?
if ($asset->save()) {
$logaction = $asset->logCheckin($target, e(request('note')));
$data['log_id'] = $logaction->id;
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
$data['last_name'] = get_class($target) == User::class ? $target->last_name : '';
$data['item_name'] = $asset->present()->name();
$data['checkin_date'] = $logaction->created_at;
$data['item_tag'] = $asset->asset_tag;
$data['item_serial'] = $asset->serial;
$data['note'] = $logaction->note;
$data['manufacturer_name'] = $asset->model->manufacturer->name;
$data['model_name'] = $asset->model->name;
$data['model_number'] = $asset->model->model_number;
if ((isset($user)) && ($backto =='user')) {
return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
}
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkin.success'));
}
// Redirect to the asset management page with error
return redirect()->route("hardware.index")->with('error', trans('admin/hardware/message.checkin.error'));
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace App\Http\Controllers;
use App\Exceptions\CheckoutNotAllowed;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Requests\AssetCheckoutRequest;
use App\Models\Asset;
use App\Models\Location;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AssetCheckoutController extends Controller
{
use CheckInOutRequest;
/**
* Returns a view that presents a form to check an asset out to a
* user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v1.0]
* @return View
*/
public function create($assetId)
{
// Check if the asset exists
if (is_null($asset = Asset::find(e($assetId)))) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('checkout', $asset);
if ($asset->availableForCheckout()) {
return view('hardware/checkout', compact('asset'));
}
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
}
/**
* Validate and process the form data to check out an asset to a user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param AssetCheckoutRequest $request
* @param int $assetId
* @return Redirect
* @since [v1.0]
*/
public function store(AssetCheckoutRequest $request, $assetId)
{
try {
// Check if the asset exists
if (!$asset = Asset::find($assetId)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
} elseif (!$asset->availableForCheckout()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
}
$this->authorize('checkout', $asset);
$admin = Auth::user();
$target = $this->determineCheckoutTarget($asset);
if ($asset->is($target)) {
throw new CheckoutNotAllowed('You cannot check an asset out to itself.');
}
$asset = $this->updateAssetLocation($asset, $target);
$checkout_at = date("Y-m-d H:i:s");
if (($request->filled('checkout_at')) && ($request->get('checkout_at')!= date("Y-m-d"))) {
$checkout_at = $request->get('checkout_at');
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = $request->get('expected_checkin');
}
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
} catch (ModelNotFoundException $e) {
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
} catch (CheckoutNotAllowed $e) {
return redirect()->back()->with('error', $e->getMessage());
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\AssetFileRequest;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;
class AssetFilesController extends Controller
{
/**
* Upload a file to the server.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param AssetFileRequest $request
* @param int $assetId
* @return Redirect
* @since [v1.0]
*/
public function store(AssetFileRequest $request, $assetId = null)
{
if (!$asset = Asset::find($assetId)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('update', $asset);
$destinationPath = config('app.private_uploads').'/assets';
if ($request->hasFile('file')) {
foreach ($request->file('file') as $file) {
$extension = $file->getClientOriginalExtension();
$filename = 'hardware-'.$asset->id.'-'.str_random(8);
$filename .= '-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
$file->move($destinationPath, $filename);
$asset->logUpload($filename, e($request->get('notes')));
}
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
* Check for permissions and display the file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @param int $fileId
* @since [v1.0]
* @return View
*/
public function show($assetId = null, $fileId = null, $download = true)
{
$asset = Asset::find($assetId);
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
if (!$log = Actionlog::find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
$file = $log->get_src('assets');
if ($log->action_type =='audit') {
$file = $log->get_src('audits');
}
if (!file_exists($file)) {
return response('File '.$file.' not found on server', 404)
->header('Content-Type', 'text/plain');
}
if ($download != 'true') {
if ($contents = file_get_contents($file)) {
return Response::make($contents)->header('Content-Type', mime_content_type($file));
}
return JsonResponse::create(["error" => "Failed validation: "], 500);
}
return Response::download($file);
}
// Prepare the error message
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', $error);
}
/**
* Delete the associated file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @param int $fileId
* @since [v1.0]
* @return View
*/
public function destroy($assetId = null, $fileId = null)
{
$asset = Asset::find($assetId);
$this->authorize('update', $asset);
$destinationPath = config('app.private_uploads').'/imports/assets';
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
$log = Actionlog::find($fileId);
if ($log) {
$full_filename = $destinationPath.'/'.$log->filename;
if (file_exists($full_filename)) {
unlink($destinationPath.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()->with('error', 'Could not find matching upload log.');
}
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
}

View File

@@ -9,11 +9,9 @@ use Input;
use Lang;
use Log;
use Mail;
use Redirect;
use Response;
use Slack;
use Str;
use App\Models\Supplier;
use TCPDF;
use Validator;
use View;
@@ -22,6 +20,7 @@ use App\Models\Asset;
use App\Helpers\Helper;
use Auth;
use Gate;
use Illuminate\Http\Request;
/**
* This controller handles all actions related to Asset Maintenance for
@@ -43,7 +42,7 @@ class AssetMaintenancesController extends Controller
*/
private static function getInsufficientPermissionsRedirect()
{
return redirect()->route('asset_maintenances')
return redirect()->route('maintenances.index')
->with('error', trans('general.insufficient_permissions'));
}
@@ -58,129 +57,39 @@ class AssetMaintenancesController extends Controller
* @since [v1.8]
* @return View
*/
public function getIndex()
public function index()
{
return View::make('asset_maintenances/index');
return view('asset_maintenances/index');
}
/**
* Generates the JSON response for asset maintenances listing view.
*
* @see AssetMaintenancesController::getIndex() method that generates view
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return String JSON
*/
public function getDatatable()
{
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company','admin');
if (Input::has('search')) {
$maintenances = $maintenances->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['id','title','asset_maintenance_time','asset_maintenance_type','cost','start_date','completion_date','notes','user_id'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
switch ($sort) {
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
default:
$maintenances = $maintenances->orderBy($sort, $order);
break;
}
$maintenancesCount = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
$rows = array();
$settings = Setting::getSettings();
foreach ($maintenances as $maintenance) {
$actions = '';
if (Gate::allows('assets.edit')) {
$actions .= '<nobr><a href="' . route('update/asset_maintenance',
$maintenance->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/asset_maintenance',
$maintenance->id) . '" data-content="' . trans('admin/asset_maintenances/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($maintenance->title) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></nobr>';
}
if (($maintenance->cost) && (isset($maintenance->asset)) && ($maintenance->asset->assetloc) && ($maintenance->asset->assetloc->currency!='')) {
$maintenance_cost = $maintenance->asset->assetloc->currency.$maintenance->cost;
} else {
$maintenance_cost = $settings->default_currency.$maintenance->cost;
}
$rows[] = array(
'id' => $maintenance->id,
'asset_name' => ($maintenance->asset) ? (string)link_to('/hardware/'.$maintenance->asset->id.'/view', $maintenance->asset->showAssetName()) : 'Deleted Asset' ,
'title' => $maintenance->title,
'notes' => $maintenance->notes,
'supplier' => ($maintenance->supplier) ? (string)link_to('/admin/settings/suppliers/'.$maintenance->supplier->id.'/view', $maintenance->supplier->name) : 'Deleted Supplier',
'cost' => $maintenance_cost,
'asset_maintenance_type' => e($maintenance->asset_maintenance_type),
'start_date' => $maintenance->start_date,
'asset_maintenance_time' => $maintenance->asset_maintenance_time,
'completion_date' => $maintenance->completion_date,
'user_id' => ($maintenance->admin) ? (string)link_to('/admin/users/'.$maintenance->admin->id.'/view', $maintenance->admin->fullName()) : '',
'actions' => $actions,
'companyName' => ($maintenance->asset->company) ? $maintenance->asset->company->name : ''
);
}
$data = array('total' => $maintenancesCount, 'rows' => $rows);
return $data;
}
/**
* Returns a form view to create a new asset maintenance.
*
* @see AssetMaintenancesController::postCreate() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return mixed
*/
public function getCreate($assetId = null)
* Returns a form view to create a new asset maintenance.
*
* @see AssetMaintenancesController::postCreate() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return mixed
*/
public function create()
{
$asset = null;
if ($asset = Asset::find(request('asset_id'))) {
// We have to set this so that the correct property is set in the select2 ajax dropdown
$asset->asset_id = $asset->id;
}
// Prepare Asset Maintenance Type List
$assetMaintenanceType = [
'' => 'Select an asset maintenance type',
] + AssetMaintenance::getImprovementOptions();
// Mark the selected asset, if it came in
$selectedAsset = $assetId;
$assets = Helper::detailedAssetList();
$supplier_list = Helper::suppliersList();
// Render the view
return View::make('asset_maintenances/edit')
->with('asset_list', $assets)
->with('selectedAsset', $selectedAsset)
->with('supplier_list', $supplier_list)
return view('asset_maintenances/edit')
->with('asset', $asset)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', new AssetMaintenance);
}
@@ -194,62 +103,29 @@ class AssetMaintenancesController extends Controller
* @since [v1.8]
* @return mixed
*/
public function postCreate()
public function store(Request $request)
{
// get the POST data
$new = Input::all();
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = e($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
if (e(Input::get('supplier_id')) == '') {
$assetMaintenance->supplier_id = null;
} else {
$assetMaintenance->supplier_id = e(Input::get('supplier_id'));
}
if (e(Input::get('is_warranty')) == '') {
$assetMaintenance->is_warranty = 0;
} else {
$assetMaintenance->is_warranty = e(Input::get('is_warranty'));
}
if (e(Input::get('cost')) == '') {
$assetMaintenance->cost = '';
} else {
$assetMaintenance->cost = Helper::ParseFloat(e(Input::get('cost')));
}
if (e(Input::get('notes')) == '') {
$assetMaintenance->notes = null;
} else {
$assetMaintenance->notes = e(Input::get('notes'));
}
$asset = Asset::find(e(Input::get('asset_id')));
if (!Company::isCurrentUserHasAccess($asset)) {
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
return static::getInsufficientPermissionsRedirect();
}
// Save the asset maintenance data
$assetMaintenance->asset_id = e(Input::get('asset_id'));
$assetMaintenance->asset_maintenance_type = e(Input::get('asset_maintenance_type'));
$assetMaintenance->title = e(Input::get('title'));
$assetMaintenance->start_date = e(Input::get('start_date'));
$assetMaintenance->completion_date = e(Input::get('completion_date'));
$assetMaintenance->user_id = Auth::user()->id;
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->user_id = Auth::id();
if (( $assetMaintenance->completion_date == "" )
|| ( $assetMaintenance->completion_date == "0000-00-00" )
) {
$assetMaintenance->completion_date = null;
}
if (( $assetMaintenance->completion_date !== "" )
&& ( $assetMaintenance->completion_date !== "0000-00-00" )
if (( $assetMaintenance->completion_date !== null )
&& ( $assetMaintenance->start_date !== "" )
&& ( $assetMaintenance->start_date !== "0000-00-00" )
) {
@@ -260,17 +136,13 @@ class AssetMaintenancesController extends Controller
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->to("admin/asset_maintenances")
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
/**
@@ -283,13 +155,16 @@ class AssetMaintenancesController extends Controller
* @since [v1.8]
* @return mixed
*/
public function getEdit($assetMaintenanceId = null)
public function edit($assetMaintenanceId = null)
{
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the improvement management page
return redirect()->to('admin/asset_maintenances')
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (!$assetMaintenance->asset) {
return redirect()->route('maintenances.index')
->with('error', 'The asset associated with this maintenance does not exist.');
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
@@ -311,88 +186,57 @@ class AssetMaintenancesController extends Controller
'' => 'Select an improvement type',
] + AssetMaintenance::getImprovementOptions();
$assets = Company::scopeCompanyables(Asset::with('model','assignedUser')->get(), 'assets.company_id')->lists('detailed_name', 'id');
// Get Supplier List
$supplier_list = Helper::suppliersList();
// Render the view
return View::make('asset_maintenances/edit')
->with('asset_list', $assets)
return view('asset_maintenances/edit')
->with('selectedAsset', null)
->with('supplier_list', $supplier_list)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', $assetMaintenance);
}
/**
* Validates and stores an update to an asset maintenance
*
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
* @return mixed
*/
public function postEdit($assetMaintenanceId = null)
* Validates and stores an update to an asset maintenance
*
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param Request $request
* @param int $assetMaintenanceId
* @return mixed
* @version v1.0
* @since [v1.8]
*/
public function update(Request $request, $assetMaintenanceId = null)
{
// get the POST data
$new = Input::all();
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->to('admin/asset_maintenances')
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost')));
$assetMaintenance->notes = e($request->input('notes'));
if (e(Input::get('supplier_id')) == '') {
$assetMaintenance->supplier_id = null;
} else {
$assetMaintenance->supplier_id = e(Input::get('supplier_id'));
}
if (e(Input::get('is_warranty')) == '') {
$assetMaintenance->is_warranty = 0;
} else {
$assetMaintenance->is_warranty = e(Input::get('is_warranty'));
}
if (e(Input::get('cost')) == '') {
$assetMaintenance->cost = '';
} else {
$assetMaintenance->cost = Helper::ParseFloat(e(Input::get('cost')));
}
if (e(Input::get('notes')) == '') {
$assetMaintenance->notes = null;
} else {
$assetMaintenance->notes = e(Input::get('notes'));
}
$asset = Asset::find(e(Input::get('asset_id')));
$asset = Asset::find(request('asset_id'));
if (!Company::isCurrentUserHasAccess($asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Save the asset maintenance data
$assetMaintenance->asset_id = e(Input::get('asset_id'));
$assetMaintenance->asset_maintenance_type = e(Input::get('asset_maintenance_type'));
$assetMaintenance->title = e(Input::get('title'));
$assetMaintenance->start_date = e(Input::get('start_date'));
$assetMaintenance->completion_date = e(Input::get('completion_date'));
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
if (( $assetMaintenance->completion_date == "" )
|| ( $assetMaintenance->completion_date == "0000-00-00" )
if (( $assetMaintenance->completion_date == null )
) {
$assetMaintenance->completion_date = null;
if (( $assetMaintenance->asset_maintenance_time !== 0 )
|| ( !is_null($assetMaintenance->asset_maintenance_time) )
) {
@@ -400,8 +244,7 @@ class AssetMaintenancesController extends Controller
}
}
if (( $assetMaintenance->completion_date !== "" )
&& ( $assetMaintenance->completion_date !== "0000-00-00" )
if (( $assetMaintenance->completion_date !== null )
&& ( $assetMaintenance->start_date !== "" )
&& ( $assetMaintenance->start_date !== "0000-00-00" )
) {
@@ -414,12 +257,10 @@ class AssetMaintenancesController extends Controller
if ($assetMaintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->to("admin/asset_maintenances")
->with('success', trans('admin/asset_maintenances/message.create.success'));
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.edit.success'));
}
return redirect()->back() ->withInput()->withErrors($assetMaintenance->getErrors());
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
/**
@@ -431,12 +272,12 @@ class AssetMaintenancesController extends Controller
* @since [v1.8]
* @return mixed
*/
public function getDelete($assetMaintenanceId)
public function destroy($assetMaintenanceId)
{
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->to('admin/asset_maintenances')
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
@@ -446,7 +287,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->delete();
// Redirect to the asset_maintenance management page
return redirect()->to('admin/asset_maintenances')
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.delete.success'));
}
@@ -459,17 +300,17 @@ class AssetMaintenancesController extends Controller
* @since [v1.8]
* @return View
*/
public function getView($assetMaintenanceId)
public function show($assetMaintenanceId)
{
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->to('admin/asset_maintenances')
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
return View::make('asset_maintenances/view')->with('assetMaintenance', $assetMaintenance);
return view('asset_maintenances/view')->with('assetMaintenance', $assetMaintenance);
}
}

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Http\Controllers;
use App\Models\CustomField;
use Image;
use Input;
use Lang;
@@ -15,6 +16,8 @@ use App\Models\Asset;
use App\Models\Company;
use Config;
use App\Helpers\Helper;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -32,14 +35,13 @@ class AssetModelsController extends Controller
* the content for the accessories listing, which is generated in getDatatable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see AssetModelsController::getDatatable() method that generates the JSON response
* @since [v1.0]
* @return View
*/
public function getIndex()
public function index()
{
// Show the page
return View::make('models/index');
$this->authorize('index', AssetModel::class);
return view('models/index');
}
/**
@@ -49,16 +51,12 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @return View
*/
public function getCreate()
public function create()
{
// Show the page
$depreciation_list = Helper::depreciationList();
$manufacturer_list = Helper::manufacturerList();
$category_list = Helper::categoryList('asset');
return View::make('models/edit')
->with('category_list', $category_list)
->with('depreciation_list', $depreciation_list)
->with('manufacturer_list', $manufacturer_list)
$this->authorize('create', AssetModel::class);
$category_type = 'asset';
return view('models/edit')->with('category_type',$category_type)
->with('depreciation_list', Helper::depreciationList())
->with('item', new AssetModel);
}
@@ -70,98 +68,42 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @return Redirect
*/
public function postCreate()
public function store(ImageUploadRequest $request)
{
$this->authorize('create', AssetModel::class);
// Create a new asset model
$model = new AssetModel;
if (e(Input::get('depreciation_id')) == '') {
$model->depreciation_id = 0;
} else {
$model->depreciation_id = e(Input::get('depreciation_id'));
}
if (e(Input::get('eol')) == '') {
$model->eol = 0;
} else {
$model->eol = e(Input::get('eol'));
}
// Save the model data
$model->name = e(Input::get('name'));
$model->model_number = e(Input::get('model_number'));
$model->manufacturer_id = e(Input::get('manufacturer_id'));
$model->category_id = e(Input::get('category_id'));
$model->notes = e(Input::get('notes'));
$model->user_id = Auth::user()->id;
$model->eol = $request->input('eol');
$model->depreciation_id = $request->input('depreciation_id');
$model->name = $request->input('name');
$model->model_number = $request->input('model_number');
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->notes = $request->input('notes');
$model->user_id = Auth::id();
$model->requestable = Input::has('requestable');
if (Input::get('custom_fieldset')!='') {
$model->fieldset_id = e(Input::get('custom_fieldset'));
if ($request->input('custom_fieldset')!='') {
$model->fieldset_id = e($request->input('custom_fieldset'));
}
if (Input::file('image')) {
$image = Input::file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/models/'.$file_name);
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$model->image = $file_name;
}
$model = $request->handleImages($model,600, public_path().'/uploads/models');
// Was it created?
if ($model->save()) {
if ($this->shouldAddDefaultValues($request->input())) {
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
}
// Redirect to the new model page
return redirect()->to("hardware/models")->with('success', trans('admin/models/message.create.success'));
return redirect()->route("models.index")->with('success', trans('admin/models/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($model->getErrors());
return redirect()->back()->withInput()->withErrors($model->getErrors());
}
/**
* Validates and stores new Asset Model data created from the
* modal form on the Asset Creation view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @return String JSON
*/
public function store()
{
//COPYPASTA!!!! FIXME
$model = new AssetModel;
$settings=Input::all();
$settings['eol']= null;
$model->name=e(Input::get('name'));
$model->manufacturer_id = e(Input::get('manufacturer_id'));
$model->category_id = e(Input::get('category_id'));
$model->model_number = e(Input::get('model_number'));
$model->user_id = Auth::user()->id;
$model->notes = e(Input::get('notes'));
$model->eol= null;
if (Input::get('fieldset_id')=='') {
$model->fieldset_id = null;
} else {
$model->fieldset_id = e(Input::get('fieldset_id'));
}
if ($model->save()) {
return JsonResponse::create($model);
} else {
return JsonResponse::create(["error" => "Failed validation: ".print_r($model->getErrors()->all('<li>:message</li>'), true)], 500);
}
}
/**
* Returns a view containing the asset model edit form.
*
@@ -170,23 +112,18 @@ class AssetModelsController extends Controller
* @param int $modelId
* @return View
*/
public function getEdit($modelId = null)
public function edit($modelId = null)
{
// Check if the model exists
if (is_null($item = AssetModel::find($modelId))) {
// Redirect to the model management page
return redirect()->to('assets/models')->with('error', trans('admin/models/message.does_not_exist'));
$this->authorize('update', AssetModel::class);
if ($item = AssetModel::find($modelId)) {
$category_type = 'asset';
$view = View::make('models/edit', compact('item','category_type'));
$view->with('depreciation_list', Helper::depreciationList());
return $view;
}
$depreciation_list = Helper::depreciationList();
$manufacturer_list = Helper::manufacturerList();
$category_list = Helper::categoryList('asset');
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
$view = View::make('models/edit', compact('item'));
$view->with('category_list', $category_list);
$view->with('depreciation_list', $depreciation_list);
$view->with('manufacturer_list', $manufacturer_list);
return $view;
}
@@ -199,68 +136,42 @@ class AssetModelsController extends Controller
* @param int $modelId
* @return Redirect
*/
public function postEdit($modelId = null)
public function update(ImageUploadRequest $request, $modelId = null)
{
$this->authorize('update', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
// Redirect to the models management page
return redirect()->to('admin/models')->with('error', trans('admin/models/message.does_not_exist'));
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
$model->depreciation_id = $request->input('depreciation_id');
$model->eol = $request->input('eol');
$model->name = $request->input('name');
$model->model_number = $request->input('model_number');
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->notes = $request->input('notes');
$model->requestable = $request->input('requestable', '0');
if (e(Input::get('depreciation_id')) == '') {
$model->depreciation_id = 0;
} else {
$model->depreciation_id = e(Input::get('depreciation_id'));
}
$this->removeCustomFieldsDefaultValues($model);
if (e(Input::get('eol')) == '') {
$model->eol = null;
} else {
$model->eol = e(Input::get('eol'));
}
// Update the model data
$model->name = e(Input::get('name'));
$model->model_number = e(Input::get('model_number'));
$model->manufacturer_id = e(Input::get('manufacturer_id'));
$model->category_id = e(Input::get('category_id'));
$model->notes = e(Input::get('notes'));
$model->requestable = Input::has('requestable');
if (Input::get('custom_fieldset')=='') {
if ($request->input('custom_fieldset')=='') {
$model->fieldset_id = null;
} else {
$model->fieldset_id = e(Input::get('custom_fieldset'));
$model->fieldset_id = $request->input('custom_fieldset');
if ($this->shouldAddDefaultValues($request->input())) {
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
}
}
if (Input::file('image')) {
$image = Input::file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/models/'.$file_name);
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$model->image = $file_name;
}
$model = $request->handleImages($model,600, public_path().'/uploads/models');
if (Input::get('image_delete') == 1 && Input::file('image') == "") {
$model->image = null;
}
// Was it created?
if ($model->save()) {
// Redirect to the new model page
return redirect()->to("hardware/models")->with('success', trans('admin/models/message.update.success'));
} else {
return redirect()->back()->withInput()->withErrors($model->getErrors());
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
}
// Redirect to the model create page
return redirect()->to("hardware/models/$modelId/edit")->with('error', trans('admin/models/message.update.error'));
return redirect()->back()->withInput()->withErrors($model->getErrors());
}
/**
@@ -272,25 +183,32 @@ class AssetModelsController extends Controller
* @param int $modelId
* @return Redirect
*/
public function getDelete($modelId)
public function destroy($modelId)
{
$this->authorize('delete', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
// Redirect to the blogs management page
return redirect()->to('hardware/models')->with('error', trans('admin/models/message.not_found'));
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found'));
}
if ($model->assets->count() > 0) {
if ($model->assets()->count() > 0) {
// Throw an error that this model is associated with assets
return redirect()->to('hardware/models')->with('error', trans('admin/models/message.assoc_users'));
} else {
// Delete the model
$model->delete();
// Redirect to the models management page
return redirect()->to('hardware/models')->with('success', trans('admin/models/message.delete.success'));
return redirect()->route('models.index')->with('error', trans('admin/models/message.assoc_users'));
}
if ($model->image) {
try {
unlink(public_path().'/uploads/models/'.$model->image);
} catch (\Exception $e) {
\Log::info($e);
}
}
// Delete the model
$model->delete();
// Redirect to the models management page
return redirect()->route('models.index')->with('success', trans('admin/models/message.delete.success'));
}
@@ -304,7 +222,7 @@ class AssetModelsController extends Controller
*/
public function getRestore($modelId = null)
{
$this->authorize('create', AssetModel::class);
// Get user information
$model = AssetModel::withTrashed()->find($modelId);
@@ -317,11 +235,10 @@ class AssetModelsController extends Controller
$success = trans('admin/models/message.restore.success');
// Redirect back
return redirect()->back()->with('success', $success);
return redirect()->route('models.index')->with('success', $success);
} else {
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
}
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
}
@@ -334,21 +251,16 @@ class AssetModelsController extends Controller
* @param int $modelId
* @return View
*/
public function getView($modelId = null)
public function show($modelId = null)
{
$this->authorize('view', AssetModel::class);
$model = AssetModel::withTrashed()->find($modelId);
if (isset($model->id)) {
return View::make('models/view', compact('model'));
} else {
// Prepare the error message
$error = trans('admin/models/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('models')->with('error', $error);
return view('models/view', compact('model'));
}
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
/**
@@ -363,21 +275,15 @@ class AssetModelsController extends Controller
{
// Check if the model exists
if (is_null($model_to_clone = AssetModel::find($modelId))) {
// Redirect to the model management page
return redirect()->to('assets/models')->with('error', trans('admin/models/message.does_not_exist'));
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
$model = clone $model_to_clone;
$model->id = null;
// Show the page
$depreciation_list = Helper::depreciationList();
$manufacturer_list = Helper::manufacturerList();
$category_list = Helper::categoryList('asset');
$view = View::make('models/edit');
$view->with('category_list', $category_list);
$view->with('depreciation_list', $depreciation_list);
$view->with('manufacturer_list', $manufacturer_list);
$view->with('depreciation_list', Helper::depreciationList());
$view->with('item', $model);
$view->with('clone_model', $model_to_clone);
return $view;
@@ -396,156 +302,186 @@ class AssetModelsController extends Controller
public function getCustomFields($modelId)
{
$model = AssetModel::find($modelId);
return View::make("models.custom_fields_form")->with("model", $model);
return view("models.custom_fields_form")->with("model", $model);
}
/**
* Get the JSON response to populate the data tables on the
* Asset Model listing page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @param string $status
* @return String JSON
*/
public function getDatatable($status = null)
* Returns a view that allows the user to bulk edit model attrbutes
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.7]
* @return \Illuminate\Contracts\View\View
*/
public function postBulkEdit(Request $request)
{
$models = AssetModel::with('category', 'assets', 'depreciation', 'manufacturer');
switch ($status) {
case 'Deleted':
$models->withTrashed()->Deleted();
break;
}
$models_raw_array = Input::get('ids');
// Make sure some IDs have been selected
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
if (Input::has('search')) {
$models = $models->TextSearch(Input::get('search'));
}
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->orderBy('assets_count', 'ASC')->get();
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
// If deleting....
if ($request->input('bulk_actions')=='delete') {
$valid_count = 0;
foreach ($models as $model) {
if ($model->assets_count == 0) {
$valid_count++;
}
}
return view('models/bulk-delete', compact('models'))->with('valid_count', $valid_count);
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['id','name','model_number'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
$models = $models->orderBy($sort, $order);
$modelCount = $models->count();
$models = $models->skip($offset)->take($limit)->get();
$rows = array();
foreach ($models as $model) {
if ($model->deleted_at == '') {
$actions = '<div style=" white-space: nowrap;"><a href="'.route('clone/model', $model->id).'" class="btn btn-info btn-sm" title="Clone Model" data-toggle="tooltip"><i class="fa fa-clone"></i></a> <a href="'.route('update/model', $model->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/model', $model->id).'" data-content="'.trans('admin/models/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($model->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
// Otherwise display the bulk edit screen
} else {
$actions = '<a href="'.route('restore/model', $model->id).'" class="btn btn-warning btn-sm"><i class="fa fa-recycle icon-white"></i></a>';
$nochange = ['NC' => 'No Change'];
$fieldset_list = $nochange + Helper::customFieldsetList();
$depreciation_list = $nochange + Helper::depreciationList();
return view('models/bulk-edit', compact('models'))
->with('fieldset_list', $fieldset_list)
->with('depreciation_list', $depreciation_list);
}
$rows[] = array(
'id' => $model->id,
'manufacturer' => (string)link_to('/admin/settings/manufacturers/'.$model->manufacturer->id.'/view', $model->manufacturer->name),
'name' => (string)link_to('/hardware/models/'.$model->id.'/view', $model->name),
'image' => ($model->image!='') ? '<img src="'.config('app.url').'/uploads/models/'.$model->image.'" height=50 width=50>' : '',
'modelnumber' => $model->model_number,
'numassets' => $model->assets->count(),
'depreciation' => (($model->depreciation) && ($model->depreciation->id > 0)) ? $model->depreciation->name.' ('.$model->depreciation->months.')' : trans('general.no_depreciation'),
'category' => ($model->category) ? (string)link_to('admin/settings/categories/'.$model->category->id.'/view', $model->category->name) : '',
'eol' => ($model->eol) ? $model->eol.' '.trans('general.months') : '',
'note' => $model->getNote(),
'fieldset' => ($model->fieldset) ? (string)link_to('admin/custom_fields/'.$model->fieldset->id, $model->fieldset->name) : '',
'actions' => $actions
);
}
$data = array('total' => $modelCount, 'rows' => $rows);
return redirect()->route('models.index')
->with('error', 'You must select at least one model to edit.');
return $data;
}
/**
* Get the asset information to present to the model view detail page
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @param int $modelId
* @return String JSON
*/
public function getDataView($modelID)
* Returns a view that allows the user to bulk edit model attrbutes
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.7]
* @return \Illuminate\Contracts\View\View
*/
public function postBulkEditSave(Request $request)
{
$assets = Asset::where('model_id', '=', $modelID)->with('company', 'assetstatus');
if (Input::has('search')) {
$assets = $assets->TextSearch(e(Input::get('search')));
$models_raw_array = Input::get('ids');
$update_array = array();
if (($request->filled('manufacturer_id') && ($request->input('manufacturer_id')!='NC'))) {
$update_array['manufacturer_id'] = $request->input('manufacturer_id');
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
if (($request->filled('category_id') && ($request->input('category_id')!='NC'))) {
$update_array['category_id'] = $request->input('category_id');
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
if ($request->input('fieldset_id')!='NC') {
$update_array['fieldset_id'] = $request->input('fieldset_id');
}
if ($request->input('depreciation_id')!='NC') {
$update_array['depreciation_id'] = $request->input('depreciation_id');
}
$allowed_columns = ['name', 'serial','asset_tag'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
if (count($update_array) > 0) {
AssetModel::whereIn('id', $models_raw_array)->update($update_array);
return redirect()->route('models.index')
->with('success', trans('admin/models/message.bulkedit.success'));
}
$assets = $assets->orderBy($sort, $order);
return redirect()->route('models.index')
->with('warning', trans('admin/models/message.bulkedit.error'));
$assetsCount = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
}
$rows = array();
/**
* Validate and delete the given Asset Models. An Asset Model
* cannot be deleted if there are associated assets.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $modelId
* @return Redirect
*/
public function postBulkDelete(Request $request)
{
$models_raw_array = Input::get('ids');
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
foreach ($assets as $asset) {
$actions = '';
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->get();
if ($asset->assetstatus) {
if ($asset->assetstatus->deployable != 0) {
if (($asset->assigned_to !='') && ($asset->assigned_to > 0)) {
$actions = '<a href="'.route('checkin/hardware', $asset->id).'" class="btn btn-primary btn-sm">'.trans('general.checkin').'</a>';
} else {
$actions = '<a href="'.route('checkout/hardware', $asset->id).'" class="btn btn-info btn-sm">'.trans('general.checkout').'</a>';
}
$del_error_count = 0;
$del_count = 0;
foreach ($models as $model) {
\Log::debug($model->id);
if ($model->assets_count > 0) {
$del_error_count++;
} else {
$model->delete();
$del_count++;
}
}
$rows[] = array(
'id' => $asset->id,
'name' => (string)link_to('/hardware/'.$asset->id.'/view', $asset->showAssetName()),
'asset_tag' => (string)link_to('hardware/'.$asset->id.'/view', $asset->asset_tag),
'serial' => $asset->serial,
'assigned_to' => ($asset->assigned_to) ? (string)link_to('/admin/users/'.$asset->assigned_to.'/view', $asset->assigneduser->fullName()) : '',
'actions' => $actions,
'companyName' => Company::getName($asset)
);
\Log::debug($del_count);
\Log::debug($del_error_count);
if ($del_error_count == 0) {
return redirect()->route('models.index')
->with('success', trans('admin/models/message.bulkdelete.success',['success_count'=> $del_count] ));
}
return redirect()->route('models.index')
->with('warning', trans('admin/models/message.bulkdelete.success_partial', ['fail_count'=>$del_error_count, 'success_count'=> $del_count]));
}
$data = array('total' => $assetsCount, 'rows' => $rows);
return redirect()->route('models.index')
->with('error', trans('admin/models/message.bulkdelete.error'));
return $data;
}
/**
* Returns true if a fieldset is set, 'add default values' is ticked and if
* any default values were entered into the form.
*
* @param array $input
* @return boolean
*/
private function shouldAddDefaultValues(array $input)
{
return !empty($input['add_default_values'])
&& !empty($input['default_values'])
&& !empty($input['custom_fieldset']);
}
/**
* Adds default values to a model (as long as they are truthy)
*
* @param AssetModel $model
* @param array $defaultValues
* @return void
*/
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues)
{
foreach ($defaultValues as $customFieldId => $defaultValue) {
if ($defaultValue) {
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
}
}
}
/**
* Removes all default values
*
* @return void
*/
private function removeCustomFieldsDefaultValues(AssetModel $model)
{
$model->defaultValues()->detach();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,296 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use App\Models\Setting;
use App\Models\Ldap;
use App\Models\User;
use Auth;
use Config;
use Illuminate\Http\Request;
use Input;
use Redirect;
use Log;
use View;
use PragmaRX\Google2FA\Google2FA;
/**
* This controller handles authentication for the user, including local
* database users and LDAP users.
*
* @todo Move LDAP methods into user model for better separation of concerns.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @version v1.0
*/
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
// This tells the auth controller to use username instead of email address
protected $username = 'username';
/**
* Where to redirect users after login / registration.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
}
function showLoginForm()
{
if (Auth::check()) {
return redirect()->intended('dashboard');
}
return View::make('auth.login');
}
private function login_via_ldap(Request $request)
{
LOG::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
if(!$ldap_user) {
LOG::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
throw new \Exception("Could not find user in LDAP directory");
} else {
LOG::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
}
// Check if the user already exists in the database and was imported via LDAP
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import','=',1)->first();
LOG::debug("Local auth lookup complete");
// The user does not exist in the database. Try to get them from LDAP.
// If user does not exist and authenticates successfully with LDAP we
// will create it on the fly and sign in with default permissions
if (!$user) {
LOG::debug("Local user ".Input::get('username')." does not exist");
LOG::debug("Creating local user ".Input::get('username'));
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
LOG::debug("Local user created.");
} else {
LOG::debug("Could not create local user.");
throw new \Exception("Could not create local user");
}
// If the user exists and they were imported from LDAP already
} else {
LOG::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}
$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname'];
$user->save();
} // End if(!user)
return $user;
}
/**
* Account sign in form processing.
*
* @return Redirect
*/
public function login(Request $request)
{
$validator = $this->validator(Input::all());
if ($validator->fails()) {
return redirect()->back()->withInput()->withErrors($validator);
}
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
$this->lockoutTime = config('auth.throttle.lockout_duration');
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = null;
// Should we even check for LDAP users?
if (Setting::getSettings()->ldap_enabled=='1') {
LOG::debug("LDAP is enabled.");
try {
$user = $this->login_via_ldap($request);
Auth::login($user, true);
// If the user was unable to login via LDAP, log the error and let them fall through to
// local authentication.
} catch (\Exception $e) {
LOG::error("There was an error authenticating the LDAP user: ".$e->getMessage());
}
}
// If the user wasn't authenticated via LDAP, skip to local auth
if(!$user) {
LOG::debug("Authenticating user against database.");
// Try to log the user in
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
LOG::debug("Local authentication failed.");
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
} else {
if ($throttles) {
$this->clearLoginAttempts($request);
}
}
}
// Get the page we were before
$redirect = \Session::get('loginRedirect', 'home');
// Unset the page we were before from the session
\Session::forget('loginRedirect');
// Redirect to the users page
return redirect()->to($redirect)->with('success', trans('auth/message.signin.success'));
}
/**
* Two factor enrollment page
*
* @return Redirect
*/
public function getTwoFactorEnroll()
{
if (!Auth::check()) {
return redirect()->route('login')->with('error', 'You must be logged in.');
}
$user = Auth::user();
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
if ($user->two_factor_secret=='') {
$user->two_factor_secret = $google2fa->generateSecretKey(32);
$user->save();
}
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
urlencode(Setting::getSettings()->site_name),
urlencode($user->username),
$user->two_factor_secret
);
return View::make('auth.two_factor_enroll')->with('google2fa_url',$google2fa_url);
}
/**
* Two factor code form page
*
* @return Redirect
*/
public function getTwoFactorAuth() {
return View::make('auth.two_factor');
}
/**
* Two factor code submission
*
* @return Redirect
*/
public function postTwoFactorAuth(Request $request) {
if (!Auth::check()) {
return redirect()->route('login')->with('error', 'You must be logged in.');
}
$user = Auth::user();
$secret = $request->get('two_factor_secret');
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);
if ($valid) {
$user->two_factor_enrolled = 1;
$user->save();
$request->session()->put('2fa_authed', 'true');
return redirect()->route('home')->with('success', 'You are logged in!');
}
return redirect()->route('two-factor')->with('error', 'Invalid two-factor code');
}
/**
* Logout page.
*
* @return Redirect
*/
public function logout(Request $request)
{
$request->session()->forget('2fa_authed');
Auth::logout();
return redirect()->route('login')->with('success', 'You have successfully logged out!');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'username' => 'required',
'password' => 'required',
]);
}
/**
* Get the login lockout error message.
*
* @param int $seconds
* @return string
*/
protected function getLockoutErrorMessage($seconds)
{
return \Lang::has('auth/message.throttle')
? \Lang::get('auth/message.throttle', ['seconds' => $seconds])
: 'Too many login attempts. Please try again in '.$seconds.' seconds.';
}
}

View File

@@ -0,0 +1,104 @@
<?php
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
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get the e-mail subject line to be used for the reset link email.
* Overriding method "getEmailSubject()" from trait "use ResetsPasswords"
* @return string
*/
public function getEmailSubject()
{
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function sendResetLinkEmail(Request $request)
{
/**
* 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'),
['activated' => '1']
)
);
if ($response === \Password::RESET_LINK_SENT) {
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.
*
* 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

@@ -0,0 +1,389 @@
<?php
namespace App\Http\Controllers\Auth;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use App\Models\Setting;
use App\Models\Ldap;
use App\Models\User;
use Auth;
use Config;
use Illuminate\Http\Request;
use Input;
use Redirect;
use Log;
use View;
use PragmaRX\Google2FA\Google2FA;
/**
* This controller handles authentication for the user, including local
* database users and LDAP users.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @version v1.0
*/
class LoginController extends Controller
{
use ThrottlesLogins;
// This tells the auth controller to use username instead of email address
protected $username = 'username';
/**
* Where to redirect users after login / registration.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
\Session::put('backUrl', \URL::previous());
}
function showLoginForm(Request $request)
{
$this->loginViaRemoteUser($request);
if (Auth::check()) {
return redirect()->intended('dashboard');
}
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
return view('auth.login');
}
private function loginViaRemoteUser(Request $request)
{
$remote_user = $request->server('REMOTE_USER');
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
Log::debug("Authenticatiing via REMOTE_USER.");
$pos = strpos($remote_user, '\\');
if ($pos > 0) {
$remote_user = substr($remote_user, $pos + 1);
};
try {
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
Log::debug("Remote user auth lookup complete");
if(!is_null($user)) Auth::login($user, true);
} catch(Exception $e) {
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
}
}
}
private function loginViaLdap(Request $request)
{
Log::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
if (!$ldap_user) {
Log::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
throw new \Exception("Could not find user in LDAP directory");
} else {
Log::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
}
// Check if the user already exists in the database and was imported via LDAP
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->where('activated', '=', '1')->first();
Log::debug("Local auth lookup complete");
// The user does not exist in the database. Try to get them from LDAP.
// If user does not exist and authenticates successfully with LDAP we
// will create it on the fly and sign in with default permissions
if (!$user) {
Log::debug("Local user ".Input::get('username')." does not exist");
Log::debug("Creating local user ".Input::get('username'));
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
Log::debug("Local user created.");
} else {
Log::debug("Could not create local user.");
throw new \Exception("Could not create local user");
}
// If the user exists and they were imported from LDAP already
} else {
Log::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}
$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname'];
$user->save();
} // End if(!user)
return $user;
}
/**
* Account sign in form processing.
*
* @return Redirect
*/
public function login(Request $request)
{
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
$validator = $this->validator(Input::all());
if ($validator->fails()) {
return redirect()->back()->withInput()->withErrors($validator);
}
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
$this->lockoutTime = config('auth.throttle.lockout_duration');
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = null;
// Should we even check for LDAP users?
if (Setting::getSettings()->ldap_enabled=='1') {
Log::debug("LDAP is enabled.");
try {
$user = $this->loginViaLdap($request);
Auth::login($user, true);
// If the user was unable to login via LDAP, log the error and let them fall through to
// local authentication.
} catch (\Exception $e) {
Log::debug("There was an error authenticating the LDAP user: ".$e->getMessage());
}
}
// If the user wasn't authenticated via LDAP, skip to local auth
if (!$user) {
Log::debug("Authenticating user against database.");
// Try to log the user in
if (!Auth::attempt(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
if (!$lockedOut) {
$this->incrementLoginAttempts($request);
}
Log::debug("Local authentication failed.");
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
} else {
$this->clearLoginAttempts($request);
}
}
if ($user = Auth::user()) {
$user->last_login = \Carbon::now();
$user->save();
}
// Redirect to the users page
return redirect()->intended()->with('success', trans('auth/message.signin.success'));
}
/**
* Two factor enrollment page
*
* @return Redirect
*/
public function getTwoFactorEnroll()
{
// Make sure the user is logged in
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
$settings = Setting::getSettings();
$user = Auth::user();
// We wouldn't normally see this page if 2FA isn't enforced via the
// \App\Http\Middleware\CheckForTwoFactor middleware AND if a device isn't enrolled,
// but let's check check anyway in case there's a browser history or back button thing.
// While you can access this page directly, enrolling a device when 2FA isn't enforced
// won't cause any harm.
if (($user->two_factor_secret!='') && ($user->two_factor_enrolled==1)) {
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.already_enrolled'));
}
$google2fa = new Google2FA();
$secret = $google2fa->generateSecretKey();
$user->two_factor_secret = $secret;
$user->save();
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj('QRCODE', 'otpauth://totp/'.urlencode($settings->site_name).':'.urlencode($user->username).'?secret='.urlencode($secret).'&issuer=Snipe-IT&period=30', 300, 300, 'black', array(-2, -2, -2, -2));
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
}
/**
* Two factor code form page
*
* @return Redirect
*/
public function getTwoFactorAuth()
{
// Check that the user is logged in
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
$user = Auth::user();
// Check whether there is a device enrolled.
// This *should* be handled via the \App\Http\Middleware\CheckForTwoFactor middleware
// but we're just making sure (in case someone edited the database directly, etc)
if (($user->two_factor_secret=='') || ($user->two_factor_enrolled!=1)) {
return redirect()->route('two-factor-enroll');
}
return view('auth.two_factor');
}
/**
* Two factor code submission
*
* @return Redirect
*/
public function postTwoFactorAuth(Request $request)
{
if (!Auth::check()) {
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
}
if (!$request->filled('two_factor_secret')) {
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.code_required'));
}
$user = Auth::user();
$google2fa = new Google2FA();
$secret = $request->input('two_factor_secret');
if ($google2fa->verifyKey($user->two_factor_secret, $secret)) {
$user->two_factor_enrolled = 1;
$user->save();
$request->session()->put('2fa_authed', 'true');
return redirect()->route('home')->with('success', 'You are logged in!');
}
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.invalid_code'));
}
/**
* Logout page.
*
* @return Redirect
*/
public function logout(Request $request)
{
$request->session()->regenerate(true);
Auth::logout();
$settings = Setting::getSettings();
$customLogoutUrl = $settings->login_remote_user_custom_logout_url ;
if ($settings->login_remote_user_enabled == '1' && $customLogoutUrl != '') {
return redirect()->away($customLogoutUrl);
}
return redirect()->route('login')->with('success', trans('auth/message.logout.success'));
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'username' => 'required',
'password' => 'required',
]);
}
public function username()
{
return 'username';
}
/**
* Redirect the user after determining they are locked out.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendLockoutResponse(Request $request)
{
$seconds = $this->limiter()->availableIn(
$this->throttleKey($request)
);
$minutes = round($seconds / 60);
$message = \Lang::get('auth/message.throttle', ['minutes' => $minutes]);
return redirect()->back()
->withInput($request->only($this->username(), 'remember'))
->withErrors([$this->username() => $message]);
}
/**
* Override the lockout time and duration
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function hasTooManyLoginAttempts(Request $request)
{
$lockoutTime = config('auth.throttle.lockout_duration');
$maxLoginAttempts = config('auth.throttle.max_attempts');
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request),
$maxLoginAttempts,
$lockoutTime
);
}
public function legacyAuthRedirect() {
return redirect()->route('login');
}
public function redirectTo()
{
return Session::get('backUrl') ? Session::get('backUrl') : $this->redirectTo;
}
}

View File

@@ -1,60 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
/**
* This controller handles password resets for the user.
* It uses the built-in Laravel magic of ResetsPasswords, so we
* don't actually do much here.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @version v1.0
*/
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Create a new password controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get the e-mail subject line to be used for the reset link email.
* Overriding method "getEmailSubject()" from trait "use ResetsPasswords"
* @return string
*/
public function getEmailSubject(){
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
}
/**
* Get the response for after the reset link has been successfully sent.
*
* @param string $response
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function getSendResetLinkEmailSuccessResponse($response)
{
return redirect()->route('login')->with('status', trans($response));
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class RegisterController extends Controller
{
public function __construct()
{
$this->middleware('guest');
}
public function showRegistrationForm() {
abort(404,'Page not found');
}
public function register() {
abort(404,'Page not found');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use App\Models\User;
use Illuminate\Http\Request;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View File

@@ -0,0 +1,249 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Models\Asset;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class BulkAssetsController extends Controller
{
use CheckInOutRequest;
/**
* Display the bulk edit page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return View
* @internal param int $assetId
* @since [v2.0]
*/
public function edit(Request $request)
{
$this->authorize('update', Asset::class);
if (!$request->filled('ids')) {
return redirect()->back()->with('error', 'No assets selected');
}
$asset_ids = array_keys($request->input('ids'));
if ($request->filled('bulk_actions')) {
switch($request->input('bulk_actions')) {
case 'labels':
return view('hardware/labels')
->with('assets', Asset::find($asset_ids))
->with('settings', Setting::getSettings())
->with('count', 0);
case 'delete':
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
$assets->each(function ($asset) {
$this->authorize('delete', $asset);
});
return view('hardware/bulk-delete')->with('assets', $assets);
case 'edit':
return view('hardware/bulk')
->with('assets', request('ids'))
->with('statuslabel_list', Helper::statusLabelList());
}
}
return redirect()->back()->with('error', 'No action selected');
}
/**
* Save bulk edits
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return Redirect
* @internal param array $assets
* @since [v2.0]
*/
public function update(Request $request)
{
$this->authorize('update', Asset::class);
\Log::debug($request->input('ids'));
if(!$request->filled('ids') || count($request->input('ids')) <= 0) {
return redirect()->route("hardware.index")->with('warning', trans('No assets selected, so nothing was updated.'));
}
$assets = array_keys($request->input('ids'));
if (($request->filled('purchase_date'))
|| ($request->filled('purchase_cost'))
|| ($request->filled('supplier_id'))
|| ($request->filled('order_number'))
|| ($request->filled('warranty_months'))
|| ($request->filled('rtd_location_id'))
|| ($request->filled('requestable'))
|| ($request->filled('company_id'))
|| ($request->filled('status_id'))
|| ($request->filled('model_id'))
) {
foreach ($assets as $assetId) {
$this->update_array = [];
$this->conditionallyAddItem('purchase_date')
->conditionallyAddItem('model_id')
->conditionallyAddItem('order_number')
->conditionallyAddItem('requestable')
->conditionallyAddItem('status_id')
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months');
if ($request->filled('purchase_cost')) {
$this->update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost'));
}
if ($request->filled('company_id')) {
$this->update_array['company_id'] = $request->input('company_id');
if ($request->input('company_id')=="clear") {
$this->update_array['company_id'] = null;
}
}
if ($request->filled('rtd_location_id')) {
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
$this->update_array['location_id'] = $request->input('rtd_location_id');
}
}
DB::table('assets')
->where('id', $assetId)
->update($this->update_array);
} // endforeach
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.update.success'));
// no values given, nothing to update
}
return redirect()->route("hardware.index")->with('warning', trans('admin/hardware/message.update.nothing_updated'));
}
/**
* Array to store update data per item
* @var Array
*/
private $update_array;
/**
* Adds parameter to update array for an item if it exists in request
* @param String $field field name
* @return this Model for Chaining
*/
protected function conditionallyAddItem($field)
{
if(request()->filled($field)) {
$this->update_array[$field] = request()->input($field);
}
return $this;
}
/**
* Save bulk deleted.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return View
* @internal param array $assets
* @since [v2.0]
*/
public function destroy(Request $request)
{
$this->authorize('delete', Asset::class);
if ($request->filled('ids')) {
$assets = Asset::find($request->get('ids'));
foreach ($assets as $asset) {
$update_array['deleted_at'] = date('Y-m-d H:i:s');
$update_array['assigned_to'] = null;
DB::table('assets')
->where('id', $asset->id)
->update($update_array);
} // endforeach
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.delete.success'));
// no values given, nothing to update
}
return redirect()->to("hardware")->with('info', trans('admin/hardware/message.delete.nothing_updated'));
}
/**
* Show Bulk Checkout Page
* @return View View to checkout multiple assets
*/
public function showCheckout()
{
$this->authorize('checkout', Asset::class);
// Filter out assets that are not deployable.
return view('hardware/bulk-checkout');
}
/**
* Process Multiple Checkout Request
* @return View
*/
public function storeCheckout(Request $request)
{
try {
$admin = Auth::user();
$target = $this->determineCheckoutTarget();
if (!is_array($request->get('selected_assets'))) {
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
}
$asset_ids = array_filter($request->get('selected_assets'));
foreach ($asset_ids as $asset_id) {
if ($target->id == $asset_id && request('checkout_to_type') =='asset') {
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
}
}
$checkout_at = date("Y-m-d H:i:s");
if (($request->filled('checkout_at')) && ($request->get('checkout_at')!= date("Y-m-d"))) {
$checkout_at = e($request->get('checkout_at'));
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = e($request->get('expected_checkin'));
}
$errors = [];
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
foreach ($asset_ids as $asset_id) {
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkout', $asset);
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), null);
if ($target->location_id!='') {
$asset->location_id = $target->location_id;
$asset->unsetEventDispatcher();
$asset->save();
}
if ($error) {
array_merge_recursive($errors, $asset->getErrors()->toArray());
}
}
});
if (!$errors) {
// Redirect to the new asset page
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
} catch (ModelNotFoundException $e) {
return redirect()->to("hardware/bulk-checkout")->with('error', $e->getErrors());
}
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Category as Category;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\Setting;
use Auth;
use DB;
@@ -14,6 +15,8 @@ use Lang;
use Redirect;
use Str;
use View;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This class controls all actions related to Categories for
@@ -32,12 +35,13 @@ class CategoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::getDatatable() method that generates the JSON response
* @since [v1.0]
* @return View
*/
public function getIndex()
* @return \Illuminate\Contracts\View\View
*/
public function index()
{
// Show the page
return View::make('categories/index');
$this->authorize('view', Category::class);
return view('categories/index');
}
@@ -45,16 +49,17 @@ class CategoriesController extends Controller
* Returns a form view to create a new category.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::postCreate() method that stores the data
* @see CategoriesController::store() method that stores the data
* @since [v1.0]
* @return View
*/
public function getCreate()
* @return \Illuminate\Contracts\View\View
*/
public function create()
{
// Show the page
$this->authorize('create', Category::class);
$category_types= Helper::categoryTypeList();
return View::make('categories/edit')->with('item', new Category)
->with('category_types', $category_types);
return view('categories/edit')->with('item', new Category)
->with('category_types', $category_types);
}
@@ -62,39 +67,29 @@ class CategoriesController extends Controller
* Validates and stores the new category data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::getCreate() method that makes the form.
* @see CategoriesController::create() method that makes the form.
* @since [v1.0]
* @return Redirect
*/
public function postCreate()
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ImageUploadRequest $request)
{
// create a new model instance
$this->authorize('create', Category::class);
$category = new Category();
$category->name = $request->input('name');
$category->category_type = $request->input('category_type');
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->user_id = Auth::id();
// Update the category data
$category->name = e(Input::get('name'));
$category->category_type = e(Input::get('category_type'));
$category->eula_text = e(Input::get('eula_text'));
$category->use_default_eula = e(Input::get('use_default_eula', '0'));
$category->require_acceptance = e(Input::get('require_acceptance', '0'));
$category->checkin_email = e(Input::get('checkin_email', '0'));
$category->user_id = Auth::user()->id;
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
if ($category->save()) {
// Redirect to the new category page
return redirect()->to("admin/settings/categories")->with('success', trans('admin/categories/message.create.success'));
} else {
// The given data did not pass validation
return redirect()->back()->withInput()->withErrors($category->getErrors());
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.create.success'));
}
// Redirect to the category create page
return redirect()->to('admin/settings/categories/create')->with('error', trans('admin/categories/message.create.error'));
return redirect()->back()->withInput()->withErrors($category->getErrors());
}
/**
@@ -104,67 +99,62 @@ class CategoriesController extends Controller
* @see CategoriesController::postEdit() method saves the data
* @param int $categoryId
* @since [v1.0]
* @return View
*/
public function getEdit($categoryId = null)
* @return \Illuminate\Contracts\View\View
*/
public function edit($categoryId = null)
{
// Check if the category exists
$this->authorize('edit', Category::class);
if (is_null($item = Category::find($categoryId))) {
// Redirect to the blogs management page
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.does_not_exist'));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
// Show the page
//$category_options = array('' => 'Top Level') + Category::lists('name', 'id');
$category_options = array('' => 'Top Level') + DB::table('categories')->where('id', '!=', $categoryId)->lists('name', 'id');
$category_types= Helper::categoryTypeList();
return View::make('categories/edit', compact('item'))
->with('category_options', $category_options)
return view('categories/edit', compact('item'))
->with('category_types', $category_types);
}
/**
* Validates and stores the updated category data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::getEdit() method that makes the form.
* @param int $categoryId
* @since [v1.0]
* @return Redirect
*/
public function postEdit(Request $request, $categoryId = null)
* Validates and stores the updated category data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::getEdit() method that makes the form.
* @param Request $request
* @param int $categoryId
* @return \Illuminate\Http\RedirectResponse
* @since [v1.0]
*/
public function update(ImageUploadRequest $request, $categoryId = null)
{
// Check if the blog post exists
$this->authorize('edit', Category::class);
if (is_null($category = Category::find($categoryId))) {
// Redirect to the blogs management page
// Redirect to the categories management page
return redirect()->to('admin/categories')->with('error', trans('admin/categories/message.does_not_exist'));
}
// Update the category data
$category->name = e($request->input('name'));
$category->name = $request->input('name');
// If the item count is > 0, we disable the category type in the edit. Disabled items
// don't POST, so if the category_type is blank we just set it to the default.
$category->category_type = e($request->input('category_type', $category->category_type));
$category->eula_text = e($request->input('eula_text'));
$category->use_default_eula = e($request->input('use_default_eula', '0'));
$category->require_acceptance = e($request->input('require_acceptance', '0'));
$category->checkin_email = e($request->input('checkin_email', '0'));
$category->category_type = $request->input('category_type', $category->category_type);
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
if ($category->save()) {
// Redirect to the new category page
return redirect()->to("admin/settings/categories")->with('success', trans('admin/categories/message.update.success'));
} // attempt validation
else {
// The given data did not pass validation
return redirect()->back()->withInput()->withErrors($category->getErrors());
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$category->image = null;
}
// Redirect to the category management page
return redirect()->back()->with('error', trans('admin/categories/message.update.error'));
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
if ($category->save()) {
// Redirect to the new category page
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.update.success'));
}
// The given data did not pass validation
return redirect()->back()->withInput()->withErrors($category->getErrors());
}
/**
@@ -173,41 +163,32 @@ class CategoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $categoryId
* @return Redirect
*/
public function getDelete($categoryId)
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($categoryId)
{
$this->authorize('delete', Category::class);
// Check if the category exists
if (is_null($category = Category::find($categoryId))) {
// Redirect to the blogs management page
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.not_found'));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
}
if ($category->has_models() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
} elseif ($category->accessories()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
} elseif ($category->consumables()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
} elseif ($category->components()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
} else {
$category->delete();
// Redirect to the locations management page
return redirect()->to('admin/settings/categories')->with('success', trans('admin/categories/message.delete.success'));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
}
$category->delete();
// Redirect to the locations management page
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
}
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the categories detail view, which is generated in getDataView.
@@ -216,319 +197,30 @@ class CategoriesController extends Controller
* @see CategoriesController::getDataView() method that generates the JSON response
* @param int $categoryId
* @since [v1.8]
* @return View
*/
public function getView($categoryId = null)
* @return \Illuminate\Contracts\View\View
*/
public function show($id)
{
$category = Category::find($categoryId);
$this->authorize('view', Category::class);
if ($category = Category::find($id)) {
if (isset($category->id)) {
return View::make('categories/view', compact('category'));
} else {
// Prepare the error message
$error = trans('admin/categories/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('categories')->with('error', $error);
}
}
/**
* Returns a JSON response with the data to populate the bootstrap table on the
* cateory listing page.
*
* @todo Refactor this nastiness. Assets do not behave the same as accessories, etc.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see CategoriesController::getIndex() method that generates the view
* @since [v1.8]
* @return String JSON
*/
public function getDatatable()
{
// Grab all the categories
$categories = Category::with('assets', 'accessories', 'consumables', 'components');
if (Input::has('search')) {
$categories = $categories->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['id','name','category_type'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
$categories = $categories->orderBy($sort, $order);
$catCount = $categories->count();
$categories = $categories->skip($offset)->take($limit)->get();
$rows = array();
foreach ($categories as $category) {
$actions = '<a href="'.route('update/category', $category->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;">';
$actions .='<i class="fa fa-pencil icon-white"></i></a>';
$actions .='<a data-html="false" class="btn delete-asset btn-danger btn-sm';
if ($category->itemCount() > 0) {
$actions .=' disabled';
}
$actions .=' data-toggle="modal" href="'.route('delete/category', $category->id).'" data-content="'.trans('admin/categories/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($category->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
$rows[] = array(
'id' => $category->id,
'name' => (string)link_to('/admin/settings/categories/'.$category->id.'/view', $category->name) ,
'category_type' => ucwords($category->category_type),
'count' => $category->itemCount(),
'acceptance' => ($category->require_acceptance=='1') ? '<i class="fa fa-check"></i>' : '',
'eula' => ($category->getEula()) ? '<i class="fa fa-check"></i>' : '',
'actions' => $actions
);
}
$data = array('total' => $catCount, 'rows' => $rows);
return $data;
}
public function getDataViewAssets($categoryID)
{
$category = Category::find($categoryID);
$category = $category->load('assets.company', 'assets.model', 'assets.assetstatus', 'assets.assigneduser');
$category_assets = $category->assets();
if (Input::has('search')) {
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$allowed_columns = ['id','name','serial','asset_tag'];
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
$count = $category_assets->count();
$category_assets = $category_assets->skip($offset)->take($limit)->get();
$rows = array();
foreach ($category_assets as $asset) {
$actions = '';
$inout='';
if ($asset->deleted_at=='') {
$actions = '<div style=" white-space: nowrap;"><a href="'.route('clone/hardware', $asset->id).'" class="btn btn-info btn-sm" title="Clone asset"><i class="fa fa-files-o"></i></a> <a href="'.route('update/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/hardware', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->asset_tag).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
} elseif ($asset->deleted_at!='') {
$actions = '<a href="'.route('restore/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-recycle icon-white"></i></a>';
}
if ($asset->availableForCheckout()) {
if (Gate::allows('assets.checkout')) {
$inout = '<a href="'.route('checkout/hardware', $asset->id).'" class="btn btn-info btn-sm">'.trans('general.checkout').'</a>';
}
if ($category->category_type=='asset') {
$category_type = 'hardware';
$category_type_route = 'assets';
} elseif ($category->category_type=='accessory') {
$category_type = 'accessories';
$category_type_route = 'accessories';
} else {
if (Gate::allows('assets.checkin')) {
$inout = '<a href="'.route('checkin/hardware', $asset->id).'" class="btn btn-primary btn-sm">'.trans('general.checkin').'</a>';
}
$category_type = $category->category_type;
$category_type_route = $category->category_type.'s';
}
$rows[] = array(
'id' => $asset->id,
'name' => (string)link_to('/hardware/'.$asset->id.'/view', $asset->showAssetName()),
'model' => ($asset->model) ? (string)link_to('hardware/models/'.$asset->model->id.'/view', $asset->model->name) : '',
'asset_tag' => $asset->asset_tag,
'serial' => $asset->serial,
'assigned_to' => ($asset->assigneduser) ? (string)link_to('/admin/users/'.$asset->assigneduser->id.'/view', $asset->assigneduser->fullName()): '',
'change' => $inout,
'actions' => $actions,
'companyName' => is_null($asset->company) ? '' : e($asset->company->name)
);
return view('categories/view', compact('category'))
->with('category_type',$category_type)
->with('category_type_route',$category_type_route);
}
$data = array('total' => $count, 'rows' => $rows);
return $data;
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
public function getDataViewAccessories($categoryID)
{
$category = Category::with('accessories.company')->find($categoryID);
$category_assets = $category->accessories;
if (Input::has('search')) {
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$allowed_columns = ['id','name','serial','asset_tag'];
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
$count = $category_assets->count();
$rows = array();
foreach ($category_assets as $asset) {
$actions = '';
$inout='';
if ($asset->deleted_at=='') {
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/accessory', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/accessory', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
}
$rows[] = array(
'id' => $asset->id,
'name' => (string)link_to_route('view/accessory', $asset->name, [$asset->id]),
'actions' => $actions,
'companyName' => Company::getName($asset),
);
}
$data = array('total' => $count, 'rows' => $rows);
return $data;
}
public function getDataViewConsumables($categoryID)
{
$category = Category::with('accessories.company')->find($categoryID);
$category_assets = $category->consumables;
if (Input::has('search')) {
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$allowed_columns = ['id','name','serial','asset_tag'];
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
$count = $category_assets->count();
$rows = array();
foreach ($category_assets as $asset) {
$actions = '';
$inout='';
if ($asset->deleted_at=='') {
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/consumable', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/consumable', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
}
$rows[] = array(
'id' => $asset->id,
'name' => (string) link_to_route('view/consumable', $asset->name, [$asset->id]),
'actions' => $actions,
'companyName' => Company::getName($asset),
);
}
$data = array('total' => $count, 'rows' => $rows);
return $data;
}
public function getDataViewComponent($categoryID)
{
$category = Category::with('accessories.company')->find($categoryID);
$category_assets = $category->components;
if (Input::has('search')) {
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$allowed_columns = ['id','name','serial','asset_tag'];
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
$count = $category_assets->count();
$rows = array();
foreach ($category_assets as $asset) {
$actions = '';
$inout='';
if ($asset->deleted_at=='') {
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/component', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/component', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
}
$rows[] = array(
'id' => $asset->id,
'name' => (string)link_to_route('view/accessory', $asset->name, [$asset->id]),
'actions' => $actions,
'companyName' => Company::getName($asset),
);
}
$data = array('total' => $count, 'rows' => $rows);
return $data;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers;
use App\Exceptions\CheckoutNotAllowed;
use App\Models\Asset;
use App\Models\Location;
use App\Models\User;
trait CheckInOutRequest
{
/**
* Find target for checkout
* @return SnipeModel Target asset is being checked out to.
*/
protected function determineCheckoutTarget()
{
// This item is checked out to a location
switch(request('checkout_to_type'))
{
case 'location':
return Location::findOrFail(request('assigned_location'));
case 'asset':
return Asset::findOrFail(request('assigned_asset'));
case 'user':
return User::findOrFail(request('assigned_user'));
}
return null;
}
/**
* Update the location of the asset passed in.
* @param Asset $asset Asset being updated
* @param SnipeModel $target Target with location
* @return Asset Asset being updated
*/
protected function updateAssetLocation($asset, $target)
{
switch(request('checkout_to_type'))
{
case 'location':
$asset->location_id = $target->id;
break;
case '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->location_id;
}
break;
case 'user':
$asset->location_id = $target->location_id;
break;
}
return $asset;
}
}

View File

@@ -6,6 +6,9 @@ use Input;
use Lang;
use Redirect;
use View;
use Illuminate\Http\Request;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to Companies for
@@ -22,11 +25,13 @@ final class CompaniesController extends Controller
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @return View
*/
public function getIndex()
* @return \Illuminate\Contracts\View\View
*/
public function index()
{
return View::make('companies/index')->with('companies', Company::all());
$this->authorize('view', Company::class);
return view('companies/index');
}
/**
@@ -34,33 +39,37 @@ final class CompaniesController extends Controller
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @return View
*/
public function getCreate()
* @return \Illuminate\Contracts\View\View
*/
public function create()
{
return View::make('companies/edit')->with('item', new Company);
$this->authorize('create', Company::class);
return view('companies/edit')->with('item', new Company);
}
/**
* Save data from new company form.
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @return Redirect
*/
public function postCreate()
* Save data from new company form.
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ImageUploadRequest $request)
{
$company = new Company;
$this->authorize('create', Company::class);
$company->name = e(Input::get('name'));
$company = new Company;
$company->name = $request->input('name');
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
if ($company->save()) {
return redirect()->to('admin/settings/companies')
return redirect()->route('companies.index')
->with('success', trans('admin/companies/message.create.success'));
} else {
return redirect()->back()->withInput()->withErrors($company->getErrors());
}
return redirect()->back()->withInput()->withErrors($company->getErrors());
}
@@ -70,44 +79,53 @@ final class CompaniesController extends Controller
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @param int $companyId
* @return View
*/
public function getEdit($companyId)
* @return \Illuminate\Contracts\View\View
*/
public function edit($companyId)
{
if (is_null($item = Company::find($companyId))) {
return redirect()->to('admin/settings/companies')
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.does_not_exist'));
} else {
return View::make('companies/edit')->with('item', $item);
}
$this->authorize('update', $item);
return view('companies/edit')->with('item', $item);
}
/**
* Save data from edit company form.
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @param int $companyId
* @return Redirect
*/
public function postEdit($companyId)
* Save data from edit company form.
*
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @param Request $request
* @param int $companyId
* @return \Illuminate\Http\RedirectResponse
*/
public function update(ImageUploadRequest $request, $companyId)
{
if (is_null($company = Company::find($companyId))) {
return redirect()->to('admin/settings/companies')->with('error', trans('admin/companies/message.does_not_exist'));
} else {
$company->name = e(Input::get('name'));
if ($company->save()) {
return redirect()->to('admin/settings/companies')
->with('success', trans('admin/companies/message.update.success'));
} else {
return redirect()->to("admin/settings/companies/$companyId/edit")
->with('error', trans('admin/companies/message.update.error'));
}
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
}
$this->authorize('update', $company);
$company->name = $request->input('name');
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$company->image = null;
}
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
if ($company->save()) {
return redirect()->route('companies.index')
->with('success', trans('admin/companies/message.update.success'));
}
return redirect()->route('companies.edit', ['company' => $companyId])
->with('error', trans('admin/companies/message.update.error'));
}
/**
@@ -116,17 +134,20 @@ final class CompaniesController extends Controller
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
* @since [v1.8]
* @param int $companyId
* @return Redirect
*/
public function postDelete($companyId)
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($companyId)
{
if (is_null($company = Company::find($companyId))) {
return redirect()->to('admin/settings/companies')
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
} else {
$this->authorize('delete', $company);
try {
$company->delete();
return redirect()->to('admin/settings/companies')
return redirect()->route('companies.index')
->with('success', trans('admin/companies/message.delete.success'));
} catch (\Illuminate\Database\QueryException $exception) {
/*
@@ -134,7 +155,7 @@ final class CompaniesController extends Controller
* For example when rows in other tables are referencing this company
*/
if ($exception->getCode() == 23000) {
return redirect()->to('admin/settings/companies')
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.assoc_users'));
} else {
throw $exception;
@@ -142,4 +163,16 @@ final class CompaniesController extends Controller
}
}
}
public function show($id) {
$this->authorize('view', Company::class);
if (is_null($company = Company::find($id))) {
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
} else {
return view('companies/view')->with('company',$company);
}
}
}

View File

@@ -2,9 +2,11 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Component;
use App\Models\CustomField;
use App\Models\Setting;
use App\Models\User;
use App\Models\Asset;
@@ -21,6 +23,7 @@ use View;
use Validator;
use Illuminate\Http\Request;
use Gate;
use Image;
/**
* This class controls all actions related to Components for
@@ -37,11 +40,12 @@ class ComponentsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getDatatable() method that generates the JSON response
* @since [v3.0]
* @return View
* @return \Illuminate\Contracts\View\View
*/
public function getIndex()
public function index()
{
return View::make('components/index');
$this->authorize('view', Component::class);
return view('components/index');
}
@@ -51,20 +55,14 @@ class ComponentsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::postCreate() method that stores the data
* @since [v3.0]
* @return View
* @return \Illuminate\Contracts\View\View
*/
public function getCreate()
public function create()
{
// Show the page
$category_list = Helper::categoryList('component');
$company_list = Helper::companyList();
$location_list = Helper::locationsList();
return View::make('components/edit')
->with('item', new Component)
->with('category_list', $category_list)
->with('company_list', $company_list)
->with('location_list', $location_list);
$this->authorize('create', Component::class);
$category_type = 'component';
return view('components/edit')->with('category_type',$category_type)
->with('item', new Component);
}
@@ -74,48 +72,31 @@ class ComponentsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getCreate() method that generates the view
* @since [v3.0]
* @return Redirect
*/
public function postCreate()
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ImageUploadRequest $request)
{
// create a new model instance
$this->authorize('create', Component::class);
$component = new Component();
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number', null);
$component->min_amt = $request->input('min_amt', null);
$component->serial = $request->input('serial', null);
$component->purchase_date = $request->input('purchase_date', null);
$component->purchase_cost = $request->input('purchase_cost', null);
$component->qty = $request->input('qty');
$component->user_id = Auth::id();
// Update the component data
$component->name = e(Input::get('name'));
$component->category_id = e(Input::get('category_id'));
$component->location_id = e(Input::get('location_id'));
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$component->order_number = e(Input::get('order_number'));
$component->min_amt = e(Input::get('min_amt'));
$component->serial = e(Input::get('serial'));
if (e(Input::get('purchase_date')) == '') {
$component->purchase_date = null;
} else {
$component->purchase_date = e(Input::get('purchase_date'));
}
$component = $request->handleImages($component,600, public_path().'/uploads/components');
if (e(Input::get('purchase_cost')) == '0.00') {
$component->purchase_cost = null;
} else {
$component->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
}
$component->qty = e(Input::get('qty'));
$component->user_id = Auth::user()->id;
// Was the component created?
if ($component->save()) {
$component->logCreate();
// Redirect to the new component page
return redirect()->to("admin/components")->with('success', trans('admin/components/message.create.success'));
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
}
/**
@@ -125,26 +106,22 @@ class ComponentsController extends Controller
* @see ComponentsController::postEdit() method that stores the data.
* @since [v3.0]
* @param int $componentId
* @return View
*/
public function getEdit($componentId = null)
* @return \Illuminate\Contracts\View\View
*/
public function edit($componentId = null)
{
// Check if the component exists
if (is_null($item = Component::find($componentId))) {
// Redirect to the blogs management page
return redirect()->to('admin/components')->with('error', trans('admin/components/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($item)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
if ($item = Component::find($componentId)) {
$this->authorize('update', $item);
$category_type = 'component';
return view('components/edit', compact('item'))->with('category_type', $category_type);
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
$category_list = Helper::categoryList('component');
$company_list = Helper::companyList();
$location_list = Helper::locationsList();
return View::make('components/edit', compact('item'))
->with('category_list', $category_list)
->with('company_list', $company_list)
->with('location_list', $location_list);
}
@@ -155,53 +132,35 @@ class ComponentsController extends Controller
* @see ComponentsController::getEdit() method presents the form.
* @param int $componentId
* @since [v3.0]
* @return Redirect
*/
public function postEdit($componentId = null)
* @return \Illuminate\Http\RedirectResponse
*/
public function update(ImageUploadRequest $request, $componentId = null)
{
// Check if the blog post exists
if (is_null($component = Component::find($componentId))) {
// Redirect to the blogs management page
return redirect()->to('admin/components')->with('error', trans('admin/components/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($component)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
$this->authorize('update', $component);
// Update the component data
$component->name = e(Input::get('name'));
$component->category_id = e(Input::get('category_id'));
$component->location_id = e(Input::get('location_id'));
$component->name = Input::get('name');
$component->category_id = Input::get('category_id');
$component->location_id = Input::get('location_id');
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$component->order_number = e(Input::get('order_number'));
$component->min_amt = e(Input::get('min_amt'));
$component->serial = e(Input::get('serial'));
$component->order_number = Input::get('order_number');
$component->min_amt = Input::get('min_amt');
$component->serial = Input::get('serial');
$component->purchase_date = Input::get('purchase_date');
$component->purchase_cost = request('purchase_cost');
$component->qty = Input::get('qty');
if (e(Input::get('purchase_date')) == '') {
$component->purchase_date = null;
} else {
$component->purchase_date = e(Input::get('purchase_date'));
}
$component = $request->handleImages($component,600, public_path().'/uploads/components');
if (e(Input::get('purchase_cost')) == '0.00') {
$component->purchase_cost = null;
} else {
$component->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
}
$component->qty = e(Input::get('qty'));
// Was the component created?
if ($component->save()) {
// Redirect to the new component page
return redirect()->to("admin/components")->with('success', trans('admin/components/message.update.success'));
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
}
/**
@@ -210,36 +169,19 @@ class ComponentsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param int $componentId
* @return Redirect
*/
public function getDelete($componentId)
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($componentId)
{
// Check if the blog post exists
if (is_null($component = Component::find($componentId))) {
// Redirect to the blogs management page
return redirect()->to('admin/components')->with('error', trans('admin/components/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($component)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
$component->delete();
// Redirect to the locations management page
return redirect()->to('admin/components')->with('success', trans('admin/components/message.delete.success'));
$this->authorize('delete', $component);
$component->delete();
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
}
public function postBulk($componentId = null)
{
echo 'Stubbed - not yet complete';
}
public function postBulkSave($componentId = null)
{
echo 'Stubbed - not yet complete';
}
/**
* Return a view to display component information.
*
@@ -247,29 +189,18 @@ class ComponentsController extends Controller
* @see ComponentsController::getDataView() method that generates the JSON response
* @since [v3.0]
* @param int $componentId
* @return View
*/
public function getView($componentId = null)
* @return \Illuminate\Contracts\View\View
*/
public function show($componentId = null)
{
$component = Component::find($componentId);
if (isset($component->id)) {
if (!Company::isCurrentUserHasAccess($component)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
} else {
return View::make('components/view', compact('component'));
}
} else {
// Prepare the error message
$error = trans('admin/components/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('components')->with('error', $error);
$this->authorize('view', $component);
return view('components/view', compact('component'));
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
/**
@@ -279,50 +210,41 @@ class ComponentsController extends Controller
* @see ComponentsController::postCheckout() method that stores the data.
* @since [v3.0]
* @param int $componentId
* @return View
*/
* @return \Illuminate\Contracts\View\View
*/
public function getCheckout($componentId)
{
// Check if the component exists
if (is_null($component = Component::find($componentId))) {
// Redirect to the component management page with error
return redirect()->to('components')->with('error', trans('admin/components/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($component)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
// Get the dropdown of assets and then pass it to the checkout view
$assets_list = Helper::detailedAssetList();
return View::make('components/checkout', compact('component'))->with('assets_list', $assets_list);
$this->authorize('checkout', $component);
return view('components/checkout', compact('component'));
}
/**
* Validate and store checkout data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getCheckout() method that returns the form.
* @since [v3.0]
* @param int $componentId
* @return Redirect
*/
* Validate and store checkout data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getCheckout() method that returns the form.
* @since [v3.0]
* @param Request $request
* @param int $componentId
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckout(Request $request, $componentId)
{
// Check if the component exists
if (is_null($component = Component::find($componentId))) {
// Redirect to the component management page with error
return redirect()->to('components')->with('error', trans('admin/components/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($component)) {
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
$this->authorize('checkout', $component);
$max_to_checkout = $component->numRemaining();
$validator = Validator::make($request->all(),[
$validator = Validator::make($request->all(), [
"asset_id" => "required",
"assigned_qty" => "required|numeric|between:1,$max_to_checkout"
]);
@@ -339,190 +261,115 @@ class ComponentsController extends Controller
// Check if the user exists
if (is_null($asset = Asset::find($asset_id))) {
// Redirect to the component management page with error
return redirect()->to('admin/components')->with('error', trans('admin/components/message.asset_does_not_exist'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.asset_does_not_exist'));
}
// Update the component data
$component->asset_id = $asset_id;
$component->assets()->attach($component->id, array(
'component_id' => $component->id,
'user_id' => $admin_user->id,
'created_at' => date('Y-m-d H:i:s'),
'assigned_qty' => e(Input::get('assigned_qty')),
'asset_id' => $asset_id));
$logaction = $component->logCheckout(e(Input::get('note')), $asset_id);
$settings = Setting::getSettings();
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => $settings->botname,
'channel' => $settings->slack_channel,
'link_names' => true
];
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
try {
$client->attach([
'color' => 'good',
'fields' => [
[
'title' => 'Checked Out:',
'value' => class_basename(strtoupper($logaction->item_type)).' <'.config('app.url').'/admin/components/'.$component->id.'/view'.'|'.$component->name.'> checked out to <'.config('app.url').'/hardware/'.$asset->id.'/view|'.$asset->showAssetName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
],
[
'title' => 'Note:',
'value' => e($logaction->note)
],
]
])->send('Component Checked Out');
} catch (Exception $e) {
}
}
// Redirect to the new component page
return redirect()->to("admin/components")->with('success', trans('admin/components/message.checkout.success'));
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'user_id' => $admin_user->id,
'created_at' => date('Y-m-d H:i:s'),
'assigned_qty' => Input::get('assigned_qty'),
'asset_id' => $asset_id
]);
$component->logCheckout(e(Input::get('note')), $asset);
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
}
/**
* Generates the JSON response for accessories listing view.
*
* For debugging, see at /api/accessories/list
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return string JSON
**/
public function getDatatable()
* Returns a view that allows the checkin of a component from an asset.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::postCheckout() method that stores the data.
* @since [v4.1.4]
* @param int $componentId
* @return \Illuminate\Contracts\View\View
*/
public function getCheckin($component_asset_id)
{
$components = Company::scopeCompanyables(Component::select('components.*')->whereNull('components.deleted_at')
->with('company', 'location', 'category'));
if (Input::has('search')) {
$components = $components->TextSearch(Input::get('search'));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','companyName','category','total_qty'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
switch ($sort) {
case 'category':
$components = $components->OrderCategory($order);
break;
case 'location':
$components = $components->OrderLocation($order);
break;
case 'companyName':
$components = $components->OrderCompany($order);
break;
default:
$components = $components->orderBy($sort, $order);
break;
}
$consumCount = $components->count();
$components = $components->skip($offset)->take($limit)->get();
$rows = array();
foreach ($components as $component) {
$actions = '<nobr>';
if (Gate::allows('components.checkout')) {
$actions .= '<a href="' . route('checkout/component',
$component->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm ' . (($component->numRemaining() > 0) ? '' : ' disabled') . '" ' . (($component->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
// This could probably be done more cleanly but I am very tired. - @snipe
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
}
if (Gate::allows('components.edit')) {
$actions .= '<a href="' . route('update/component',
$component->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
if (is_null($asset = Asset::find($component_assets->asset_id))) {
return redirect()->route('components.index')->with('error',
trans('admin/components/message.not_found'));
}
if (Gate::allows('components.delete')) {
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/component',
$component->id) . '" data-content="' . trans('admin/components/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($component->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
}
$actions .='</nobr>';
$company = $component->company;
$rows[] = array(
'checkbox' =>'<div class="text-center"><input type="checkbox" name="component['.$component->id.']" class="one_required"></div>',
'id' => $component->id,
'name' => (string)link_to('admin/components/'.$component->id.'/view', e($component->name)),
'serial_number' => $component->serial,
'location' => ($component->location) ? e($component->location->name) : '',
'qty' => e($component->qty),
'min_amt' => e($component->min_amt),
'category' => ($component->category) ? e($component->category->name) : 'Missing category',
'order_number' => e($component->order_number),
'purchase_date' => e($component->purchase_date),
'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost),
'numRemaining' => $component->numRemaining(),
'actions' => $actions,
'companyName' => is_null($company) ? '' : e($company->name),
);
$this->authorize('checkin', $component);
return view('components/checkin', compact('component_assets','component','asset'));
}
$data = array('total' => $consumCount, 'rows' => $rows);
return $data;
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
}
/**
* Return JSON data to populate the components view,
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getView() method that returns the view.
* @since [v3.0]
* @param int $componentId
* @return string JSON
*/
public function getDataView($componentId)
* Validate and store checkin data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getCheckout() method that returns the form.
* @since [v4.1.4]
* @param Request $request
* @param int $componentId
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckin(Request $request, $component_asset_id)
{
//$component = Component::find($componentID);
$component = Component::with('assets')->find($componentId);
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return redirect()->route('components.index')->with('error',
trans('admin/components/message.not_found'));
}
if (!Company::isCurrentUserHasAccess($component)) {
return ['total' => 0, 'rows' => []];
$this->authorize('checkin', $component);
$max_to_checkin = $component_assets->assigned_qty;
$validator = Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Validation passed, so let's figure out what we have to do here.
$qty_remaining_in_checkout = ($component_assets->assigned_qty - (int)$request->input('checkin_qty'));
// We have to modify the record to reflect the new qty that's
// actually checked out.
$component_assets->assigned_qty = $qty_remaining_in_checkout;
DB::table('components_assets')->where('id',
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
$log = new Actionlog();
$log->user_id = Auth::user()->id;
$log->action_type = 'checkin from';
$log->target_type = Asset::class;
$log->target_id = $component_assets->asset_id;
$log->item_id = $component_assets->component_id;
$log->item_type = Component::class;
$log->note = $request->input('note');
$log->save();
// If the checked-in qty is exactly the same as the assigned_qty,
// we can simply delete the associated components_assets record
if ($qty_remaining_in_checkout == 0) {
DB::table('components_assets')->where('id', '=', $component_asset_id)->delete();
}
return redirect()->route('components.index')->with('success',
trans('admin/components/message.checkout.success'));
}
$rows = array();
foreach ($component->assets as $component_assignment) {
$rows[] = array(
'name' => (string)link_to('/hardware/'.$component_assignment->id.'/view', e($component_assignment->showAssetName())),
'qty' => e($component_assignment->pivot->assigned_qty),
'created_at' => ($component_assignment->created_at->format('Y-m-d H:i:s')=='-0001-11-30 00:00:00') ? '' : $component_assignment->created_at->format('Y-m-d H:i:s'),
);
}
$componentCount = $component->assets->count();
$data = array('total' => $componentCount, 'rows' => $rows);
return $data;
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\Setting;
@@ -13,12 +12,13 @@ use Config;
use DB;
use Input;
use Lang;
use Mail;
use Redirect;
use Slack;
use Str;
use View;
use Gate;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to Consumables for
@@ -34,11 +34,12 @@ class ConsumablesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ConsumablesController::getDatatable() method that generates the JSON response
* @since [v1.0]
* @return View
*/
public function getIndex()
* @return \Illuminate\Contracts\View\View
*/
public function index()
{
return View::make('consumables/index');
$this->authorize('index', Consumable::class);
return view('consumables/index');
}
@@ -48,22 +49,14 @@ class ConsumablesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ConsumablesController::postCreate() method that stores the form data
* @since [v1.0]
* @return View
*/
public function getCreate()
* @return \Illuminate\Contracts\View\View
*/
public function create()
{
// Show the page
$category_list = Helper::categoryList('consumable');
$company_list = Helper::companyList();
$location_list = Helper::locationsList();
$manufacturer_list = Helper::manufacturerList();
return View::make('consumables/edit')
->with('item', new Consumable)
->with('category_list', $category_list)
->with('company_list', $company_list)
->with('location_list', $location_list)
->with('manufacturer_list', $manufacturer_list);
$this->authorize('create', Consumable::class);
$category_type = 'consumable';
return view('consumables/edit')->with('category_type', $category_type)
->with('item', new Consumable);
}
@@ -73,46 +66,36 @@ class ConsumablesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ConsumablesController::getCreate() method that returns the form view
* @since [v1.0]
* @return Redirect
*/
public function postCreate()
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Consumable::class);
$consumable = new Consumable();
$consumable->name = e(Input::get('name'));
$consumable->category_id = e(Input::get('category_id'));
$consumable->location_id = e(Input::get('location_id'));
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$consumable->order_number = e(Input::get('order_number'));
$consumable->min_amt = e(Input::get('min_amt'));
$consumable->manufacturer_id = e(Input::get('manufacturer_id'));
$consumable->model_number = e(Input::get('model_number'));
$consumable->item_no = e(Input::get('item_no'));
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
$consumable->min_amt = $request->input('min_amt');
$consumable->manufacturer_id = $request->input('manufacturer_id');
$consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat($request->input('purchase_cost'));
$consumable->qty = $request->input('qty');
$consumable->user_id = Auth::id();
if (e(Input::get('purchase_date')) == '') {
$consumable->purchase_date = null;
} else {
$consumable->purchase_date = e(Input::get('purchase_date'));
}
if (e(Input::get('purchase_cost')) == '0.00') {
$consumable->purchase_cost = null;
} else {
$consumable->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
}
$consumable = $request->handleImages($consumable,600, public_path().'/uploads/components');
$consumable->qty = e(Input::get('qty'));
$consumable->user_id = Auth::user()->id;
// Was the consumable created?
if ($consumable->save()) {
$consumable->logCreate();
// Redirect to the new consumable page
return redirect()->to("admin/consumables")->with('success', trans('admin/consumables/message.create.success'));
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
}
/**
@@ -122,28 +105,18 @@ class ConsumablesController extends Controller
* @param int $consumableId
* @see ConsumablesController::postEdit() method that stores the form data.
* @since [v1.0]
* @return View
*/
public function getEdit($consumableId = null)
* @return \Illuminate\Contracts\View\View
*/
public function edit($consumableId = null)
{
// Check if the consumable exists
if (is_null($item = Consumable::find($consumableId))) {
// Redirect to the blogs management page
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($item)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
if ($item = Consumable::find($consumableId)) {
$this->authorize($item);
$category_type = 'consumable';
return view('consumables/edit', compact('item'))->with('category_type', $category_type);
}
$category_list = Helper::categoryList('consumable');
$company_list = Helper::companyList();
$location_list = Helper::locationsList();
$manufacturer_list = Helper::manufacturerList();
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
return View::make('consumables/edit', compact('item'))
->with('category_list', $category_list)
->with('company_list', $company_list)
->with('location_list', $location_list)
->with('manufacturer_list', $manufacturer_list);
}
@@ -154,46 +127,46 @@ class ConsumablesController extends Controller
* @param int $consumableId
* @see ConsumablesController::getEdit() method that stores the form data.
* @since [v1.0]
* @return Redirect
*/
public function postEdit($consumableId = null)
* @return \Illuminate\Http\RedirectResponse
*/
public function update(ImageUploadRequest $request, $consumableId = null)
{
if (is_null($consumable = Consumable::find($consumableId))) {
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.does_not_exist'));
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
$consumable->name = e(Input::get('name'));
$consumable->category_id = e(Input::get('category_id'));
$consumable->location_id = e(Input::get('location_id'));
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$consumable->order_number = e(Input::get('order_number'));
$consumable->min_amt = e(Input::get('min_amt'));
$consumable->manufacturer_id = e(Input::get('manufacturer_id'));
$consumable->model_number = e(Input::get('model_number'));
$consumable->item_no = e(Input::get('item_no'));
$this->authorize($consumable);
if (e(Input::get('purchase_date')) == '') {
$consumable->purchase_date = null;
} else {
$consumable->purchase_date = e(Input::get('purchase_date'));
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
$consumable->min_amt = $request->input('min_amt');
$consumable->manufacturer_id = $request->input('manufacturer_id');
$consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
$consumable->qty = Helper::ParseFloat(Input::get('qty'));
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;
} elseif ($request->input('image_delete')=='1') {
$consumable->image = null;
}
if (e(Input::get('purchase_cost')) == '0.00') {
$consumable->purchase_cost = null;
} else {
$consumable->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
}
$consumable->qty = Helper::ParseFloat(e(Input::get('qty')));
if ($consumable->save()) {
return redirect()->to("admin/consumables")->with('success', trans('admin/consumables/message.update.success'));
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
}
/**
@@ -202,27 +175,19 @@ class ConsumablesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $consumableId
* @since [v1.0]
* @return Redirect
*/
public function getDelete($consumableId)
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($consumableId)
{
// Check if the blog post exists
if (is_null($consumable = Consumable::find($consumableId))) {
// Redirect to the blogs management page
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
}
$consumable->delete();
// Redirect to the locations management page
return redirect()->to('admin/consumables')->with('success', trans('admin/consumables/message.delete.success'));
$this->authorize($consumable);
$consumable->delete();
// Redirect to the locations management page
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.delete.success'));
}
/**
* Return a view to display component information.
*
@@ -230,29 +195,16 @@ class ConsumablesController extends Controller
* @see ConsumablesController::getDataView() method that generates the JSON response
* @since [v1.0]
* @param int $consumableId
* @return View
*/
public function getView($consumableId = null)
* @return \Illuminate\Contracts\View\View
*/
public function show($consumableId = null)
{
$consumable = Consumable::find($consumableId);
$this->authorize($consumable);
if (isset($consumable->id)) {
if (!Company::isCurrentUserHasAccess($consumable)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
} else {
return View::make('consumables/view', compact('consumable'));
}
} else {
// Prepare the error message
$error = trans('admin/consumables/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('consumables')->with('error', $error);
return view('consumables/view', compact('consumable'));
}
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
/**
@@ -262,23 +214,15 @@ class ConsumablesController extends Controller
* @see ConsumablesController::postCheckout() method that stores the data.
* @since [v1.0]
* @param int $consumableId
* @return View
*/
* @return \Illuminate\Contracts\View\View
*/
public function getCheckout($consumableId)
{
// Check if the consumable exists
if (is_null($consumable = Consumable::find($consumableId))) {
// Redirect to the consumable management page with error
return redirect()->to('consumables')->with('error', trans('admin/consumables/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
// Get the dropdown of users and then pass it to the checkout view
$users_list = Helper::usersList();
return View::make('consumables/checkout', compact('consumable'))->with('users_list', $users_list);
$this->authorize('checkout', $consumable);
return view('consumables/checkout', compact('consumable'));
}
/**
@@ -288,71 +232,35 @@ class ConsumablesController extends Controller
* @see ConsumablesController::getCheckout() method that returns the form.
* @since [v1.0]
* @param int $consumableId
* @return Redirect
*/
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckout($consumableId)
{
// Check if the consumable exists
if (is_null($consumable = Consumable::find($consumableId))) {
// Redirect to the consumable management page with error
return redirect()->to('consumables')->with('error', trans('admin/consumables/message.not_found'));
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
}
$this->authorize('checkout', $consumable);
$admin_user = Auth::user();
$assigned_to = e(Input::get('assigned_to'));
// Check if the user exists
// Check if the user exists
if (is_null($user = User::find($assigned_to))) {
// Redirect to the consumable management page with error
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.user_does_not_exist'));
return redirect()->route('checkout/consumable', $consumable)->with('error', trans('admin/consumables/message.checkout.user_does_not_exist'));
}
// Update the consumable data
// Update the consumable data
$consumable->assigned_to = e(Input::get('assigned_to'));
$consumable->users()->attach($consumable->id, array(
'consumable_id' => $consumable->id,
'user_id' => $admin_user->id,
'assigned_to' => e(Input::get('assigned_to'))));
$logaction = $consumable->logCheckout(e(Input::get('note')));
$settings = Setting::getSettings();
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => $settings->botname,
'channel' => $settings->slack_channel,
'link_names' => true
];
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
try {
$client->attach([
'color' => 'good',
'fields' => [
[
'title' => 'Checked Out:',
'value' => 'Consumable <'.config('app.url').'/admin/consumables/'.$consumable->id.'/view'.'|'.$consumable->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
],
[
'title' => 'Note:',
'value' => e($logaction->note)
],
]
])->send('Consumable Checked Out');
} catch (Exception $e) {
}
}
$consumable_user = DB::table('consumables_users')->where('assigned_to', '=', $consumable->assigned_to)->where('consumable_id', '=', $consumable->id)->first();
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'user_id' => $admin_user->id,
'assigned_to' => e(Input::get('assigned_to'))
]);
$logaction = $consumable->logCheckout(e(Input::get('note')), $user);
$data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name;
@@ -362,168 +270,9 @@ class ConsumablesController extends Controller
$data['require_acceptance'] = $consumable->requireAcceptance();
if (($consumable->requireAcceptance()=='1') || ($consumable->getEula())) {
Mail::send('emails.accept-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_consumable_delivery'));
});
}
// Redirect to the new consumable page
return redirect()->to("admin/consumables")->with('success', trans('admin/consumables/message.checkout.success'));
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
}
/**
* Returns the JSON response containing the the consumables data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ConsumablesController::getIndex() method that returns the view that consumes the JSON.
* @since [v1.0]
* @param int $consumableId
* @return View
*/
public function getDatatable()
{
$consumables = Company::scopeCompanyables(
Consumable::select('consumables.*')
->whereNull('consumables.deleted_at')
->with('company', 'location', 'category', 'users', 'manufacturer')
);
if (Input::has('search')) {
$consumables = $consumables->TextSearch(e(Input::get('search')));
}
if (Input::has('offset')) {
$offset = e(Input::get('offset'));
} else {
$offset = 0;
}
if (Input::has('limit')) {
$limit = e(Input::get('limit'));
} else {
$limit = 50;
}
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','companyName','category','model_number', 'item_no', 'manufacturer'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
switch ($sort) {
case 'category':
$consumables = $consumables->OrderCategory($order);
break;
case 'location':
$consumables = $consumables->OrderLocation($order);
break;
case 'manufacturer':
$consumables = $consumables->OrderManufacturer($order);
break;
case 'companyName':
$consumables = $consumables->OrderCompany($order);
break;
default:
$consumables = $consumables->orderBy($sort, $order);
break;
}
$consumCount = $consumables->count();
$consumables = $consumables->skip($offset)->take($limit)->get();
$rows = array();
foreach ($consumables as $consumable) {
$actions = '<nobr>';
if (Gate::allows('consumables.checkout')) {
$actions .= '<a href="' . route('checkout/consumable',
$consumable->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($consumable->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
}
if (Gate::allows('consumables.edit')) {
$actions .= '<a href="' . route('update/consumable',
$consumable->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
}
if (Gate::allows('consumables.delete')) {
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/consumable',
$consumable->id) . '" data-content="' . trans('admin/consumables/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($consumable->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
}
$actions .='</nobr>';
$company = $consumable->company;
$rows[] = array(
'id' => $consumable->id,
'name' => (string)link_to('admin/consumables/'.$consumable->id.'/view', e($consumable->name)),
'location' => ($consumable->location) ? e($consumable->location->name) : '',
'min_amt' => e($consumable->min_amt),
'qty' => e($consumable->qty),
'manufacturer' => ($consumable->manufacturer) ? (string) link_to('/admin/settings/manufacturers/'.$consumable->manufacturer_id.'/view', $consumable->manufacturer->name): '',
'model_number' => e($consumable->model_number),
'item_no' => e($consumable->item_no),
'category' => ($consumable->category) ? (string) link_to('/admin/settings/categories/'.$consumable->category_id.'/view', $consumable->category->name) : 'Missing category',
'order_number' => e($consumable->order_number),
'purchase_date' => e($consumable->purchase_date),
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
'numRemaining' => $consumable->numRemaining(),
'actions' => $actions,
'companyName' => is_null($company) ? '' : e($company->name),
);
}
$data = array('total' => $consumCount, 'rows' => $rows);
return $data;
}
/**
* Returns a JSON response containing details on the users associated with this consumable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ConsumablesController::getView() method that returns the form.
* @since [v1.0]
* @param int $consumableId
* @return View
*/
public function getDataView($consumableId)
{
//$consumable = Consumable::find($consumableID);
$consumable = Consumable::with(array('consumableAssigments'=>
function ($query) {
$query->orderBy('created_at', 'DESC');
},
'consumableAssigments.admin'=> function ($query) {
},
'consumableAssigments.user'=> function ($query) {
},
))->find($consumableId);
// $consumable->load('consumableAssigments.admin','consumableAssigments.user');
if (!Company::isCurrentUserHasAccess($consumable)) {
return ['total' => 0, 'rows' => []];
}
$rows = array();
foreach ($consumable->consumableAssigments as $consumable_assignment) {
$rows[] = array(
'name' => (string)link_to('/admin/users/'.$consumable_assignment->user->id.'/view', e($consumable_assignment->user->fullName())),
'created_at' => ($consumable_assignment->created_at->format('Y-m-d H:i:s')=='-0001-11-30 00:00:00') ? '' : $consumable_assignment->created_at->format('Y-m-d H:i:s'),
'admin' => ($consumable_assignment->admin) ? e($consumable_assignment->admin->fullName()) : '',
);
}
$consumableCount = $consumable->users->count();
$data = array('total' => $consumableCount, 'rows' => $rows);
return $data;
}
}

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\CustomFieldRequest;
use View;
use App\Models\CustomFieldset;
use App\Models\CustomField;
@@ -11,6 +12,7 @@ use App\Models\AssetModel;
use Lang;
use Auth;
use Illuminate\Http\Request;
use App\Helpers\Helper;
use Log;
/**
@@ -35,77 +37,16 @@ class CustomFieldsController extends Controller
*/
public function index()
{
//
$fieldsets=CustomFieldset::with("fields", "models")->get();
//$fieldsets=CustomFieldset::all();
$fields=CustomField::with("fieldset")->get();
//$fields=CustomField::all();
return View::make("custom_fields.index")->with("custom_fieldsets", $fieldsets)->with("custom_fields", $fields);
$this->authorize('view', CustomField::class);
$fieldsets = CustomFieldset::with("fields", "models")->get();
$fields = CustomField::with("fieldset")->get();
return view("custom_fields.index")->with("custom_fieldsets", $fieldsets)->with("custom_fields", $fields);
}
/**
* Returns a view with a form for creating a new custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return View
*/
public function create()
{
//
return View::make("custom_fields.create");
}
/**
* Validates and stores a new custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return Redirect
*/
public function store(Request $request)
{
//
$cfset = new CustomFieldset(
[
"name" => e($request->get("name")),
"user_id" => Auth::user()->id]
);
$validator=Validator::make(Input::all(), $cfset->rules);
if ($validator->passes()) {
$cfset->save();
return redirect()->route("admin.custom_fields.show", [$cfset->id])->with('success', trans('admin/custom_fields/message.fieldset.create.success'));
} else {
return redirect()->back()->withInput()->withErrors($validator);
}
}
/**
* Associate the custom field with a custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return View
*/
public function associate($id)
{
$set = CustomFieldset::find($id);
foreach ($set->fields as $field) {
if ($field->id == Input::get('field_id')) {
return redirect()->route("admin.custom_fields.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
}
}
$results=$set->fields()->attach(Input::get('field_id'), ["required" => (Input::get('required') == "on"),"order" => Input::get('order')]);
return redirect()->route("admin.custom_fields.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
}
/**
* Returns a view with a form to create a new custom field.
@@ -115,9 +56,11 @@ class CustomFieldsController extends Controller
* @since [v1.8]
* @return View
*/
public function createField()
public function create()
{
return View::make("custom_fields.create_field");
$this->authorize('create', CustomField::class);
return view("custom_fields.fields.edit")->with('field', new CustomField());
}
@@ -129,38 +72,33 @@ class CustomFieldsController extends Controller
* @since [v1.8]
* @return Redirect
*/
public function storeField(Request $request)
public function store(CustomFieldRequest $request)
{
$this->authorize('create', CustomField::class);
$field = new CustomField([
"name" => e($request->get("name")),
"element" => e($request->get("element")),
"field_values" => e($request->get("field_values")),
"field_encrypted" => e($request->get("field_encrypted", 0)),
"name" => $request->get("name"),
"element" => $request->get("element"),
"help_text" => $request->get("help_text"),
"field_values" => $request->get("field_values"),
"field_encrypted" => $request->get("field_encrypted", 0),
"show_in_email" => $request->get("show_in_email", 0),
"user_id" => Auth::user()->id
]);
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
if ($request->has("custom_format")) {
$field->format = e($request->get("custom_format"));
} else {
$field->format = e($request->get("format"));
}
$validator=Validator::make(Input::all(), $field->rules);
if ($validator->passes()) {
$results = $field->save();
if ($results) {
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
} else {
dd($field);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
}
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
} else {
return redirect()->back()->withInput()->withErrors($validator);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
}
}
@@ -175,8 +113,10 @@ class CustomFieldsController extends Controller
{
$field = CustomField::find($field_id);
$this->authorize('update', $field);
if ($field->fieldset()->detach($fieldset_id)) {
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
@@ -189,123 +129,76 @@ class CustomFieldsController extends Controller
* @since [v1.8]
* @return Redirect
*/
public function deleteField($field_id)
public function destroy($field_id)
{
$field = CustomField::find($field_id);
$this->authorize('delete', $field);
if ($field->fieldset->count()>0) {
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
} else {
$field->delete();
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
}
/**
* Validates and stores a new custom field.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return View
*/
public function show($id)
{
$cfset = CustomFieldset::with('fields')->where('id','=',$id)->orderBy('id','ASC')->first();
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::lists("name", "id")->toArray();
$maxid = 0;
foreach ($cfset->fields() as $field) {
if ($field->pivot->order > $maxid) {
$maxid=$field->pivot->order;
}
if (isset($custom_fields_list[$field->id])) {
unset($custom_fields_list[$field->id]);
}
}
return View::make("custom_fields.show")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
}
/**
* What the actual fuck, Brady?
* Return a view to edit a custom field
*
* @todo Uhh, build this?
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v1.8]
* @return Fuckall
* @since [v4.0]
* @return View
*/
public function edit($id)
{
//
$field = CustomField::find($id);
$this->authorize('update', $field);
return view("custom_fields.fields.edit")->with('field', $field);
}
/**
* GET IN THE SEA BRADY.
* Store the updated field
*
* @todo Uhh, build this too?
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return Fuckall
*/
public function update($id)
{
//
}
/**
* Validates a custom fieldset and then deletes if it has no models associated.
* @todo Allow encrypting/decrypting if encryption status changes
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v1.8]
* @return View
* @since [v4.0]
* @return Redirect
*/
public function destroy($id)
public function update(CustomFieldRequest $request, $id)
{
//
$fieldset = CustomFieldset::find($id);
$field = CustomField::find($id);
$models = AssetModel::where("fieldset_id", "=", $id);
if ($models->count() == 0) {
$fieldset->delete();
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
$this->authorize('update', $field);
$field->name = e($request->get("name"));
$field->element = e($request->get("element"));
$field->field_values = e($request->get("field_values"));
$field->user_id = Auth::user()->id;
$field->help_text = $request->get("help_text");
$field->show_in_email = $request->get("show_in_email", 0);
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
$field->format = e($request->get("custom_format"));
} else {
return redirect()->route("admin.custom_fields.index")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
$field->format = e($request->get("format"));
}
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
}
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
}
/**
* Reorder the custom fields within a fieldset
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v3.0]
* @return Array
*/
public function postReorder(Request $request, $id)
{
$fieldset = CustomFieldset::find($id);
$fields = array();
$order_array = array();
$items = $request->input('item');
foreach ($items as $order => $field_id) {
$order_array[$field_id] = $order;
}
foreach ($fieldset->fields as $field) {
$fields[$field->id] = ['required' => $field->pivot->required, 'order' => $order_array[$field->id]];
}
return $fieldset->fields()->sync($fields);
}
}

View File

@@ -0,0 +1,237 @@
<?php
namespace App\Http\Controllers;
use View;
use App\Models\CustomFieldset;
use App\Models\CustomField;
use Input;
use Validator;
use Redirect;
use App\Models\AssetModel;
use Lang;
use Auth;
use Illuminate\Http\Request;
use Log;
/**
* This controller handles all actions related to Custom Asset Fields for
* the Snipe-IT Asset Management application.
*
* @todo Improve documentation here.
* @todo Check for raw DB queries and try to convert them to query builder statements
* @version v2.0
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
*/
class CustomFieldsetsController extends Controller
{
/**
* Validates and stores a new custom field.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return View
*/
public function show($id)
{
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
$this->authorize('view', $cfset);
if ($cfset) {
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
$maxid = 0;
foreach ($cfset->fields as $field) {
if ($field->pivot->order > $maxid) {
$maxid=$field->pivot->order;
}
if (isset($custom_fields_list[$field->id])) {
unset($custom_fields_list[$field->id]);
}
}
return view("custom_fields.fieldsets.view")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
}
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
}
/**
* Returns a view with a form for creating a new custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return View
*/
public function create()
{
$this->authorize('create', CustomFieldset::class);
return view("custom_fields.fieldsets.edit");
}
/**
* Validates and stores a new custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return Redirect
*/
public function store(Request $request)
{
$this->authorize('create', CustomFieldset::class);
$cfset = new CustomFieldset(
[
"name" => e($request->get("name")),
"user_id" => Auth::user()->id]
);
$validator = Validator::make(Input::all(), $cfset->rules);
if ($validator->passes()) {
$cfset->save();
return redirect()->route("fieldsets.show", [$cfset->id])->with('success', trans('admin/custom_fields/message.fieldset.create.success'));
} else {
return redirect()->back()->withInput()->withErrors($validator);
}
}
/**
* What the actual fuck, Brady?
*
* @todo Uhh, build this?
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return Fuckall
*/
public function edit($id)
{
//
}
/**
* GET IN THE SEA BRADY.
*
* @todo Uhh, build this too?
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return Fuckall
*/
public function update($id)
{
//
}
/**
* Validates a custom fieldset and then deletes if it has no models associated.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @param int $id
* @since [v1.8]
* @return View
*/
public function destroy($id)
{
$fieldset = CustomFieldset::find($id);
$this->authorize('delete', $fieldset);
if ($fieldset) {
$models = AssetModel::where("fieldset_id", "=", $id);
if ($models->count() == 0) {
$fieldset->delete();
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
} else {
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
}
}
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
}
/**
* Associate the custom field with a custom fieldset.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return View
*/
public function associate(Request $request, $id)
{
$set = CustomFieldset::find($id);
$this->authorize('update', $set);
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.');
}
/**
* 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

@@ -30,53 +30,20 @@ class DashboardController extends Controller
// Show the page
if (Auth::user()->hasAccess('admin')) {
$asset_stats['total'] = Asset::Hardware()->count();
$asset_stats=null;
$asset_stats['rtd']['total'] = Asset::Hardware()->RTD()->count();
$counts['asset'] = \App\Models\Asset::count();
$counts['accessory'] = \App\Models\Accessory::count();
$counts['license'] = \App\Models\License::assetcount();
$counts['consumable'] = \App\Models\Consumable::count();
$counts['grand_total'] = $counts['asset'] + $counts['accessory'] + $counts['license'] + $counts['consumable'];
if ($asset_stats['rtd']['total'] > 0) {
$asset_stats['rtd']['percent'] = round(($asset_stats['rtd']['total']/$asset_stats['total']) * 100);
} else {
$asset_stats['rtd']['percent'] = 0;
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
\Artisan::call('migrate', ['--force' => true]);
\Artisan::call('passport:install');
}
$asset_stats['pending']['total'] = Asset::Hardware()->Pending()->count();
if ($asset_stats['pending']['total'] > 0) {
$asset_stats['pending']['percent'] = round(($asset_stats['pending']['total']/$asset_stats['total']) * 100);
} else {
$asset_stats['pending']['percent'] = 0;
}
$asset_stats['deployed']['total'] = Asset::Hardware()->Deployed()->count();
if ($asset_stats['deployed']['total'] > 0) {
$asset_stats['deployed']['percent'] = round(($asset_stats['deployed']['total']/$asset_stats['total']) * 100);
} else {
$asset_stats['deployed']['percent'] = 0;
}
$asset_stats['undeployable']['total'] = Asset::Hardware()->Undeployable()->count();
if ($asset_stats['undeployable']['total'] > 0) {
$asset_stats['undeployable']['percent'] = round(($asset_stats['undeployable']['total']/$asset_stats['total']) * 100);
} else {
$asset_stats['undeployable']['percent'] = 0;
}
$asset_stats['archived']['total'] = Asset::Hardware()->Archived()->count();
if ($asset_stats['archived']['total'] > 0) {
$asset_stats['archived']['percent'] = round(($asset_stats['archived']['total']/$asset_stats['total']) * 100);
} else {
$asset_stats['archived']['percent'] = 0;
}
return View::make('dashboard')->with('asset_stats', $asset_stats);
return view('dashboard')->with('asset_stats', $asset_stats)->with('counts', $counts);
} else {
// Redirect to the profile page
return redirect()->intended('account/view-assets');

View File

@@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Department;
use App\Helpers\Helper;
use Auth;
use Image;
use App\Http\Requests\ImageUploadRequest;
class DepartmentsController extends Controller
{
public function __construct()
{
$this->middleware('auth');
parent::__construct();
}
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the assets listing, which is generated in getDatatable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see AssetController::getDatatable() method that generates the JSON response
* @since [v4.0]
* @return View
*/
public function index(Request $request)
{
$this->authorize('index', Department::class);
$company = null;
if ($request->filled('company_id')) {
$company = Company::find($request->input('company_id'));
}
return view('departments/index')->with('company', $company);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Department::class);
$department = new Department;
$department->fill($request->all());
$department->user_id = Auth::user()->id;
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
$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'));
}
return redirect()->back()->withInput()->withErrors($department->getErrors());
}
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the department detail page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v4.0]
* @return \Illuminate\Contracts\View\View
*/
public function show($id)
{
$department = Department::find($id);
$this->authorize('view', $department);
if (isset($department->id)) {
return view('departments/view', compact('department'));
}
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
/**
* Returns a form view used to create a new department.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see DepartmentsController::postCreate() method that validates and stores the data
* @since [v4.0]
* @return \Illuminate\Contracts\View\View
*/
public function create()
{
$this->authorize('create', Department::class);
return view('departments/edit')->with('item', new Department);
}
/**
* Validates and deletes selected department.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $locationId
* @since [v4.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($id)
{
if (is_null($department = Department::find($id))) {
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.not_found'));
}
$this->authorize('delete', $department);
if ($department->users->count() > 0) {
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.assoc_users'));
}
$department->delete();
return redirect()->back()->with('success', trans('admin/departments/message.delete.success'));
}
/**
* Makes a form view to edit location information.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see LocationsController::postCreate() method that validates and stores
* @param int $locationId
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function edit($id = null)
{
if (is_null($item = Department::find($id))) {
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('departments/edit', compact('item'));
}
public function update(ImageUploadRequest $request, $id) {
if (is_null($department = Department::find($id))) {
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
$this->authorize('update', $department);
$department->fill($request->all());
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
$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'));
}
return redirect()->back()->withInput()->withErrors($department->getErrors());
}
}

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