Compare commits

...

176 Commits

Author SHA1 Message Date
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 29f1cf2b48 Increase depreciation max 2017-07-19 19:41:21 -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 1052be670d Increase size of state field 2017-06-15 21:04:10 -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
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
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
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 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
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 08ef78356d Update Crowdin configuration file 2017-04-06 20:59:25 -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
snipe 265a896211 Bumped version 2017-02-22 22:38:33 -08:00
snipe f7e4fca70d Add model_number to accessory search 2017-02-22 22:34:56 -08:00
snipe bb4c443cd9 Added notes to custom report 2017-02-03 02:20:56 -08:00
snipe 7b1d2ee050 Fixed typo 2017-01-17 11:57:24 -08:00
snipe b3f70a046f Don’t require superadmin for checkin and delete user 2017-01-17 09:19:14 -08:00
morph027 734e87f85f Installer fine tuning (#2993)
* used shellcheck to lint snipeit.sh

* do not depend on lsb-release command

* add lsb codename

* really use perms()

* more fixes

* quiet apt

* silent logging using log()

* start mysql if not running

* added curl to ubuntu

* added logfile to log function

* update apt index after adding mariadb repo

* fixed typo

* review fixes
2017-01-10 23:07:06 -08:00
snipe f371c5fd62 Merge branch 'master' of github.com:snipe/snipe-it 2017-01-10 19:02:18 -08:00
snipe 4b3edbd2f5 Fixed #3130 - model ID not being saved weith model info on modal for create asset 2017-01-10 19:02:13 -08:00
Byron Wolfman 1f3106b9da Use debian base container and clean up apt-get (#3011)
* Use debian base container and clean up apt-get

Attempt to slim down the docker image by replacing ubuntu:trusty with debian:jessie and clean up after apt-get invocation. Building against the 3.6.0 commit shows a healthy reduction of 44MB, or 9%, compared to the ubuntu-based image.

* Use debian:jessie-slim for an even smaller image

If we're using a debian base image for sizing reasons, we may as well go the full distance and use debian-slim.
2017-01-10 16:22:06 -08:00
snipe 9fd3a9a82d Removed logging 2017-01-06 03:12:49 -08:00
snipe a6e6991a2d Removed second number_format 2017-01-06 03:11:27 -08:00
snipe 93ba0717d8 Removed number_format 2017-01-06 03:02:38 -08:00
snipe a8839e0ef4 Fixes #3104, #2914 and #2918 - auto-increment incorrectly generating next ID 2017-01-06 02:52:10 -08:00
snipe 3e4152c966 Bumped version 2017-01-06 00:04:26 -08:00
snipe 1bf34d73f5 Use correct authorize rule in middleware for user delete 2017-01-06 00:02:46 -08:00
snipe d1e360d64d Fix logic to use gate instead of asking if superuser 2017-01-06 00:02:19 -08:00
snipe 3b5b19848c Added oauth keys to gitignore 2017-01-06 00:01:55 -08:00
snipe 93ba90e837 Merge remote-tracking branch 'origin/develop' 2016-12-12 19:24:00 -08:00
snipe fc70d79a17 Merge remote-tracking branch 'origin/develop' 2016-12-12 19:07:07 -08:00
snipe 65016a2383 Merge remote-tracking branch 'origin/develop' 2016-12-05 16:12:41 -08:00
snipe dbcb2ccb46 Merge remote-tracking branch 'origin/develop' 2016-12-05 15:09:49 -08:00
snipe 2afcfcc87c Set DB_SSL to false for config 2016-12-03 17:32:44 -08:00
snipe 1afef9416a Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2016-12-03 17:15:02 -08:00
snipe 4049143ebf Bumped version 2016-12-01 06:11:03 -08:00
snipe 4df53bdf8d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
#	resources/views/account/view-assets.blade.php
2016-12-01 06:10:50 -08:00
snipe d49a1ea304 More graceful error message if log doesn’t exist 2016-12-01 06:05:14 -08:00
snipe 521f4facd5 Bumped version 2016-12-01 05:56:24 -08:00
snipe b4653dfc15 Ignore npm log 2016-12-01 05:55:29 -08:00
697 changed files with 12114 additions and 5024 deletions
+18
View File
@@ -719,6 +719,24 @@
"contributions": [
"code"
]
},
{
"login": "zwerch",
"name": "Robin Temme",
"avatar_url": "https://avatars2.githubusercontent.com/u/2809241?v=4",
"profile": "https://github.com/zwerch",
"contributions": [
"code"
]
},
{
"login": "imanghafoori1",
"name": "Iman",
"avatar_url": "https://avatars0.githubusercontent.com/u/6961695?v=4",
"profile": "https://github.com/imanghafoori1",
"contributions": [
"code"
]
}
]
}
+1 -1
View File
@@ -15,7 +15,7 @@ FILESYSTEM_DISK=local
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=snipeit_unit
DB_USERNAME=travis
DB_USERNAME=root
DB_PASSWORD=null
# --------------------------------------------
+7 -1
View File
@@ -6,6 +6,9 @@ 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
@@ -15,7 +18,10 @@ php:
before_script:
- phantomjs --webdriver=4444 &
- sleep 4
- mysql -e "create database IF NOT EXISTS snipeit_unit;" -utravis
- 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
- chmod -R 777 storage
+2
View File
@@ -13,6 +13,7 @@ php7.0-gd \
php7.0-xml \
php7.0-mbstring \
php7.0-zip \
php7.0-bcmath \
patch \
curl \
vim \
@@ -23,6 +24,7 @@ mysql-client \
RUN phpenmod mcrypt
RUN phpenmod gd
RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.0/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.0/cli/php.ini
+3 -2
View File
@@ -1,5 +1,5 @@
[![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&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&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-77-orange.svg?style=flat-square)](#contributors)
[![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&title=Ready+for+development)](http://waffle.io/snipe/snipe-it) [![Maintenance](https://img.shields.io/maintenance/yes/2017.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&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-79-orange.svg?style=flat-square)](#contributors)
## Snipe-IT - Open Source Asset Management System
@@ -67,6 +67,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<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") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
+164
View File
@@ -0,0 +1,164 @@
<?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';
/**
* 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);
$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_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.");
if ($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');
}
$mcrypter = new McryptEncrypter($legacy_key);
$settings = Setting::getSettings();
if ($settings->ldap_password=='') {
$this->comment('INFO: No LDAP password found. Skipping... ');
}
$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
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($custom_fields as $encrypted_field) {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($encrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
$bar->advance();
}
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
// Make sure the value isn't null
if ($asset->{$encrypted_field}!='') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$encrypted_field});
$asset->{$encrypted_field} = \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);
}
}
}
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use Illuminate\Console\Command;
use App\Notifications\ExpectedCheckinNotification;
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 fire()
{
$whenNotify = Carbon::now()->addDays(7);
$assets = Asset::with('assignedTo')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
$this->info($whenNotify.' is deadline');
$this->info($assets->count().' assets');
foreach ($assets as $asset) {
if ($asset->assignedTo && $asset->checkoutOutToUser()) {
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
//$this->info($asset);
}
}
}
}
+4 -1
View File
@@ -17,13 +17,15 @@ class Kernel extends ConsoleKernel
Commands\CreateAdmin::class,
Commands\SendExpirationAlerts::class,
Commands\SendInventoryAlerts::class,
Commands\SendExpectedCheckinAlerts::class,
Commands\ObjectImportCommand::class,
Commands\Versioning::class,
Commands\SystemBackup::class,
Commands\DisableLDAP::class,
Commands\Purge::class,
Commands\LdapSync::class,
Commands\FixDoubleEscape::class
Commands\FixDoubleEscape::class,
Commands\RecryptFromMcrypt::class
];
/**
@@ -37,6 +39,7 @@ class Kernel extends ConsoleKernel
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkins')->daily();
$schedule->command('snipeit:backup')->weekly();
$schedule->command('backup:clean')->daily();
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Exceptions;
use Exception;
class CheckoutNotAllowed extends Exception
{
public function __toString()
{
"A checkout is not allowed under these circumstances";
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ class Handler extends ExceptionHandler
}
}
// Try to parse 500 Errors ina bit nicer way when debug is enabled.
// Try to parse 500 Errors in a bit nicer way when debug is enabled.
if (config('app.debug')) {
return response()->json(Helper::formatStandardApiResponse('error', null, "An Error has occured! " . $e->getMessage()), 500);
}
+2 -3
View File
@@ -683,12 +683,11 @@ class Helper
public static function formatStandardApiResponse($status, $payload = null, $messages = null) {
$array['status'] = $status;
($payload) ? $array['payload'] = $payload : '';
$array['messages'] = $messages;
if (($messages) && (count($messages) > 0)) {
$array['messages'] = $messages;
}
($payload) ? $array['payload'] = $payload : $array['payload'] = null;
return $array;
}
@@ -260,7 +260,7 @@ class AccessoriesController extends Controller
'assigned_to' => $request->get('assigned_to')
]);
$logaction = $accessory->logCheckout(e(Input::get('note')));
$logaction = $accessory->logCheckout(e(Input::get('note')), $user);
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
@@ -0,0 +1,227 @@
<?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')));
}
$offset = request('offset', 0);
$limit = request('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($request->input('sort')) : 'created_at';
switch ($sort) {
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
default:
$maintenances = $maintenances->orderBy($sort, $order);
break;
}
$maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $maintenances->count());
}
/**
* 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);
}
}
@@ -76,7 +76,7 @@ class AssetModelsController extends Controller
$assetmodel->fill($request->all());
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/assetmodels/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
+53 -5
View File
@@ -31,6 +31,7 @@ use TCPDF;
use Validator;
use View;
/**
* This class controls all actions related to assets for
* the Snipe-IT Asset Management application.
@@ -83,9 +84,8 @@ class AssetsController extends Controller
}
$assets = Company::scopeCompanyables(Asset::select('assets.*'))->with(
'assetLoc', 'assetstatus', 'defaultLoc', 'assetlog', 'company',
'model.category', 'model.manufacturer', 'model.fieldset', 'assigneduser','supplier');
'assetloc', 'assetstatus', 'defaultLoc', 'assetlog', 'company',
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// If we should search on everything
if (($request->has('search')) && (count($filter) == 0)) {
$assets->TextSearch($request->input('search'));
@@ -96,7 +96,6 @@ class AssetsController extends Controller
}
}
// These are used by the API to query against specific ID numbers
if ($request->has('status_id')) {
$assets->where('status_id', '=', $request->input('status_id'));
@@ -231,7 +230,8 @@ class AssetsController extends Controller
*/
public function store(AssetRequest $request)
{
// $this->authorize('create', Asset::class);
$this->authorize('create', Asset::class);
$asset = new Asset();
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
@@ -279,6 +279,7 @@ class AssetsController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
@@ -493,5 +494,52 @@ class AssetsController extends Controller
}
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) {
$asset->next_audit_date = $request->input('next_audit_date');
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'));
}
}
@@ -22,7 +22,7 @@ class CategoriesController extends Controller
$this->authorize('view', Category::class);
$allowed_columns = ['id', 'name','category_type','use_default_eula','require_acceptance','checkin_email'];
$categories = Category::select(['id', 'name','category_type','use_default_eula','require_acceptance','checkin_email'])
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','require_acceptance','checkin_email'])
->withCount('assets', 'accessories', 'consumables', 'components');
if ($request->has('search')) {
@@ -75,7 +75,8 @@ class CategoriesController extends Controller
{
$this->authorize('view', Category::class);
$category = Category::findOrFail($id);
return $category;
return (new CategoriesTransformer)->transformCategory($category);
}
@@ -148,4 +148,47 @@ class ConsumablesController extends Controller
$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::find($consumableID);
$consumable = Consumable::with(array('consumableAssignments'=>
function ($query) {
$query->orderBy('created_at', 'DESC');
},
'consumableAssignments.admin'=> function ($query) {
},
'consumableAssignments.user'=> function ($query) {
},
))->find($consumableId);
// $consumable->load('consumableAssignments.admin','consumableAssignments.user');
if (!Company::isCurrentUserHasAccess($consumable)) {
return ['total' => 0, 'rows' => []];
}
$this->authorize('view', Component::class);
$rows = array();
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'name' => $consumable_assignment->user->present()->nameUrl(),
'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) ? $consumable_assignment->admin->present()->nameUrl() : '',
];
}
$consumableCount = $consumable->users->count();
$data = array('total' => $consumableCount, 'rows' => $rows);
return $data;
}
}
@@ -2,9 +2,11 @@
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Transformers\CustomFieldsTransformer;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Http\Request;
class CustomFieldsController extends Controller
{
@@ -16,6 +18,15 @@ class CustomFieldsController extends Controller
* @since [v3.0]
* @return Array
*/
public function index()
{
$this->authorize('index', CustomFields::class);
$fields = CustomField::get();
$total = count($fields);
return (new CustomFieldsTransformer)->transformCustomFields($fields, $total);
}
public function postReorder(Request $request, $id)
{
$fieldset = CustomFieldset::find($id);
@@ -99,16 +99,14 @@ class DepartmentsController extends Controller
*/
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'));
}
$department = Department::findOrFail($id);
if ($department->users->count() > 0) {
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.assoc_users'));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/departments/message.assoc_users')));
}
$department->delete();
return redirect()->to(route('departments.index'))->with('success', trans('admin/departments/message.delete.success'));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/departments/message.delete.success')));
}
@@ -21,7 +21,7 @@ class LicensesController extends Controller
public function index(Request $request)
{
$this->authorize('view', License::class);
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer'));
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer', 'supplier'));
if ($request->has('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
@@ -59,6 +59,10 @@ class LicensesController extends Controller
$licenses->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
if ($request->has('supplier_id')) {
$licenses->where('supplier_id','=',$request->input('supplier_id'));
}
if ($request->has('depreciation_id')) {
$licenses->where('depreciation_id','=',$request->input('depreciation_id'));
}
@@ -69,22 +73,26 @@ class LicensesController extends Controller
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','manufacturer','company','license_name','license_email'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
switch ($sort) {
switch ($request->input('sort')) {
case 'manufacturer':
$licenses = $licenses->OrderManufacturer($order);
break;
case 'supplier':
$licenses = $licenses->OrderSupplier($order);
break;
case 'company':
$licenses = $licenses->OrderCompany($order);
break;
default:
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','license_name','license_email'];
$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();
@@ -21,7 +21,7 @@ class LocationsController extends Controller
{
$this->authorize('view', Location::class);
$allowed_columns = ['id','name','address','address2','city','state','country','zip','created_at',
'updated_at','parent_id'];
'updated_at','parent_id', 'manager_id'];
$locations = Location::select([
'locations.id',
@@ -33,6 +33,7 @@ class LocationsController extends Controller
'locations.zip',
'locations.country',
'locations.parent_id',
'locations.manager_id',
'locations.created_at',
'locations.updated_at',
'locations.currency'
@@ -19,7 +19,7 @@ class ReportsController extends Controller
public function index(Request $request)
{
$actionlogs = Actionlog::with('item', 'user', 'target');
$actionlogs = Actionlog::with('item', 'user', 'target','location');
if ($request->has('search')) {
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
@@ -36,6 +36,10 @@ class ReportsController extends Controller
->where('item_type','=',"App\\Models\\".ucwords($request->input('item_type')));
}
if ($request->has('action_type')) {
$actionlogs = $actionlogs->where('action_type','=',$request->input('action_type'))->orderBy('created_at', 'desc');
}
$allowed_columns = [
'id',
'created_at'
@@ -53,9 +53,20 @@ class StatuslabelsController extends Controller
public function store(Request $request)
{
$this->authorize('create', Statuslabel::class);
$request->except('deployable', 'pending','archived');
if (!$request->has('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]));
}
$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')));
}
@@ -75,7 +86,7 @@ class StatuslabelsController extends Controller
{
$this->authorize('view', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
return $statuslabel;
return (new StatuslabelsTransformer)->transformStatuslabel($statuslabel);
}
@@ -92,8 +103,20 @@ class StatuslabelsController extends Controller
{
$this->authorize('edit', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$request->except('deployable', 'pending','archived');
if (!$request->has('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')));
}
+25 -2
View File
@@ -8,6 +8,8 @@ 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;
class UsersController extends Controller
{
@@ -50,6 +52,12 @@ class UsersController extends Controller
$users = $users->TextSearch($request->input('search'));
}
if (($request->has('deleted')) && ($request->input('deleted')=='true')) {
$users = $users->GetDeleted();
}
if ($request->has('company_id')) {
$users = $users->where('company_id', '=', $request->input('company_id'));
}
@@ -102,7 +110,7 @@ class UsersController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(SaveUserRequest $request)
{
$this->authorize('view', User::class);
$user = new User;
@@ -139,7 +147,7 @@ class UsersController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
public function update(SaveUserRequest $request, $id)
{
$this->authorize('edit', User::class);
$user = User::findOrFail($id);
@@ -181,4 +189,19 @@ class UsersController extends Controller
}
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);
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
return response()->json($assets);
}
}
@@ -414,7 +414,7 @@ class AssetModelsController extends Controller
$manufacturer_list = $nochange + Helper::manufacturerList();
return view('models/bulk-edit', compact('models'))
return view('models/bulk-edit', compact('models'))
->with('manufacturer_list', $manufacturer_list)
->with('category_list', $category_list)
->with('fieldset_list', $fieldset_list)
+68 -13
View File
@@ -116,9 +116,9 @@ class AssetsController extends Controller
->with('statuslabel_list', Helper::statusLabelList())
->with('location_list', Helper::locationsList())
->with('item', new Asset)
->with('manufacturer', Helper::manufacturerList())
->with('category', Helper::categoryList('asset'))
->with('statuslabel_types', Helper::statusTypeList())
->with('manufacturer', Helper::manufacturerList()) //handled in modal now?
->with('category', Helper::categoryList('asset')) //handled in modal now?
->with('statuslabel_types', Helper::statusTypeList()) //handled in modal now?
->with('users_list', Helper::usersList())
->with('assets_list', Helper::assetsList())
->with('locations_list', Helper::locationsList());
@@ -380,7 +380,7 @@ class AssetsController extends Controller
if ($asset->save()) {
// Redirect to the new asset page
\Session::flash('success', trans('admin/hardware/message.update.success'));
return response()->json(['redirect_url' => route("hardware.show", $assetId)]);
return response()->json(['redirect_url' => route("view/hardware", $assetId)]);
}
\Input::flash();
\Session::flash('errors', $asset->getErrors());
@@ -412,6 +412,13 @@ class AssetsController extends Controller
$asset->delete();
$logaction = new Actionlog();
$logaction->item_type = Asset::class;
$logaction->item_id = $asset->id;
$logaction->created_at = date("Y-m-d H:i:s");
$logaction->user_id = Auth::user()->id;
$log = $logaction->logaction('deleted');
// Redirect to the asset management page
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
}
@@ -537,17 +544,18 @@ class AssetsController extends Controller
$this->authorize('checkin', $asset);
$admin = Auth::user();
$user = $asset->assignedUser;
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'));
}
// This is just used for the redirect
$return_to = $asset->assigned_to;
$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 = e(Input::get('name'));
@@ -566,7 +574,7 @@ class AssetsController extends Controller
$data['item_serial'] = $asset->serial;
$data['note'] = $logaction->note;
if ((($asset->checkin_email()=='1')) && (isset($user)) && (!config('app.lock_passwords'))) {
if ((($asset->checkin_email()=='1')) && (isset($user)) && (!empty($user->email)) && (!config('app.lock_passwords'))) {
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'));
@@ -575,7 +583,7 @@ class AssetsController extends Controller
}
if ($backto=='user') {
return redirect()->to("admin/users/".$return_to.'/view')->with('success', trans('admin/hardware/message.checkin.success'));
return redirect()->to("admin/users/".$user->id.'/view')->with('success', trans('admin/hardware/message.checkin.success'));
}
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkin.success'));
}
@@ -595,9 +603,12 @@ class AssetsController extends Controller
*/
public function show($assetId = null)
{
$asset = Asset::withTrashed()->find($assetId);
$settings = Setting::getSettings();
$this->authorize('view', $asset);
$settings = Setting::getSettings();
$audit_log = Actionlog::where('action_type','=','audit')->where('item_id','=',$assetId)->where('item_type','=',Asset::class)->orderBy('created_at','DESC')->first();
if (isset($asset)) {
@@ -617,7 +628,8 @@ class AssetsController extends Controller
'url' => route('qr_code/hardware', $asset->id)
);
return view('hardware/view', compact('asset', 'qr_code', 'settings'))->with('use_currency', $use_currency);
return view('hardware/view', compact('asset', 'qr_code', 'settings'))
->with('use_currency', $use_currency)->with('audit_log',$audit_log);
}
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist', compact('id')));
@@ -638,7 +650,7 @@ class AssetsController extends Controller
if ($settings->qr_code == '1') {
$asset = Asset::find($assetId);
$size = Helper::barcodeDimensions($settings->barcode_type);
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'.png';
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png';
if (isset($asset->id,$asset->asset_tag)) {
@@ -953,7 +965,7 @@ class AssetsController extends Controller
* @since [v1.0]
* @return View
*/
public function getDeleteFile($assetId = null, $fileId = null)
public function deleteFile($assetId = null, $fileId = null)
{
$asset = Asset::find($assetId);
$this->authorize('update', $asset);
@@ -1233,4 +1245,47 @@ class AssetsController extends Controller
// Redirect to the asset management page with error
return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
}
public function quickScan(Request $request)
{
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths(12)->toDateString();
return view('hardware/quickscan')->with('next_audit_date', $dt)->with('locations_list', Helper::locationsList());
}
public function audit(Request $request, $id)
{
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths(12)->toDateString();
$asset = Asset::findOrFail($id);
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list', Helper::locationsList());
}
public function auditStore(Request $request, $id)
{
$this->authorize('audit', Asset::class);
$rules = array(
'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::findOrFail($id);
$asset->next_audit_date = $request->input('next_audit_date');
if ($asset->save()) {
$asset->logAudit(request('note'),request('location_id'));
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.audit.success'));
}
}
}
@@ -283,7 +283,7 @@ class ComponentsController extends Controller
'asset_id' => $asset_id
]);
$component->logCheckout(e(Input::get('note')), $asset_id);
$component->logCheckout(e(Input::get('note')), $asset);
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
}
+1 -44
View File
@@ -250,7 +250,7 @@ class ConsumablesController extends Controller
'assigned_to' => e(Input::get('assigned_to'))
]);
$logaction = $consumable->logCheckout(e(Input::get('note')));
$logaction = $consumable->logCheckout(e(Input::get('note')), $user);
$data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name;
@@ -273,47 +273,4 @@ class ConsumablesController extends Controller
}
/**
* 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::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' => []];
}
$this->authorize('view', Component::class);
$rows = array();
foreach ($consumable->consumableAssigments as $consumable_assignment) {
$rows[] = [
'name' => $consumable_assignment->user->present()->nameUrl(),
'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) ? $consumable_assignment->admin->present()->nameUrl() : '',
];
}
$consumableCount = $consumable->users->count();
$data = array('total' => $consumableCount, 'rows' => $rows);
return $data;
}
}
+8 -5
View File
@@ -291,22 +291,22 @@ class LicensesController extends Controller
// Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($validator);
}
$target = null;
if ($assigned_to!='') {
// Check if the user exists
if (is_null($is_assigned_to = User::find($assigned_to))) {
if (is_null($target = User::find($assigned_to))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
}
}
if ($asset_id!='') {
if (is_null($asset = Asset::find($asset_id))) {
if (is_null($target = Asset::find($asset_id))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
}
if (($asset->assigned_to!='') && (($asset->assigned_to!=$assigned_to)) && ($assigned_to!='')) {
if (($target->assigned_to!='') && (($target->assigned_to!=$assigned_to)) && ($target!='')) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.owner_doesnt_match_asset'));
}
}
@@ -332,7 +332,7 @@ class LicensesController extends Controller
// Was the asset updated?
if ($licenseSeat->save()) {
$licenseSeat->logCheckout($request->input('note'));
$licenseSeat->logCheckout($request->input('note'), $target);
$data['license_id'] =$licenseSeat->license_id;
$data['note'] = $request->input('note');
@@ -440,7 +440,10 @@ class LicensesController extends Controller
*/
public function show($licenseId = null)
{
$license = License::find($licenseId);
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
if (isset($license->id)) {
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
$this->authorize('view', $license);
+8 -78
View File
@@ -63,7 +63,8 @@ class LocationsController extends Controller
return view('locations/edit')
->with('location_options', $location_options)
->with('item', new Location);
->with('item', new Location)
->with('manager_list', Helper::managerList());
}
@@ -88,6 +89,7 @@ class LocationsController extends Controller
$location->state = Input::get('state');
$location->country = Input::get('country');
$location->zip = Input::get('zip');
$location->manager_id = Input::get('manager_id');
$location->user_id = Auth::id();
if ($location->save()) {
@@ -154,7 +156,10 @@ class LocationsController extends Controller
$location_options = Location::flattenLocationsArray($location_options_array);
$location_options = array('' => 'Top Level') + $location_options;
return view('locations/edit', compact('item'))->with('location_options', $location_options);
return view('locations/edit', compact('item'))
->with('location_options', $location_options)
->with('manager_list', Helper::managerList());
}
@@ -185,6 +190,7 @@ class LocationsController extends Controller
$location->country = Input::get('country');
$location->zip = Input::get('zip');
$location->ldap_ou = Input::get('ldap_ou');
$location->manager_id = Input::get('manager_id');
// Was the location updated?
if ($location->save()) {
@@ -232,8 +238,6 @@ class LocationsController extends Controller
* the content for the locations detail page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see LocationsController::getDataViewUsers() method that returns JSON for location users
* @see LocationsController::getDataViewAssets() method that returns JSON for location assets
* @param int $locationId
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
@@ -252,78 +256,4 @@ class LocationsController extends Controller
return redirect()->route('locations.index')->with('error', $error);
}
/**
* Returns a JSON response that contains the users association with the
* selected location, to be used by the location detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see LocationsController::getView() method that creates the display view
* @param $locationID
* @return array
* @internal param int $locationId
* @since [v1.8]
*/
public function getDataViewUsers($locationID)
{
$location = Location::find($locationID);
$users = User::where('location_id', '=', $location->id);
if (Input::has('search')) {
$users = $users->TextSearch(e(Input::get('search')));
}
$users = $users->get();
$rows = array();
foreach ($users as $user) {
$rows[] = array(
'name' => (string)link_to_route('users.show', e($user->present()->fullName()), ['user'=>$user->id])
);
}
$data = array('total' => $users->count(), 'rows' => $rows);
return $data;
}
/**
* Returns a JSON response that contains the assets association with the
* selected location, to be used by the location detail view.
*
* @todo This is broken for accessories and consumables.
* @todo This is a very naive implementation. Should clean this up with query scopes.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see LocationsController::getView() method that creates the display view
* @param int $locationID
* @since [v1.8]
* @return array
*/
public function getDataViewAssets($locationID)
{
$location = Location::find($locationID)->load('assignedassets.model');
$assets = Asset::AssetsByLocation($location);
if (Input::has('search')) {
$assets = $assets->TextSearch(e(Input::get('search')));
}
$assets = $assets->get();
$rows = array();
foreach ($assets as $asset) {
$rows[] = [
'name' => (string)link_to_route('hardware.show', e($asset->present()->name()), ['hardware' => $asset->id]),
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
'model' => e($asset->model->name),
];
}
$data = array('total' => $assets->count(), 'rows' => $rows);
return $data;
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Helpers\Helper;
class ModalController extends Controller
{
function location() {
return view('modals.location');
}
function model() {
return view('modals.model')
->with('manufacturer', Helper::manufacturerList())
->with('category', Helper::categoryList('asset'));
}
function statuslabel() {
return view('modals.statuslabel')->with('statuslabel_types', Helper::statusTypeList());
}
function supplier() {
return view('modals.supplier');
}
function user() {
return view('modals.user');
}
}
+58 -1
View File
@@ -4,12 +4,13 @@ namespace App\Http\Controllers;
use Image;
use Input;
use Redirect;
use App\Models\Location;
use View;
use Auth;
use App\Helpers\Helper;
use App\Models\Setting;
use Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
/**
* This controller handles all actions related to User Profiles for
@@ -87,4 +88,60 @@ class ProfileController extends Controller
public function api() {
return view('account/api');
}
/**
* User change email page.
*
* @return View
*/
public function password()
{
$user = Auth::user();
return view('account/change-password', compact('user'));
}
/**
* Users change password form processing page.
*
* @return Redirect
*/
public function passwordSave(Request $request)
{
if (config('app.lock_passwords')) {
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/table.lock_passwords'));
}
$user = Auth::user();
if ($user->ldap_import=='1') {
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/message.error.password_ldap'));
}
$rules = array(
'current_password' => 'required',
'password' => Setting::passwordComplexityRulesSaving('store'),
'password_confirm' => 'required|same:password',
);
$validator = \Validator::make($request->all(), $rules);
$validator->after(function($validator) use ($request, $user) {
if (!Hash::check($request->input('current_password'), $user->password)) {
$validator->errors()->add('current_password', trans('validation.hashed_pass'));
}
});
if (!$validator->fails()) {
$user->password = Hash::make($request->input('password'));
$user->save();
return redirect()->route('account.password.index')->with('success', 'Password updated!');
}
return redirect()->back()->withInput()->withErrors($validator);
}
}
+23 -9
View File
@@ -186,7 +186,7 @@ class ReportsController extends Controller
{
// Grab all the assets
$assets = Asset::with('model', 'assignedTo', 'assetstatus', 'defaultLoc', 'assetlog', 'company')
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'assetloc', 'assetlog', 'company', 'model.category', 'model.depreciation')
->orderBy('created_at', 'DESC')->get();
return view('reports/depreciation', compact('assets'));
@@ -271,6 +271,20 @@ class ReportsController extends Controller
}
/**
* Displays audit report.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return View
*/
public function audit()
{
return view('reports/audit');
}
/**
* Displays activity report.
*
@@ -376,7 +390,7 @@ class ReportsController extends Controller
*/
public function postCustom()
{
$assets = Asset::orderBy('created_at', 'DESC')->with('company', 'assigneduser', 'assetloc', 'defaultLoc', 'assigneduser.userloc', 'model', 'supplier', 'assetstatus', 'model.manufacturer')->get();
$assets = Asset::orderBy('created_at', 'DESC')->with('company', 'assignedTo', 'assetloc', 'defaultLoc', 'model', 'supplier', 'assetstatus', 'model.manufacturer')->get();
$customfields = CustomField::get();
$rows = [ ];
@@ -449,7 +463,7 @@ class ReportsController extends Controller
}
if (e(Input::get('notes')) == '1') {
$header[] = 'Notes';
$header[] = trans('general.notes');
}
@@ -538,8 +552,8 @@ class ReportsController extends Controller
if (e(Input::get('username')) == '1') {
// Only works if we're checked out to a user, not anything else.
if ($asset->assigneduser) {
$row[] = '"' .e($asset->assigneduser->username). '"';
if ($asset->checkedOutToUser()) {
$row[] = '"' .e($asset->assignedTo->username). '"';
} else {
$row[] = ''; // Empty string if unassigned
}
@@ -547,8 +561,8 @@ class ReportsController extends Controller
if (e(Input::get('employee_num')) == '1') {
// Only works if we're checked out to a user, not anything else.
if ($asset->assigneduser) {
$row[] = '"' .e($asset->assigneduser->employee_num). '"';
if ($asset->checkedOutToUser()) {
$row[] = '"' .e($asset->assignedTo->employee_num). '"';
} else {
$row[] = ''; // Empty string if unassigned
}
@@ -590,9 +604,9 @@ class ReportsController extends Controller
if (e(Input::get('notes')) == '1') {
if ($asset->notes) {
$row[] = '"' .$asset->notes. '"';
$row[] = '"' .$asset->notes . '"';
} else {
$row[] = ''; // Empty string if unassigned
$row[] = '';
}
}
+21 -1
View File
@@ -20,6 +20,7 @@ use Auth;
use App\Models\User;
use App\Http\Requests\SetupUserRequest;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SettingsLdapRequest;
/**
* This controller handles all actions related to Settings for
@@ -184,6 +185,7 @@ class SettingsController extends Controller
$settings->site_name = e(Input::get('site_name'));
$settings->alert_email = e(Input::get('email'));
$settings->alerts_enabled = 1;
$settings->pwd_secure_min = 10;
$settings->brand = 1;
$settings->locale = 'en';
$settings->default_currency = 'USD';
@@ -259,6 +261,13 @@ class SettingsController extends Controller
Artisan::call('migrate', ['--force' => true]);
$output = Artisan::output();
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
Artisan::call('passport:install');
Artisan::call('migrate', ['--force' => true]);
}
return view('setup/migrate')
->with('output', $output)
->with('step', 2)
@@ -462,6 +471,15 @@ class SettingsController extends Controller
}
$setting->pwd_secure_uncommon = (int) $request->input('pwd_secure_uncommon');
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
$setting->pwd_secure_complexity = '';
if ($request->has('pwd_secure_complexity')) {
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
}
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -545,10 +563,12 @@ class SettingsController extends Controller
$alert_email = rtrim($request->input('alert_email'), ',');
$alert_email = trim($alert_email);
$setting->alert_email = e($alert_email);
$setting->alert_email = $alert_email;
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
$setting->alert_interval = $request->input('alert_interval');
$setting->alert_threshold = $request->input('alert_threshold');
$setting->audit_interval = $request->input('audit_interval');
$setting->audit_warning_days = $request->input('audit_warning_days');
if ($setting->save()) {
return redirect()->route('settings.index')
+21 -35
View File
@@ -12,9 +12,7 @@ use App\Models\Company;
use App\Models\Location;
use App\Models\License;
use App\Models\Setting;
use App\Models\Statuslabel;
use App\Http\Requests\SaveUserRequest;
use App\Http\Requests\UpdateUserRequest;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Models\User;
use App\Models\Ldap;
@@ -23,7 +21,6 @@ use Config;
use Crypt;
use DB;
use HTML;
use Illuminate\Support\Facades\Log;
use Input;
use Lang;
use League\Csv\Reader;
@@ -169,7 +166,7 @@ class UsersController extends Controller
* @since [v1.8]
* @return string JSON
*/
public function apiStore(Request $request)
public function apiStore(SaveUserRequest $request)
{
$this->authorize('create', User::class);
@@ -270,7 +267,7 @@ class UsersController extends Controller
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateUserRequest $request, $id = null)
public function update(SaveUserRequest $request, $id = null)
{
// We need to reverse the UI specific logic for our
// permissions here before we update the user.
@@ -309,14 +306,11 @@ class UsersController extends Controller
}
}
// Do we want to update the user password?
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
if ($request->has('username')) {
$user->username = e($request->input('username'));
$user->username = $request->input('username');
}
$user->email = e($request->input('email'));
$user->email = $request->input('email');
// Update the user
@@ -334,6 +328,12 @@ class UsersController extends Controller
$user->notes = $request->input('notes');
$user->department_id = $request->input('department_id', null);
// Do we want to update the user password?
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
// Strip out the superuser permission if the user isn't a superadmin
$permissions_array = $request->input('permission');
@@ -370,15 +370,15 @@ class UsersController extends Controller
// Authorize takes care of many of our logic checks now.
$this->authorize('delete', User::class);
if ($user->assets()->count() > 0) {
// Check if we are not trying to delete ourselves
if ($user->id === Auth::user()->id) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
}
if ($user->licenses()->count() > 0) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->licenses()->count() . ' licenses associated with them.');
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
}
if ($user->accessories()->count() > 0) {
@@ -386,6 +386,11 @@ class UsersController extends Controller
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->accessories()->count() . ' accessories associated with them.');
}
if ($user->managedLocations()->count() > 0) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->managedLocations()->count() . ' locations that they manage.');
}
// Delete the user
$user->delete();
@@ -528,10 +533,7 @@ class UsersController extends Controller
if (($key = array_search(Auth::user()->id, $user_raw_array)) !== false) {
unset($user_raw_array[$key]);
}
if (!Auth::user()->isSuperUser()) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.insufficient_permissions'));
}
if (!config('app.lock_passwords')) {
@@ -832,7 +834,6 @@ class UsersController extends Controller
'permissions' => '{"user":1}',
'notes' => 'Imported user'
);
//dd($newuser);
DB::table('users')->insert($newuser);
@@ -924,7 +925,6 @@ class UsersController extends Controller
$user = User::find($userId);
$destinationPath = config('app.private_uploads').'/users';
// the license is valid
if (isset($user->id)) {
$this->authorize('update', $user);
$log = Actionlog::find($fileId);
@@ -1147,21 +1147,7 @@ class UsersController extends Controller
}
return redirect()->route('ldap/user')->with('success', "LDAP Import successful.")->with('summary', $summary);
}
/**
* 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 getAssetList($userId)
{
$this->authorize('view', User::class);
$assets = Asset::where('assigned_to', '=', $userId)->with('model')->get();
return response()->json($assets);
}
/**
* Exports users to CSV
@@ -68,8 +68,8 @@ class ViewAssetsController extends Controller
public function getRequestableIndex()
{
$assets = Asset::with('model', 'defaultLoc', 'assetloc', 'assignedTo')->Hardware()->RequestableAssets()->get();
$models = AssetModel::with('category')->RequestableModels()->get();
$assets = Asset::with('model', 'defaultLoc', 'assetloc', 'assignedTo', 'requests')->Hardware()->RequestableAssets()->get();
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
return view('account/requestable-assets', compact('user', 'assets', 'models'));
}
+34 -7
View File
@@ -3,6 +3,7 @@
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Models\Setting;
class SaveUserRequest extends Request
{
@@ -23,12 +24,38 @@ class SaveUserRequest extends Request
*/
public function rules()
{
return [
'first_name' => 'required|string|min:1',
'email' => 'email',
'password' => 'required|min:6',
'password_confirm' => 'sometimes|required_with:password',
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
];
$rules = [];
switch($this->method())
{
// Brand new asset
case 'POST':
{
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('store');
}
// Save all fields
case 'PUT':
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
// Save only what's passed
case 'PATCH':
{
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
}
default:break;
}
$rules['password_confirm'] = 'sometimes|required_with:password';
return $rules;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Session;
class SettingsLdapRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
"ldap_server" => 'sometimes|required_if:ldap_enabled,1|url|nullable',
"ldap_uname" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_basedn" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_filter" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_username_field" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_fname_field" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_lname_field" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_auth_filter_query" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_version" => 'sometimes|required_if:ldap_enabled,1|nullable',
];
return $rules;
}
public function response(array $errors)
{
$this->session()->flash('errors', Session::get('errors', new \Illuminate\Support\ViewErrorBag)
->put('default', new \Illuminate\Support\MessageBag($errors)));
\Input::flash();
return parent::response($errors);
}
}
-32
View File
@@ -1,32 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class UpdateUserRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'first_name' => 'required|string|min:1',
'email' => 'email',
'password_confirm' => 'sometimes|required_with:password',
];
}
}
@@ -2,6 +2,7 @@
namespace App\Http\Transformers;
use App\Models\Actionlog;
use App\Models\Setting;
use Gate;
use Illuminate\Database\Eloquent\Collection;
use App\Helpers\Helper;
@@ -12,23 +13,32 @@ class ActionlogsTransformer
public function transformActionlogs (Collection $actionlogs, $total)
{
$array = array();
$settings = Setting::getSettings();
foreach ($actionlogs as $actionlog) {
$array[] = self::transformActionlog($actionlog);
$array[] = self::transformActionlog($actionlog, $settings);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformActionlog (Actionlog $actionlog)
public function transformActionlog (Actionlog $actionlog, $settings = null)
{
$array = [
'id' => (int) $actionlog->id,
'icon' => $actionlog->present()->icon(),
'image' => (method_exists($actionlog->item, 'getImageUrl')) ? $actionlog->item->getImageUrl() : null,
'item' => ($actionlog->item) ? [
'id' => (int) $actionlog->item->id,
'name' => e($actionlog->item->getDisplayNameAttribute()),
'type' => e($actionlog->itemType()),
] : null,
'location' => ($actionlog->location) ? [
'id' => (int) $actionlog->location->id,
'name' => e($actionlog->location->name)
] : null,
'created_at' => Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($actionlog->updated_at, 'datetime'),
'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(), 'date'): null,
'days_to_next_audit' => $actionlog->daysUntilNextAudit($settings->audit_interval, $actionlog->item),
'action_type' => $actionlog->present()->actionType(),
'admin' => ($actionlog->user) ? [
'id' => (int) $actionlog->user->id,
@@ -0,0 +1,54 @@
<?php
namespace App\Http\Transformers;
use App\Models\AssetMaintenance;
use Gate;
use Illuminate\Database\Eloquent\Collection;
use App\Helpers\Helper;
class AssetMaintenancesTransformer
{
public function transformAssetMaintenances (Collection $assetmaintenances, $total)
{
$array = array();
foreach ($assetmaintenances as $assetmaintenance) {
$array[] = self::transformAssetMaintenance($assetmaintenance);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformAssetMaintenance (AssetMaintenance $assetmaintenance)
{
$array = [
'id' => (int) $assetmaintenance->id,
'asset_name' => ($assetmaintenance->asset) ? ['id' => $assetmaintenance->asset->id,'name'=> e($assetmaintenance->asset->name)] : null,
'title' => ($assetmaintenance->title) ? e($assetmaintenance->title) : null,
'notes' => ($assetmaintenance->notes) ? e($assetmaintenance->notes) : null,
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id,'name'=> e($assetmaintenance->supplier->name)] : null,
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'datetime'),
'asset_maintenance_time' => $assetmaintenance->asset_maintenance_time,
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'datetime'),
'user_id' => ($assetmaintenance->admin) ? ['id' => $assetmaintenance->admin->id,'name'=> e($assetmaintenance->admin->getFullNameAttribute())] : null,
'created_at' => Helper::getFormattedDateObject($assetmaintenance->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->updated_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'update' => (bool) Gate::allows('update', Asset::class),
'delete' => (bool) Gate::allows('delete', Asset::class),
];
$array += $permissions_array;
return $array;
}
}
+23 -8
View File
@@ -62,14 +62,7 @@ class AssetsTransformer
'name'=> e($asset->defaultLoc->name)
] : null,
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
'assigned_to' => ($asset->assigneduser) ? [
'id' => (int) $asset->assigneduser->id,
'username' => e($asset->assigneduser->username),
'name' => e($asset->assigneduser->getFullNameAttribute()),
'first_name'=> e($asset->assigneduser->first_name),
'last_name'=> e($asset->assigneduser->last_name),
'employee_number' => e($asset->assigneduser->employee_num),
] : null,
'assigned_to' => $this->transformAssignedTo($asset),
'warranty' => ($asset->warranty_months > 0) ? e($asset->warranty_months . ' ' . trans('admin/hardware/form.months')) : null,
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
'created_at' => Helper::getFormattedDateObject($asset->created_at, 'datetime'),
@@ -110,6 +103,8 @@ class AssetsTransformer
//array += $fields_array;
$array['custom_fields'] = $fields_array;
}
} else {
$array['custom_fields'] = array();
}
$permissions_array['available_actions'] = [
@@ -128,4 +123,24 @@ class AssetsTransformer
{
return (new DatatablesTransformer)->transformDatatables($assets);
}
public function transformAssignedTo($asset)
{
if ($asset->checkedOutToUser()) {
return $asset->assignedTo ? [
'id' => (int) $asset->assignedTo->id,
'username' => e($asset->assignedTo->username),
'name' => e($asset->assignedTo->getFullNameAttribute()),
'first_name'=> e($asset->assignedTo->first_name),
'last_name'=> e($asset->assignedTo->last_name),
'employee_number' => e($asset->assignedTo->employee_num),
'type' => 'user'
] : null;
}
return $asset->assignedTo ? [
'id' => $asset->assignedTo->id,
'name' => $asset->assignedTo->display_name,
'type' => $asset->assignedType()
] : null;
}
}
@@ -4,6 +4,7 @@ namespace App\Http\Transformers;
use App\Models\Category;
use Illuminate\Database\Eloquent\Collection;
use Gate;
use App\Helpers\Helper;
class CategoriesTransformer
{
@@ -22,15 +23,18 @@ class CategoriesTransformer
if ($category) {
$array = [
'id' => e($category->id),
'id' => (int) $category->id,
'name' => e($category->name),
'type' => e($category->category_type),
'use_default_eula' => ($category->use_default_eula =='1') ? true : false,
'checkin_email' => ($category->checkin_email =='1') ? true : false,
'require_acceptance' => ($category->require_acceptance =='1') ? true : false,
'assets_count' => $category->assets_count,
'accessories_count' => $category->accessories_count,
'consumables_count' => $category->consumables_count,
'components_count' => $category->components_count,
'created_at' => Helper::getFormattedDateObject($category->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($category->updated_at, 'datetime'),
];
$permissions_array['available_actions'] = [
@@ -23,7 +23,7 @@ class CompaniesTransformer
if ($company) {
$array = [
'id' => e($company->id),
'id' => (int) $company->id,
'name' => e($company->name),
"created_at" => Helper::getFormattedDateObject($company->created_at, 'datetime'),
"updated_at" => Helper::getFormattedDateObject($company->updated_at, 'datetime'),
@@ -25,7 +25,7 @@ class CustomFieldsTransformer
'name' => e($field->name),
'db_column_name' => e($field->db_column_name()),
'format' => e($field->format),
'required' => $field->pivot->required,
'required' => $field->pivot ? $field->pivot->required : false,
'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'),
];
+14 -11
View File
@@ -9,7 +9,7 @@ use App\Helpers\Helper;
class LocationsTransformer
{
public function transformLocations (Collection $locations, $total)
public function transformLocations(Collection $locations, $total)
{
$array = array();
foreach ($locations as $location) {
@@ -18,18 +18,16 @@ class LocationsTransformer
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformLocation (Location $location = null)
public function transformLocation(Location $location = null)
{
if ($location) {
$assets_arr = [];
foreach($location->assets() as $asset) {
$assets_arr = ['id' => $asset->id];
}
$children_arr = [];
foreach($location->childLocations() as $child) {
$children_arr = ['id' => $child->id];
foreach($location->childLocations as $child) {
$children_arr[] = [
'id' => (int) $child->id,
'name' => $child->name
];
}
$array = [
@@ -41,10 +39,15 @@ class LocationsTransformer
'assets_checkedout' => $location->assets()->count(),
'assets_default' => $location->assignedassets()->count(),
'country' => e($location->country),
'assets' => $assets_arr,
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
'parent_id' => ($location->parent_id) ? (int) $location->parent_id : null,
'parent' => ($location->parent) ? [
'id' => (int) $location->parent->id,
'name'=> e($location->parent->name)
] : null,
'manager' => ($location->manager) ? (new UsersTransformer)->transformUser($location->manager) : null,
'children' => $children_arr,
];
@@ -30,6 +30,7 @@ class SuppliersTransformer
'city' => ($supplier->city) ? e($supplier->city) : null,
'state' => ($supplier->state) ? e($supplier->state) : null,
'country' => ($supplier->country) ? e($supplier->country) : null,
'zip' => ($supplier->zip) ? e($supplier->zip) : null,
'fax' => ($supplier->fax) ? e($supplier->fax) : null,
'phone' => ($supplier->phone) ? e($supplier->phone) : null,
'email' => ($supplier->email) ? e($supplier->email) : null,
@@ -37,6 +38,7 @@ class SuppliersTransformer
'assets_count' => (int) $supplier->assets_count,
'licenses_count' => (int) $supplier->licenses_count,
'image' => ($supplier->image) ? e($supplier->image) : null,
'notes' => ($supplier->notes) ? e($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
+22 -8
View File
@@ -32,11 +32,6 @@ class UsersTransformer
'id' => (int) $user->manager->id,
'name'=> e($user->manager->username)
] : null,
'groups' => ($user->groups) ? [
'id' => (int) $user->userloc->id,
'name'=> e($user->userloc->name)
] : null,
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'email' => e($user->email),
'department' => ($user->department) ? [
@@ -61,13 +56,32 @@ class UsersTransformer
];
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', User::class) ? true : false,
'delete' => Gate::allows('delete', User::class) ? true : false,
'clone' => Gate::allows('create', User::class) ? true : false,
'update' => (Gate::allows('update', User::class) && ($user->deleted_at=='')) ? true : false,
'delete' => (Gate::allows('delete', User::class) && ($user->deleted_at=='')) ? true : false,
'clone' => (Gate::allows('create', User::class) && ($user->deleted_at=='')) ,
'restore' => (Gate::allows('create', User::class) && ($user->deleted_at!='')) ? true : false,
];
$array += $permissions_array;
$numGroups = count($user->groups);
if($numGroups > 0)
{
$groups["total"] = $numGroups;
foreach($user->groups as $group)
{
$groups["rows"][] = [
'id' => (int) $group->id,
'name' => e($group->name)
];
}
$array["groups"] = $groups;
}
else {
$array["groups"] = null;
}
return $array;
}
+3 -1
View File
@@ -63,7 +63,9 @@ class AssetImporter extends ItemImporter
$this->item['image'] = $this->findCsvMatch($row, "image");
$this->item['warranty_months'] = intval($this->findCsvMatch($row, "warranty"));
$this->item['model_id'] = $this->createOrFetchAssetModel($row);
if (!$this->item['status_id'] && !$editingAsset) {
// If no status ID is found
if (!array_key_exists('status_id', $this->item) && !$editingAsset) {
$this->log("No status field found, defaulting to first status.");
$this->item['status_id'] = $this->defaultStatusLabelId;
}
+13 -8
View File
@@ -109,7 +109,15 @@ abstract class Importer
{
$headerRow = $this->csv->fetchOne();
$results = $this->normalizeInputArray($this->csv->fetchAssoc());
$this->customFields = CustomField::All(['name']);
// Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/
// This 'inverts' the fields such that we have a collection of fields indexed by name.
$cFs = CustomField::All();
$this->customFields = $cFs->reduce(function ($nameLookup, $field) {
$nameLookup[$field['name']] = $field;
return $nameLookup;
});
DB::transaction(function () use (&$results) {
Model::unguard();
$resultsCount = sizeof($results);
@@ -136,14 +144,12 @@ abstract class Importer
* @param $default string
* @return string
*/
public function findCsvMatch(array $array, $key, $default = '')
public function findCsvMatch(array $array, $key, $default = null)
{
$val = $default;
if ($customKey = $this->lookupCustomKey($key)) {
$key = $customKey;
}
$key = $this->lookupCustomKey($key);
$this->log("Custom Key: ${key}");
if (array_key_exists($key, $array)) {
@@ -163,13 +169,12 @@ abstract class Importer
*/
public function lookupCustomKey($key)
{
// dd($this->fieldMap);
if (array_key_exists($key, $this->fieldMap)) {
$this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
return $this->fieldMap[$key];
}
return null;
// Otherwise no custom key, return original.
return $key;
}
/**
+2 -1
View File
@@ -10,6 +10,7 @@ use App\Models\Location;
use App\Models\Manufacturer;
use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User;
class ItemImporter extends Importer
{
@@ -68,6 +69,7 @@ class ItemImporter extends Importer
if(get_class($this) !== UserImporter::class) {
if ($this->item["user"] = $this->createOrFetchUser($row)) {
$this->item['assigned_to'] = $this->item['user']->id;
$this->item['assigned_type'] = User::class;
}
}
}
@@ -90,7 +92,6 @@ class ItemImporter extends Importer
$item = collect($this->item);
// First Filter the item down to the model's fillable fields
$item = $item->only($model->getFillable());
// Then iterate through the item and, if we are updating, remove any blank values.
if ($updating) {
$item = $item->reject(function ($value) {
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace App\LegacyEncrypter;
use Illuminate\Contracts\Encryption\DecryptException;
abstract class BaseEncrypter
{
/**
* The encryption key.
*
* @var string
*/
protected $key;
/**
* Create a MAC for the given value.
*
* @param string $iv
* @param string $value
* @return string
*/
protected function hash($iv, $value)
{
return hash_hmac('sha256', $iv.$value, $this->key);
}
/**
* Get the JSON array from the given payload.
*
* @param string $payload
* @return array
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
protected function getJsonPayload($payload)
{
$payload = json_decode(base64_decode($payload), true);
// If the payload is not valid JSON or does not have the proper keys set we will
// assume it is invalid and bail out of the routine since we will not be able
// to decrypt the given value. We'll also check the MAC for this encryption.
if (! $payload || $this->invalidPayload($payload)) {
throw new DecryptException('The payload is invalid.');
}
if (! $this->validMac($payload)) {
throw new DecryptException('The MAC is invalid.');
}
return $payload;
}
/**
* Verify that the encryption payload is valid.
*
* @param array|mixed $data
* @return bool
*/
protected function invalidPayload($data)
{
return ! is_array($data) || ! isset($data['iv']) || ! isset($data['value']) || ! isset($data['mac']);
}
/**
* Determine if the MAC for the given payload is valid.
*
* @param array $payload
* @return bool
*
* @throws \RuntimeException
*/
protected function validMac(array $payload)
{
$bytes = random_bytes(16);
$calcMac = hash_hmac('sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true);
return hash_equals(hash_hmac('sha256', $payload['mac'], $bytes, true), $calcMac);
}
}
+214
View File
@@ -0,0 +1,214 @@
<?php
namespace App\LegacyEncrypter;
use Exception;
use RuntimeException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Encryption\EncryptException;
use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;
/**
* @deprecated since version 5.1. Use Illuminate\Encryption\Encrypter.
*/
class McryptEncrypter extends BaseEncrypter implements EncrypterContract
{
/**
* The algorithm used for encryption.
*
* @var string
*/
protected $cipher;
/**
* The block size of the cipher.
*
* @var int
*/
protected $block;
/**
* Create a new encrypter instance.
*
* @param string $key
* @param string $cipher
* @return void
*
* @throws \RuntimeException
*/
public function __construct($key, $cipher = MCRYPT_RIJNDAEL_128)
{
$key = (string) $key;
if (static::supported($key, $cipher)) {
$this->key = $key;
$this->cipher = $cipher;
$this->block = mcrypt_get_iv_size($this->cipher, MCRYPT_MODE_CBC);
} else {
throw new RuntimeException('The only supported ciphers are MCRYPT_RIJNDAEL_128 and MCRYPT_RIJNDAEL_256.');
}
}
/**
* Determine if the given key and cipher combination is valid.
*
* @param string $key
* @param string $cipher
* @return bool
*/
public static function supported($key, $cipher)
{
return defined('MCRYPT_RIJNDAEL_128') &&
($cipher === MCRYPT_RIJNDAEL_128 || $cipher === MCRYPT_RIJNDAEL_256);
}
/**
* Encrypt the given value.
*
* @param string $value
* @return string
*
* @throws \Illuminate\Contracts\Encryption\EncryptException
*/
public function encrypt($value, $serialize = true)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'));
if (! is_string($json)) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
/**
* Pad and use mcrypt on the given value and input vector.
*
* @param string $value
* @param string $iv
* @return string
*/
protected function padAndMcrypt($value, $iv)
{
$value = $this->addPadding(serialize($value));
return mcrypt_encrypt($this->cipher, $this->key, $value, MCRYPT_MODE_CBC, $iv);
}
/**
* Decrypt the given value.
*
* @param string $payload
* @return string
*/
public function decrypt($payload, $unserialize = true)
{
$payload = $this->getJsonPayload($payload);
// We'll go ahead and remove the PKCS7 padding from the encrypted value before
// we decrypt it. Once we have the de-padded value, we will grab the vector
// and decrypt the data, passing back the unserialized from of the value.
$value = base64_decode($payload['value']);
$iv = base64_decode($payload['iv']);
return unserialize($this->stripPadding($this->mcryptDecrypt($value, $iv)));
}
/**
* Run the mcrypt decryption routine for the value.
*
* @param string $value
* @param string $iv
* @return string
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
protected function mcryptDecrypt($value, $iv)
{
try {
return mcrypt_decrypt($this->cipher, $this->key, $value, MCRYPT_MODE_CBC, $iv);
} catch (Exception $e) {
throw new DecryptException($e->getMessage());
}
}
/**
* Add PKCS7 padding to a given value.
*
* @param string $value
* @return string
*/
protected function addPadding($value)
{
$pad = $this->block - (strlen($value) % $this->block);
return $value.str_repeat(chr($pad), $pad);
}
/**
* Remove the padding from the given value.
*
* @param string $value
* @return string
*/
protected function stripPadding($value)
{
$pad = ord($value[($len = strlen($value)) - 1]);
return $this->paddingIsValid($pad, $value) ? substr($value, 0, $len - $pad) : $value;
}
/**
* Determine if the given padding for a value is valid.
*
* @param string $pad
* @param string $value
* @return bool
*/
protected function paddingIsValid($pad, $value)
{
$beforePad = strlen($value) - $pad;
return substr($value, $beforePad) == str_repeat(substr($value, -1), $pad);
}
/**
* Get the IV size for the cipher.
*
* @return int
*/
protected function getIvSize()
{
return mcrypt_get_iv_size($this->cipher, MCRYPT_MODE_CBC);
}
/**
* Get the random data source available for the OS.
*
* @return int
*/
protected function getRandomizer()
{
if (defined('MCRYPT_DEV_URANDOM')) {
return MCRYPT_DEV_URANDOM;
}
if (defined('MCRYPT_DEV_RANDOM')) {
return MCRYPT_DEV_RANDOM;
}
mt_srand();
return MCRYPT_RAND;
}
}
+1
View File
@@ -60,6 +60,7 @@ class Accessory extends SnipeModel
'purchase_cost',
'purchase_date',
'model_number',
'manufacturer_id',
'qty',
'requestable'
];
+32
View File
@@ -5,6 +5,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
use Response;
use Carbon;
/**
* Model for the Actionlog (the table that keeps a historical log of
@@ -123,6 +124,10 @@ class Actionlog extends SnipeModel
return $this->belongsTo('\App\Models\ActionLog', 'thread_id');
}
public function location() {
return $this->belongsTo('\App\Models\Location', 'location_id' )->withTrashed();
}
/**
* Check if the file exists, and if it does, force a download
**/
@@ -149,6 +154,33 @@ class Actionlog extends SnipeModel
}
}
public function daysUntilNextAudit($monthInterval = 12, $asset = null) {
$now = Carbon::now();
$last_audit_date = $this->created_at;
$next_audit = $last_audit_date->addMonth($monthInterval);
$next_audit_days = $now->diffInDays($next_audit);
// Override the default setting for interval if the asset has its own next audit date
if (($asset) && ($asset->next_audit_date)) {
$override_default_next = \Carbon::parse($asset->next_audit_date);
$next_audit_days = $override_default_next->diffInDays($now);
}
return $next_audit_days;
}
public function calcNextAuditDate($monthInterval = 12, $asset = null) {
$last_audit_date = Carbon::parse($this->created_at);
// If there is an asset-specific next date already given,
if (($asset) && ($asset->next_audit_date)) {
return \Carbon::parse($asset->next_audit_date);
}
return \Carbon::parse($last_audit_date)->addMonths($monthInterval)->toDateString();
}
/**
* getListingOfActionLogsChronologicalOrder
*
+60 -57
View File
@@ -1,6 +1,7 @@
<?php
namespace App\Models;
use App\Exceptions\CheckoutNotAllowed;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Presenters\Presentable;
use AssetPresenter;
@@ -10,6 +11,7 @@ use Config;
use Illuminate\Database\Eloquent\SoftDeletes;
use Log;
use Watson\Validating\ValidatingTrait;
use Illuminate\Notifications\Notifiable;
/**
* Model for Assets.
@@ -19,7 +21,7 @@ use Watson\Validating\ValidatingTrait;
class Asset extends Depreciable
{
protected $presenter = 'App\Presenters\AssetPresenter';
use Loggable, Requestable, Presentable;
use Loggable, Requestable, Presentable, Notifiable;
use SoftDeletes;
const LOCATION = 'location';
@@ -66,6 +68,7 @@ class Asset extends Depreciable
'asset_tag' => 'required|min:1|max:255|unique_undeleted',
'status' => 'integer',
'purchase_cost' => 'numeric|nullable',
'next_audit_date' => 'date|nullable',
];
/**
@@ -76,6 +79,7 @@ class Asset extends Depreciable
protected $fillable = [
'asset_tag',
'assigned_to',
'assigned_type',
'company_id',
'image',
'model_id',
@@ -140,7 +144,7 @@ class Asset extends Depreciable
* @return bool
*/
//FIXME: The admin parameter is never used. Can probably be removed.
public function checkOut($target, $admin, $checkout_at = null, $expected_checkin = null, $note = null, $name = null)
public function checkOut($target, $admin = null, $checkout_at = null, $expected_checkin = null, $note = null, $name = null)
{
if (!$target) {
return false;
@@ -160,41 +164,19 @@ class Asset extends Depreciable
}
if ($this->requireAcceptance()) {
if(get_class($target) != User::class) {
throw new CheckoutNotAllowed;
}
$this->accepted="pending";
}
if ($this->save()) {
$this->logCheckout($note, $target);
// if ((($this->requireAcceptance()=='1') || ($this->getEula())) && ($user->email!='')) {
// $this->checkOutNotifyMail($log->id, $user, $checkout_at, $expected_checkin, $note);
// }
return true;
}
return false;
}
public function checkOutNotifyMail($log_id, $user, $checkout_at, $expected_checkin, $note)
{
$data['log_id'] = $log_id;
$data['eula'] = $this->getEula();
$data['first_name'] = $user->first_name;
$data['item_name'] = $this->present()->name();
$data['checkout_date'] = $checkout_at;
$data['expected_checkin'] = $expected_checkin;
$data['item_tag'] = $this->asset_tag;
$data['note'] = $note;
$data['item_serial'] = $this->serial;
$data['require_acceptance'] = $this->requireAcceptance();
if ((($this->requireAcceptance()=='1') || ($this->getEula())) && (!config('app.lock_passwords'))) {
\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_asset_delivery'));
});
}
}
public function getDetailedNameAttribute()
{
if ($this->assignedTo) {
@@ -246,16 +228,14 @@ class Asset extends Depreciable
->orderBy('created_at', 'desc');
}
/**
* Even though we allow allow for checkout to things beyond users
* this method is an easy way of seeing if we are checked out to a user.
* @return mixed
*/
public function assigneduser()
public function checkedOutToUser()
{
return $this->belongsTo('\App\Models\User', 'assigned_to')
->withTrashed();
return $this->assignedType() === self::USER;
}
public function assignedTo()
@@ -274,15 +254,19 @@ class Asset extends Depreciable
public function assetLoc()
{
if (!empty($this->assignedType())) {
// dd($this->assignedType());
if ($this->assignedType() == self::ASSET) {
return $this->assignedTo->assetloc(); // Recurse until we have a final location
} elseif ($this->assignedType() == self::LOCATION) {
return $this->assignedto->assetloc(); // Recurse until we have a final location
}
if ($this->assignedType() == self::LOCATION) {
return $this->assignedTo();
} elseif (!$this->assignedTo) {
return $this->defaultLoc();
} elseif ($this->assignedType() == self::USER) {
}
if ($this->assignedType() == self::USER) {
return $this->assignedTo->userLoc();
}
if (!$this->assignedTo) {
return $this->defaultLoc();
}
}
return $this->defaultLoc();
}
@@ -544,7 +528,7 @@ class Asset extends Depreciable
/**
* Query builder scope for pending assets
* Query builder scope for searching location
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
*
@@ -554,8 +538,17 @@ class Asset extends Depreciable
public function scopeAssetsByLocation($query, $location)
{
return $query->where(function ($query) use ($location) {
$query->whereHas('assigneduser', function ($query) use ($location) {
$query->where('users.location_id', '=', $location->id);
$query->whereHas('assignedTo', function ($query) use ($location) {
$query->where([
['users.location_id', '=', $location->id],
['assets.assigned_type', '=', User::class]
])->orWhere([
['locations.id', '=', $location->id],
['assets.assigned_type', '=', Location::class]
])->orWhere([
['assets.rtd_location_id', '=', $location->id],
['assets.assigned_type', '=', Asset::class]
]);
})->orWhere(function ($query) use ($location) {
$query->where('assets.rtd_location_id', '=', $location->id);
$query->whereNull('assets.assigned_to');
@@ -775,18 +768,26 @@ class Asset extends Depreciable
$query->whereHas('defaultLoc', function ($query) use ($search) {
$query->where('locations.name', 'LIKE', '%'.$search.'%');
});
})->orWhere(function ($query) use ($search) {
$query->whereHas('assigneduser', function ($query) use ($search) {
$query->where(function ($query) use ($search) {
$query->where('users.first_name', 'LIKE', '%'.$search.'%')
->orWhere('users.last_name', 'LIKE', '%'.$search.'%')
->orWhere(function ($query) use ($search) {
$query->whereHas('userloc', function ($query) use ($search) {
$query->where('locations.name', 'LIKE', '%'.$search.'%');
});
});
});
});
//FIXME: This needs attention to work with checkout to not-users.
// })->orWhere(function ($query) use ($search) {
// $query->whereHas('assignedTo', function ($query) use ($search) {
// $query->where(function ($query) use ($search) {
// $query->where('assets.assigned_type', '=', User::class)
// ->join('users', 'users.id', '=', 'assets.assigned_to')
// ->where(function($query) use ($search) {
// $query->where('users.first_name', 'LIKE', '%'.$search.'%')
// ->orWhere('users.last_name', 'LIKE', '%'.$search.'%');
// });
// })->orWhere(function ($query) use ($search) {
// $query->where('assets.assigned_type', '=', Location::class)
// ->join('locations', 'locations.id', '=', 'assets.assigned_to')
// ->where('locations.name', 'LIKE', '%'.$search.'%');
// })->orWhere(function ($query) use ($search) {
// $query->where('assets.assigned_type', '=', Asset::class)
// ->join('assets as assigned_asset', 'assigned_assets.id', '=', 'assets.assigned_to')
// ->where('assigned_assets.name', 'LIKE', '%'.$search.'%');
// });
// });
})->orWhere('assets.name', 'LIKE', '%'.$search.'%')
->orWhere('assets.asset_tag', 'LIKE', '%'.$search.'%')
->orWhere('assets.serial', 'LIKE', '%'.$search.'%')
@@ -1088,14 +1089,16 @@ class Asset extends Depreciable
return $query->where(function ($query) use ($search) {
$query->whereHas('defaultLoc', function ($query) use ($search) {
$query->where('locations.id', '=', $search);
})->whereNull('assigned_to');
})->orWhere(function ($query) use ($search) {
$query->whereHas('assigneduser', function ($query) use ($search) {
$query->whereHas('userloc', function ($query) use ($search) {
$query->where('locations.id', '=', $search);
});
});
});
// FIXME: This needs porting to checkout to non-user.
// ->orWhere(function ($query) use ($search) {
// $query->whereHas('assigneduser', function ($query) use ($search) {
// $query->whereHas('userloc', function ($query) use ($search) {
// $query->where('locations.id', '=', $search);
// });
// });
// });
}
+2 -2
View File
@@ -26,7 +26,7 @@ class AssetModel extends SnipeModel
// Declare the rules for the model validation
protected $rules = array(
'name' => 'required|min:1|max:255',
'model_number' => 'min:1|max:255',
'model_number' => 'max:255|nullable',
'category_id' => 'required|integer|exists:categories,id',
'manufacturer_id' => 'required|integer|exists:manufacturers,id',
'eol' => 'integer:min:0|max:240|nullable',
@@ -55,7 +55,7 @@ class AssetModel extends SnipeModel
*
* @var array
*/
protected $fillable = ['name','manufacturer_id','category_id','eol', 'user_id', 'fieldset_id'];
protected $fillable = ['name','manufacturer_id','category_id','eol', 'user_id', 'fieldset_id', 'model_number', 'notes'];
public function assets()
{
+1 -1
View File
@@ -73,7 +73,7 @@ class Consumable extends SnipeModel
return $this->belongsTo('\App\Models\User', 'user_id');
}
public function consumableAssigments()
public function consumableAssignments()
{
return $this->hasMany('\App\Models\ConsumableAssignment');
}
+11 -12
View File
@@ -38,12 +38,10 @@ class CustomField extends Model
public static function boot()
{
self::created(function ($custom_field) {
\Log::debug("\n\nCreating Original Name: ".$custom_field->name);
\Log::debug('Creating Column Name: '.$custom_field->convertUnicodeDbSlug());
// column exists - nothing to do here
if (Schema::hasColumn(CustomField::$table_name, $custom_field->convertUnicodeDbSlug())) {
\Log::debug('Column exists. Nothing to do here.');
return false;
}
@@ -57,18 +55,13 @@ class CustomField extends Model
self::updating(function ($custom_field) {
\Log::debug('Updating column name');
\Log::debug('Updating Original Name: '.$custom_field->getOriginal("name"));
\Log::debug('Updating New Column Name: '.$custom_field->convertUnicodeDbSlug());
// Column already exists. Nothing to update.
if ($custom_field->isDirty("name")) {
if (Schema::hasColumn(CustomField::$table_name, $custom_field->convertUnicodeDbSlug())) {
\Log::debug('Column already exists. Nothing to update.');
return true;
}
\Log::debug('Updating column name to.'.$custom_field->convertUnicodeDbSlug());
return Schema::table(CustomField::$table_name, function ($table) use ($custom_field) {
$table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal("name")), $custom_field->convertUnicodeDbSlug());
});
@@ -85,7 +78,7 @@ class CustomField extends Model
public function fieldset()
{
return $this->belongsToMany('\App\Models\CustomFieldset'); //?!?!?!?!?!?
return $this->belongsToMany('\App\Models\CustomFieldset');
}
public function user()
@@ -102,10 +95,9 @@ class CustomField extends Model
public function db_column_name()
{
return $this->db_column;
// return self::convertUnicodeDbSlug();
}
//mutators for 'format' attribute
// mutators for 'format' attribute
public function getFormatAttribute($value)
{
foreach (self::$PredefinedFormats as $name => $pattern) {
@@ -116,6 +108,13 @@ class CustomField extends Model
return $value;
}
/**
* Format a value string as an array for select boxes and checkboxes.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.4]
* @return Array
*/
public function setFormatAttribute($value)
{
if (isset(self::$PredefinedFormats[$value])) {
+2 -1
View File
@@ -48,6 +48,7 @@ class Ldap extends Model
// Needed for AD
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
if ($ldap_use_tls=='1') {
ldap_start_tls($connection);
@@ -110,7 +111,7 @@ class Ldap extends Model
return false;
}
if (!$user = array_change_key_case(ldap_get_attributes($connection, $entry), CASE_LOWER)) {
if (!$user = ldap_get_attributes($connection, $entry)) {
return false;
}
+14
View File
@@ -401,6 +401,20 @@ class License extends Depreciable
->orderBy('manufacturers.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'licenses.supplier_id', '=', 'suppliers.id')->select('licenses.*')
->orderBy('suppliers.name', $order);
}
/**
* Query builder scope to order on company
*
+7 -1
View File
@@ -24,6 +24,7 @@ class Location extends SnipeModel
'address' => 'max:80|nullable',
'address2' => 'max:80|nullable',
'zip' => 'min:3|max:10|nullable',
// 'manager_id' => 'exists:users'
);
/**
@@ -63,7 +64,12 @@ class Location extends SnipeModel
public function parent()
{
return $this->belongsTo('\App\Models\Location', 'parent_id');
return $this->belongsTo('\App\Models\Location', 'parent_id','id');
}
public function manager()
{
return $this->belongsTo('\App\Models\User', 'manager_id');
}
public function childLocations()
+83 -43
View File
@@ -7,6 +7,7 @@ use App\Models\Asset;
use App\Models\CheckoutRequest;
use App\Models\User;
use App\Notifications\CheckinNotification;
use App\Notifications\AuditNotification;
use App\Notifications\CheckoutNotification;
use Illuminate\Support\Facades\Auth;
@@ -29,11 +30,56 @@ trait Loggable
* @since [v3.4]
* @return \App\Models\Actionlog
*/
public function logCheckout($note, $target = null /*target is overridable for components*/)
public function logCheckout($note, $target /* What are we checking out to? */)
{
$log = new Actionlog;
$log = $this->determineLogItemType($log);
$log->user_id = Auth::user()->id;
// We need to special case licenses because of license_seat vs license. So much for clean polymorphism :)
if (!isset($target)) {
throw new Exception('All checkout logs require a target');
return;
}
$log->target_type = get_class($target);
$log->target_id = $target->id;
$class = get_class($target);
if ($class == Location::class) {
// We can checkout to a location
$log->location_id = $target->id;
} else if ($class== Asset::class) {
$log->location_id = $target->rtd_location_id;
} else {
$log->location_id = $target->location_id;
}
$log->note = $note;
$log->logaction('checkout');
$params = [
'item' => $this,
'target' => $target,
'admin' => $log->user,
'note' => $note,
'log_id' => $log->id
];
if ($settings = Setting::getSettings()) {
$settings->notify(new CheckoutNotification($params));
}
if (method_exists($target, 'notify')) {
$target->notify(new CheckoutNotification($params));
}
return $log;
}
/**
* Helper method to determine the log item type
*/
private function determineLogItemType($log)
{
// We need to special case licenses because of license_seat vs license. So much for clean polymorphism :
if (static::class == LicenseSeat::class) {
$log->item_type = License::class;
$log->item_id = $this->license_id;
@@ -42,49 +88,8 @@ trait Loggable
$log->item_id = $this->id;
}
$log->user_id = Auth::user()->id;
// @FIXME This needs to be generalized with new asset checkout.
if(isset($target)) {
$log->target_type = get_class($target);
$log->target_id = $target->id;
} else {
if (!is_null($this->asset_id)) {
$log->target_type = Asset::class;
$log->target_id = $this->asset_id;
} elseif (!is_null($this->assigned_to)) {
$log->target_type = User::class;
$log->target_id = $this->assigned_to;
}
}
$item = call_user_func(array($log->target_type, 'find'), $log->target_id);
if($this->assignedTo) {
$item = $this->assignedTo;
}
$class = get_class($item);
if($class == Location::class) {
// We can checkout to a location
$log->location_id = $item->id;
} else if ($class== Asset::class) {
$log->location_id = $item->rtd_location_id;
} else {
$log->location_id = $item->location_id;
}
$log->note = $note;
$log->logaction('checkout');
$params = [
'item' => $log->item,
'target' => $log->target,
'admin' => $log->user,
'note' => $note
];
Setting::getSettings()->notify(new CheckoutNotification($params));
return $log;
}
/**
* @author Daniel Meltzer <parallelgrapefruit@gmail.com
* @since [v3.4]
@@ -117,6 +122,41 @@ trait Loggable
return $log;
}
/**
* @author A. Gianotto <snipe@snipe.net>
* @since [v4.0]
* @return \App\Models\Actionlog
*/
public function logAudit($note, $location_id)
{
$log = new Actionlog;
$location = Location::find($location_id);
if (static::class == LicenseSeat::class) {
$log->item_type = License::class;
$log->item_id = $this->license_id;
} else {
$log->item_type = static::class;
$log->item_id = $this->id;
}
$log->location_id = ($location_id) ? $location_id : null;
$log->note = $note;
$log->user_id = Auth::user()->id;
$log->logaction('audit');
$params = [
'item' => $log->item,
'admin' => $log->user,
'location' => ($location) ? $location->name : '',
'note' => $note
];
Setting::getSettings()->notify(new AuditNotification($params));
return $log;
}
/**
* @author Daniel Meltzer <parallelgrapefruit@gmail.com
* @since [v3.5]
+3 -3
View File
@@ -19,9 +19,9 @@ trait Requestable
public function isRequestedBy(User $user)
{
return $this->requests()
->where('user_id', $user->id)
->exists();
$requests = $this->requests->where('user_id', $user->id);
return $requests->count() > 0;
}
public function scopeRequestedBy($query, User $user)
+41 -21
View File
@@ -14,36 +14,30 @@ class Setting extends Model
protected $rules = [
"brand" => 'required|min:1|numeric',
"qr_text" => 'max:31',
"qr_text" => 'max:31|nullable',
"logo_img" => 'mimes:jpeg,bmp,png,gif',
"alert_email" => 'email_array',
"alert_email" => 'email_array|nullable',
"default_currency" => 'required',
"locale" => 'required',
"slack_endpoint" => 'url|required_with:slack_channel',
"slack_channel" => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint',
"slack_endpoint" => 'url|required_with:slack_channel|nullable',
"slack_channel" => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
"slack_botname" => 'string|nullable',
'labels_per_page' => 'numeric',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_pmargin_left' => 'numeric',
'labels_pmargin_right' => 'numeric',
'labels_pmargin_top' => 'numeric',
'labels_pmargin_bottom' => 'numeric',
'labels_display_bgutter' => 'numeric',
'labels_display_sgutter' => 'numeric',
'labels_pmargin_left' => 'numeric|nullable',
'labels_pmargin_right' => 'numeric|nullable',
'labels_pmargin_top' => 'numeric|nullable',
'labels_pmargin_bottom' => 'numeric|nullable',
'labels_display_bgutter' => 'numeric|nullable',
'labels_display_sgutter' => 'numeric|nullable',
'labels_fontsize' => 'numeric|min:5',
'labels_pagewidth' => 'numeric',
'labels_pageheight' => 'numeric',
"ldap_server" => 'sometimes|required_if:ldap_enabled,1|url',
"ldap_uname" => 'sometimes|required_if:ldap_enabled,1',
"ldap_basedn" => 'sometimes|required_if:ldap_enabled,1',
"ldap_filter" => 'sometimes|required_if:ldap_enabled,1',
"ldap_username_field" => 'sometimes|required_if:ldap_enabled,1',
"ldap_fname_field" => 'sometimes|required_if:ldap_enabled,1',
"ldap_lname_field" => 'sometimes|required_if:ldap_enabled,1',
"ldap_auth_filter_query" => 'sometimes|required_if:ldap_enabled,1',
"ldap_version" => 'sometimes|required_if:ldap_enabled,1',
'labels_pagewidth' => 'numeric|nullable',
'labels_pageheight' => 'numeric|nullable',
"thumbnail_max_h" => 'numeric|max:500|min:25',
"pwd_secure_min" => "numeric|required|min:5",
"audit_warning_days" => "numeric|nullable",
"audit_interval" => "numeric|nullable",
];
protected $fillable = ['site_name','email_domain','email_format','username_format'];
@@ -158,4 +152,30 @@ class Setting extends Model
// In the future this may want to be adapted for individual notifications.
return $this->slack_endpoint;
}
public static function passwordComplexityRulesSaving($action = 'update')
{
$security_rules = '';
$settings = Setting::getSettings();
// Check if they have uncommon password enforcement selected in settings
if ($settings->pwd_secure_uncommon == 1) {
$security_rules .= '|dumbpwd';
}
// Check for any secure password complexity rules that may have been selected
if ($settings->pwd_secure_complexity!='') {
$security_rules .= '|'.$settings->pwd_secure_complexity;
}
if ($action == 'update') {
return 'nullable|min:'.$settings->pwd_secure_min.$security_rules;
}
return 'required|min:'.$settings->pwd_secure_min.$security_rules;
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ class Supplier extends SnipeModel
*
* @var array
*/
protected $fillable = ['name'];
protected $fillable = ['name','address','address2','city','state','country','zip','phone','fax','email','contact','url','notes'];
// Eager load counts.
+10 -2
View File
@@ -51,9 +51,9 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
protected $rules = [
'first_name' => 'required|string|min:1',
'username' => 'required|string|min:1|unique_undeleted',
'email' => 'email',
'email' => 'email|nullable',
'password' => 'required|min:6',
'locale' => 'max:10'
'locale' => 'max:10|nullable'
];
@@ -205,6 +205,14 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
return $this->belongsTo('\App\Models\User', 'manager_id')->withTrashed();
}
/**
* Get any locations the user manages.
**/
public function managedLocations()
{
return $this->hasMany('\App\Models\Location', 'manager_id')->withTrashed();
}
/**
* Get user groups
*/
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class AuditNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
//
$this->params = $params;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint) {
$notifyBy[] = 'slack';
}
return $notifyBy;
}
public function toSlack($notifiable)
{
return (new SlackMessage)
->success()
->content(class_basename(get_class($this->params['item'])) . " Audited")
->attachment(function ($attachment) use ($notifiable) {
$item = $this->params['item'];
$admin_user = $this->params['admin'];
$fields = [
'By' => '<'.$admin_user->present()->viewUrl().'|'.$admin_user->present()->fullName().'>'
];
array_key_exists('note', $this->params) && $fields['Notes'] = $this->params['note'];
array_key_exists('location', $this->params) && $fields['Location'] = $this->params['location'];
$attachment->title($item->present()->name, $item->present()->viewUrl())
->fields($fields);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
+32 -10
View File
@@ -5,10 +5,11 @@ namespace App\Notifications;
use App\Models\Setting;
use App\Models\SnipeModel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutNotification extends Notification
{
@@ -43,11 +44,12 @@ class CheckoutNotification extends Notification
}
$item = $this->params['item'];
if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance()=='1'))
|| (method_exists($item, 'getEula') && ($item->getEula()))
) {
$notifyBy[] = 'mail';
}
$notifyBy[]='mail';
// if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance()=='1'))
// || (method_exists($item, 'getEula') && ($item->getEula()))
// ) {
// $notifyBy[] = 'mail';
// }
return $notifyBy;
}
@@ -79,10 +81,30 @@ class CheckoutNotification extends Notification
*/
public function toMail($notifiable)
{
//TODO: Expand for non assets.
$item = $this->params['item'];
$admin_user = $this->params['admin'];
$target = $this->params['target'];
$data = [
'eula' => method_exists($item, 'getEula') ? $item->getEula() : '',
'first_name' => $target->present()->fullName(),
'item_name' => $item->present()->name(),
'checkout_date' => $item->last_checkout,
'expected_checkin' => $item->expected_checkin,
'item_tag' => $item->asset_tag,
'note' => $this->params['note'],
'item_serial' => $item->serial,
'require_acceptance' => $item->requireAcceptance(),
'log_id' => $this->params['log_id'],
];
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', 'https://laravel.com')
->line('Thank you for using our application!');
->view('emails.accept-asset', $data)
->subject(trans('mail.Confirm_asset_delivery'));
// \Mail::send('emails.accept-asset', $data, function ($m) use ($target) {
// $m->to($target->email, $target->first_name . ' ' . $target->last_name);
// $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
// $m->subject(trans('mail.Confirm_asset_delivery'));
// });
}
/**
@@ -0,0 +1,86 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use App\Models\SnipeModel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
use Carbon\Carbon;
class ExpectedCheckinNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->params = $params;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$notifyBy = [];
$item = $this->params['item'];
$notifyBy[]='mail';
return $notifyBy;
}
public function toSlack($notifiable)
{
}
/**
* Get the mail representation of the notification.
*
* @param mixed $asset
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($params)
{
$formatted_due = Carbon::parse($this->params->expected_checkin)->format('D, M j, Y');
return (new MailMessage)
->error()
->subject('Reminder: '.$this->params->present()->name().' checkin deadline approaching')
->line('Hi, '.$this->params->assignedto->first_name)
->greeting('An asset checked out to you is due to be checked back in on '.$formatted_due.'.')
->line('Asset: '.$this->params->present()->name())
->line('Serial: '.$this->params->serial)
->line('Asset Tag: '.$this->params->asset_tag)
->action('View Your Assets', route('view-assets'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
+1 -5
View File
@@ -28,8 +28,7 @@ class AccessoryObserver
/**
* Listen to the Accessory created event, and increment
* the next_auto_tag_base value in the settings table when i
* Listen to the Accessory created event when
* a new accessory is created.
*
* @param Accessory $accessory
@@ -37,9 +36,6 @@ class AccessoryObserver
*/
public function created(Accessory $accessory)
{
$settings = Setting::first();
$settings->increment('next_auto_tag_base');
$logAction = new Actionlog();
$logAction->item_type = Accessory::class;
$logAction->item_id = $accessory->id;
+6 -3
View File
@@ -18,7 +18,9 @@ class AssetObserver
public function updating(Asset $asset)
{
if (($asset->getAttributes()['assigned_to'] == $asset->getOriginal()['assigned_to'])
if ((isset($asset->getOriginal()['assigned_to'])) && ($asset->getAttributes()['assigned_to'] == $asset->getOriginal()['assigned_to'])
&& ($asset->getAttributes()['next_audit_date'] == $asset->getOriginal()['next_audit_date'])
&& ($asset->getAttributes()['last_checkout'] == $asset->getOriginal()['last_checkout'])
&& ($asset->getAttributes()['status_id'] == $asset->getOriginal()['status_id']))
{
@@ -43,8 +45,9 @@ class AssetObserver
*/
public function created(Asset $asset)
{
$settings = Setting::first();
$settings->increment('next_auto_tag_base');
if ($settings = Setting::first()) {
$settings->increment('next_auto_tag_base');
}
$logAction = new Actionlog();
$logAction->item_type = Asset::class;
+1 -5
View File
@@ -28,8 +28,7 @@ class ComponentObserver
/**
* Listen to the Component created event, and increment
* the next_auto_tag_base value in the settings table when i
* Listen to the Component created event when
* a new component is created.
*
* @param Component $component
@@ -37,9 +36,6 @@ class ComponentObserver
*/
public function created(Component $component)
{
$settings = Setting::first();
$settings->increment('next_auto_tag_base');
$logAction = new Actionlog();
$logAction->item_type = Component::class;
$logAction->item_id = $component->id;
+1 -4
View File
@@ -28,8 +28,7 @@ class ConsumableObserver
/**
* Listen to the Consumable created event, and increment
* the next_auto_tag_base value in the settings table when i
* Listen to the Consumable created event when
* a new consumable is created.
*
* @param Consumable $consumable
@@ -37,8 +36,6 @@ class ConsumableObserver
*/
public function created(Consumable $consumable)
{
$settings = Setting::first();
$settings->increment('next_auto_tag_base');
$logAction = new Actionlog();
$logAction->item_type = Consumable::class;
+1 -4
View File
@@ -28,8 +28,7 @@ class LicenseObserver
/**
* Listen to the License created event, and increment
* the next_auto_tag_base value in the settings table when i
* Listen to the License created event when
* a new license is created.
*
* @param License $license
@@ -37,8 +36,6 @@ class LicenseObserver
*/
public function created(License $license)
{
$settings = Setting::first();
$settings->increment('next_auto_tag_base');
$logAction = new Actionlog();
$logAction->item_type = License::class;
+2 -3
View File
@@ -98,15 +98,14 @@ class AssetPresenter extends Presenter
"sortable" => true,
"title" => trans('admin/hardware/form.checkedout_to'),
"visible" => true,
"formatter" => "usersLinkObjFormatter"
"formatter" => "polymorphicItemFormatter"
], [
"field" => "assigned_to",
"field" => "employee_number",
"searchable" => false,
"sortable" => false,
"title" => trans('admin/users/table.employee_num'),
"visible" => false,
"formatter" => "employeeNumFormatter"
],[
"field" => "location",
"searchable" => true,
+8
View File
@@ -61,6 +61,14 @@ class LicensePresenter extends Presenter
"searchable" => true,
"sortable" => true,
"title" => trans('admin/licenses/form.to_name'),
], [
"field" => "supplier",
"searchable" => true,
"sortable" => true,
"switchable" => true,
"title" => trans('general.supplier'),
"visible" => false,
"formatter" => "suppliersLinkObjFormatter"
], [
"field" => "manufacturer",
"searchable" => true,
+4
View File
@@ -42,4 +42,8 @@ class LocationPresenter extends Presenter
{
return '<i class="fa fa-globe"></i>';
}
public function fullName() {
return $this->name;
}
}
+5
View File
@@ -65,6 +65,11 @@ abstract class Presenter
return '';
}
public function name()
{
return $this->model->name;
}
public function __get($property)
{
if (method_exists($this, $property)) {
+7 -3
View File
@@ -67,11 +67,15 @@ class AppServiceProvider extends ServiceProvider
// This works around the use case where multiple deleted items have the same unique attribute.
// (I think this is a bug in Laravel's validator?)
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
$count = DB::table($parameters[0])->select('id')->where($attribute, '=', $value)->whereNull('deleted_at')->where('id', '!=', $parameters[1])->count();
return $count < 1;
if (count($parameters)) {
$count = DB::table($parameters[0])->select('id')->where($attribute, '=', $value)->whereNull('deleted_at')->where('id', '!=', $parameters[1])->count();
return $count < 1;
}
});
// Share common variables with all views.
// Share common setting variables with all views.
view()->composer('*', function ($view) {
$view->with('snipeSettings', \App\Models\Setting::getSettings());
});
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -3
View File
@@ -15,7 +15,7 @@
"fideloper/proxy": "^3.1",
"intervention/image": "^2.3",
"javiereguiluz/easyslugger": "^1.0",
"laravel/framework": "5.4.27",
"laravel/framework": "5.4.20",
"laravel/passport": "^1.0",
"laravel/tinker": "^1.0",
"laravelcollective/html": "^5.3",
@@ -24,9 +24,13 @@
"neitanod/forceutf8": "^2.0",
"patchwork/utf8": "~1.2",
"pragmarx/google2fa": "^1.0",
"schuppo/password-strength": "~1.5",
"spatie/laravel-backup": "^3.0.0",
"tecnickcom/tc-lib-barcode": "^1.15",
"watson/validating": "^3.0"
"unicodeveloper/laravel-password": "^1.0",
"watson/validating": "^3.0",
"doctrine/instantiator": "1.0.5",
"doctrine/inflector": "1.0.*"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
@@ -34,7 +38,8 @@
"symfony/css-selector": "3.1.*",
"symfony/dom-crawler": "3.1.*",
"codeception/codeception": "2.2.9",
"squizlabs/php_codesniffer": "*"
"squizlabs/php_codesniffer": "*",
"phpunit/php-token-stream": "1.4.11"
},
"autoload": {
Generated
+451 -280
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -220,6 +220,8 @@ return [
PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider::class,
Laravel\Passport\PassportServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class,
Unicodeveloper\DumbPassword\DumbPasswordServiceProvider::class,
Schuppo\PasswordStrength\PasswordStrengthServiceProvider::class,
/*
+2 -2
View File
@@ -85,8 +85,8 @@ return array(
array(
'permission' => 'assets.audit',
'label' => 'Audit ',
'note' => '',
'display' => false,
'note' => 'Allows the user to mark an asset as physically inventoried.',
'display' => true,
),
+3 -3
View File
@@ -1,7 +1,7 @@
<?php
return array (
'app_version' => 'v4.0-beta',
'app_version' => 'v4.0',
'build_version' => '1',
'hash_version' => 'gcb1e3b7',
'full_hash' => 'v4.0-beta-244-gcb1e3b7',
'hash_version' => 'g998c4a5',
'full_hash' => 'v4.0-1-g998c4a5',
);
+1 -1
View File
@@ -20,7 +20,7 @@ $factory->defineAs(Actionlog::class, 'asset-checkout', function (Faker\Generator
$user = factory(App\Models\User::class)->create(['company_id' => $company->id]);
$target = factory(App\Models\User::class)->create(['company_id' => $company->id]);
// $item = factory(App\Models\Asset::class)->create(['company_id' => $company->id]);
// dd($item);
return [
'user_id' => $user->id,
'action_type' => 'checkout',
+9
View File
@@ -1,6 +1,8 @@
<?php
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
/*
|--------------------------------------------------------------------------
@@ -69,6 +71,13 @@ $factory->state(Asset::class, 'assigned-to-asset', function ($faker) {
];
});
$factory->state(Asset::class, 'requires-acceptance', function ($faker) {
$cat = factory(Category::class)->states('asset-category', 'requires-acceptance')->create();
$model = factory(AssetModel::class)->create(['category_id' => $cat->id]);
return [
'model_id' => $model->id
];
});
$factory->define(App\Models\AssetModel::class, function (Faker\Generator $faker) {
return [
+7 -1
View File
@@ -15,7 +15,7 @@ $factory->define(App\Models\Category::class, function (Faker\Generator $faker) {
'name' => $faker->text(20),
'category_type' => $faker->randomElement(['asset', 'accessory', 'component', 'consumable']),
'eula_text' => $faker->paragraph(),
'require_acceptance' => $faker->boolean(),
'require_acceptance' => false,
'use_default_eula' => $faker->boolean(),
'checkin_email' => $faker->boolean()
];
@@ -44,3 +44,9 @@ $factory->state(App\Models\Category::class, 'consumable-category', function ($fa
'category_type' => 'consumable',
];
});
$factory->state(App\Models\Category::class, 'requires-acceptance', function ($faker) {
return [
'require_acceptance' => true,
];
});
@@ -0,0 +1,17 @@
<?php
$factory->define(App\Models\CustomField::class, function (Faker\Generator $faker) {
return [
'name' => $faker->catchPhrase,
'format' => 'IP',
'element' => 'text',
];
});
$factory->define(App\Models\CustomFieldset::class, function (Faker\Generator $faker) {
return [
'name' => $faker->catchPhrase,
'user_id' => Auth::id()
];
});
+1 -8
View File
@@ -97,14 +97,6 @@ $factory->define(App\Models\Consumable::class, function (Faker\Generator $faker)
];
});
$factory->define(App\Models\CustomField::class, function (Faker\Generator $faker) {
return [
'name' => $faker->catchPhrase,
'format' => 'IP',
'element' => 'text',
];
});
$factory->define(App\Models\Department::class, function (Faker\Generator $faker) {
return [
'name' => $faker->catchPhrase,
@@ -210,5 +202,6 @@ $factory->define(App\Models\Setting::class, function ($faker) {
'brand' => 1,
'default_currency' => $faker->currencyCode,
'locale' => $faker->locale,
'pwd_secure_min' => 5,
];
});
@@ -12,10 +12,13 @@ class AddSoftDeletedToLog extends Migration {
*/
public function up()
{
Schema::table('asset_logs', function ($table) {
$table->string('asset_type',100)->nullable()->change();
});
$platform = Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('enum', 'string');
Schema::table('asset_logs', function ($table) {
$table->string('asset_type',100)->nullable()->change();
});
// DB::statement('ALTER TABLE ' . DB::getTablePrefix() . 'asset_logs MODIFY column asset_type varchar(100) null');
// DB::statement('ALTER TABLE ' . DB::getTablePrefix() . 'asset_logs MODIFY column added_on timestamp default "0000-00-00 00:00:00"');
@@ -21,7 +21,7 @@ use Illuminate\Database\Schema\Blueprint;
function updateLegacyColumnName($customfield) {
$name_to_db_name = CustomField::name_to_db_name($customfield->name);
\Log::debug('Trying to rename '.$name_to_db_name." to ".$customfield->convertUnicodeDbSlug()."...\n");
//\Log::debug('Trying to rename '.$name_to_db_name." to ".$customfield->convertUnicodeDbSlug()."...\n");
if (Schema::hasColumn(CustomField::$table_name, $name_to_db_name)) {
@@ -32,7 +32,7 @@ function updateLegacyColumnName($customfield) {
);
} else {
\Log::debug('Legacy DB column '.$name_to_db_name.' was not found on the assets table.');
//\Log::debug('Legacy DB column '.$name_to_db_name.' was not found on the assets table.');
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddManagerToLocationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('locations', function (Blueprint $table) {
//
$table->integer('manager_id')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('locations', function (Blueprint $table) {
//
$table->dropColumn('manager_id');
});
}
}
@@ -25,7 +25,7 @@ class AddNextAutoincrementToSettings extends Migration
$table->bigInteger('next_auto_tag_base')->default('1');
});
\Log::debug('Setting '.$next.' as default auto-increment');
//\Log::debug('Setting '.$next.' as default auto-increment');
if ($settings = App\Models\Setting::first()) {
$settings->next_auto_tag_base = $next;
@@ -13,6 +13,9 @@ class SetAssetArchivedToZeroDefault extends Migration
*/
public function up()
{
$platform = Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('enum', 'string');
Schema::table('assets', function (Blueprint $table) {
$table->boolean('archived')->default(0)->change();
});
@@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSecurePasswordOptions extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('pwd_secure_uncommon')->default('0');
$table->string('pwd_secure_complexity')->nullable()->default(NULL);
$table->integer('pwd_secure_min')->default('8');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('pwd_secure_uncommon');
$table->dropColumn('pwd_secure_complexity');
$table->dropColumn('pwd_secure_min');
});
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddAuditingTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->date('next_audit_date')->nullable()->default(NULL);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('assets', function (Blueprint $table) {
$table->dropColumn('next_audit_date');
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddAuditingToSettings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->integer('audit_interval')->nullable()->default(NULL);
$table->integer('audit_warning_days')->nullable()->default(NULL);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('audit_interval');
$table->dropColumn('audit_warning_days');
});
}
}

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