Compare commits

...

205 Commits

Author SHA1 Message Date
snipe 0472e3a3e5 Merge branch 'develop' 2018-05-02 14:41:10 -07:00
lea-mink a0afa9f2e8 Modified the affectation of the value of the password in credential mail sent for the first user sign up (#5446)
* Modified the affectation of the value of the password

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

* fedora support. strict versioning.

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

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

* less spaces!

* rename functions

* move progress spinner variables into progress ()

* $hosts was used in one place

* missed this one

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

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

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

* Adds default value functionality

* Adds built assets

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

* Updates built assets

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

* Added department to custom asset export
Updates build assets

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

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

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

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

* Simplify Logic

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

* Removed old commented items

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

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

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

* Only show asset name in email if it has one

* Refactor requested method to only include non-canceled requests

* Refactored requestable assets to log request and cancelation

* Added softdeletes on checkout requests

* Differentiate between canceling and deleting requests

* Added asset request cancelation notification

* Added timestamps and corrected unique key on requests table

* Improved requests view

* Re-use blade for cancel/request email

* Refactored BS table formatter for requested assets

* Location name min reduced to 2

* Added PAT test as maintenance option

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

* Better slack message

* Added getImageUrl method for assets

* Include qty in request notifications

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

* Removed old asset request/cancel emails

* Added user profile asset request routes

* Added profile controller requested assets method

* Added blade link to requested assets for profile view

* Sort user history desc

* Added requested assets blade

* Added canceled at to checkoutRequest method

* Include qty in request

* Fixed comment, removed allowed_columns

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

* Fixed return type in method doc

* Fixed version number

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

* Seed the Setting object with default values.

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

* Seed the Setting object with default values.

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

* Added “show images in email” to settings

* Added nicer HTML emails

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

* Remove old notification for accessory checkout

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

* Add method for image url for accessories

* Added accessory checkout email blade

* Make accessory email notification on checkout screen consistent with assets

* Added native consumables notifications

* Fixes for asset notification

* Updated notification blades with correct-er fields

* Updated notifications

* License checkin notification - does not work yet

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

* Added alternate “cc” email for admins

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

* Fix tests

* Fixed consumable URL

* Removed unused notification

* Pass target type in params

* Show slack status

* Pass additional parameters

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

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

* Slack is broken :( Trying to fix

Will try a git bisect

* Put preview back into checkout

* Pulled old archaic mail

* Removed debugging

* Fixed wrong email title

* Fixed slack endpoint not firing

* Poobot, we hardly knew ye.

* Removed old, manual mail from API

* Typo :-/

* Code cleanup

* Use defined formatted date in JSON

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

* Removed debugging

* Use date formatter

* Fixed target_type

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

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

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

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

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

* Missing dependency for upgrade.php #4726

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

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

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

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

* Add show URL in Emails condition

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

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

* Update dark-green.css

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

* API tests for asset models and related cleanup/improvements

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

* API Category tests.

* Manufacturers API Test.

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

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

* Add Checkin/Checkout api tests.

* Accessories api test

* Add Companies API Test.

* Return ModelNotFound as a 404.

* Cleanups/simplficiations/updates.

* Locations api test.

* currency and image should be fillable on location.

* Update components api test.

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

* order_number should be fillable in component.

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

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

* Add test for assets method

* API status label test.

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

* Add serial to update.

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

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

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

* Fix functional tests

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

* Importer name/full name fixes.

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

Also update sample csvs and remove an outdated sample.

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

* Use sqlite/different db setup for unit tests.

* Fix assets api test.

* Fix Components API test.

* increase travis memory limit for functional tests.

* Use travis config for api tests as well.

* Fix memory limit file.

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

Also update sample csvs and remove an outdated sample.
2018-02-21 16:42:36 -08:00
snipe 006b2b18b0 Added qty to sorting 2018-02-21 16:21:31 -08:00
snipe fb262e38a7 Added - make total sortable in components 2018-02-21 16:21:21 -08:00
snipe f8151284ee Fixed - removed extra components table 2018-02-21 16:20:46 -08:00
snipe 5d8c91b687 Fixed regression - Added sum footer back into accessories 2018-02-21 15:54:16 -08:00
snipe 0f23462607 Merge branch 'develop' 2018-02-21 15:51:33 -08:00
snipe ca50ea190f Applied master changes to develop
Wrong branch :(
2018-02-21 15:51:04 -08:00
snipe 31192abd2c Fixed ambniguous query in asset maintenance search 2018-02-21 10:28:05 -08:00
snipe 42c7f41d24 Added some alerting in custom fields if it looks like the db_column and real column do not match 2018-02-21 10:06:23 -08:00
snipe fade03e337 Added nicer gates for auditing 2018-02-21 08:13:18 -08:00
1161 changed files with 33916 additions and 8418 deletions
+28
View File
@@ -883,6 +883,34 @@
"contributions": [
"code"
]
},
{
"login": "cepacs",
"name": "cepacs",
"avatar_url": "https://avatars3.githubusercontent.com/u/36515590?v=4",
"profile": "https://github.com/cepacs",
"contributions": [
"bug",
"doc"
]
},
{
"login": "lea-mink",
"name": "lea-mink",
"avatar_url": "https://avatars2.githubusercontent.com/u/37537300?v=4",
"profile": "https://github.com/lea-mink",
"contributions": [
"code"
]
},
{
"login": "hannahtinkler",
"name": "Hannah Tinkler",
"avatar_url": "https://avatars0.githubusercontent.com/u/7140719?v=4",
"profile": "https://github.com/hannahtinkler",
"contributions": [
"code"
]
}
]
}
+2
View File
@@ -7,6 +7,7 @@ APP_KEY=ChangeMe
APP_URL=null
APP_TIMEZONE='UTC'
APP_LOCALE=en
BACKUP_ENV=false
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
@@ -105,3 +106,4 @@ APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
APP_CIPHER=AES-256-CBC
GOOGLE_MAPS_API=
BACKUP_ENV=true
+2 -2
View File
@@ -1,8 +1,8 @@
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=sqlite_testing
DB_DEFAULT=sqlite_testing
DB_CONNECTION=mysql
DB_DEFAULT=mysql
DB_HOST=localhost
DB_DATABASE=snipeittests
DB_USERNAME=snipeit
+19
View File
@@ -0,0 +1,19 @@
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=sqlite_testing
DB_DEFAULT=sqlite_testing
DB_HOST=localhost
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# (LOGIN_LOCKOUT_DURATIONin minutes)
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=1000000
LOGIN_LOCKOUT_DURATION=100000000
MAIL_DRIVER=log
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME=Snipe-IT
+61
View File
@@ -0,0 +1,61 @@
---
name: Bug report
about: Create a report to help us improve
---
#### Please confirm you have done the following before posting your bug report:
- [ ] I have enabled debug mode
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Error Messages**
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
- If a stacktrace is provided in the error, include that too.
- Any errors that appear in your browser's error console.
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
- Include any additional information you can find in `storage/logs` and your webserver's logs.
**Additional context**
- Is this a fresh install or an upgrade?
- What OS and web server you're running Snipe-IT on
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
- Include what you've done so far in the installation, and if you got any error messages along the way.
- Indicate whether or not you've manually edited any data directly in the database
Add any other context about the problem here.
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.
+23
View File
@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+3 -2
View File
@@ -6,8 +6,9 @@ daysUntilClose: 7
exemptLabels:
- pinned
- security
- ready for dev
- bounty
- :woman_technologist: ready for dev
- :moneybag: bounty
- :hand: bug
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
+1
View File
@@ -0,0 +1 @@
memory_limit= 2048M
+11 -4
View File
@@ -1,4 +1,7 @@
addons:
code_climate:
repo_token:
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
hosts:
- localhost
sudo: false
@@ -13,11 +16,12 @@ services:
php:
- 5.6
- 7.0
- 7.2
- 7.2
- 7.1.4
# execute any number of scripts before the test run, custom env's are available as variables
before_script:
- phpenv config-add .github/travis-memory.ini
- phantomjs --webdriver=4444 &
- sleep 4
- mysql -e 'CREATE DATABASE snipeit_unit;'
@@ -47,9 +51,12 @@ before_script:
script:
- ./vendor/bin/codecept run unit
# - ./vendor/bin/codecept run acceptance --env=testing-ci
- ./vendor/bin/codecept run functional --env=functional-travis
#script: ./vendor/bin/codecept run
- ./vendor/bin/codecept run api --env=testing-ci
- ./vendor/bin/codecept run functional --env=functional-travis -g func1
- ./vendor/bin/codecept run functional --env=functional-travis -g func2
- ./vendor/bin/codecept run api --env=functional-travis
after_script:
- vendor/bin/test-reporter
after_success:
- codecov
+4 -1
View File
@@ -64,7 +64,10 @@ RUN chown -R docker /var/www/html
RUN \
rm -r "/var/www/html/storage/private_uploads" && ln -fs "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" \
&& rm -rf "/var/www/html/public/uploads" && ln -fs "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" \
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups"
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
&& mkdir "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
&& chown docker "/var/lib/snipeit/keys/"
############## DEPENDENCIES via COMPOSER ###################
+14 -3
View File
@@ -1,6 +1,5 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=master)](https://travis-ci.org/snipe/snipe-it) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeyhead.svg?style=social)](https://twitter.com/snipeyhead) [![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-94-orange.svg?style=flat-square)](#contributors)
[![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
[![All Contributors](https://img.shields.io/badge/all_contributors-98-orange.svg?style=flat-square)](#contributors) [![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
## Snipe-IT - Open Source Asset Management System
@@ -51,6 +50,18 @@ Please see the [translations documentation](https://snipe-it.readme.io/docs/tran
-----
### Libraries, Modules & Related Projects
Since the release of the JSON REST API, several third-party developers have been developing modules and libraries to work with Snipe-IT.
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [InQRy](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
-----
### Contributors
Thanks goes to all of these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)) who have helped Snipe-IT get this far:
@@ -70,7 +81,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
Vendored
+76
View File
@@ -0,0 +1,76 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
Vagrant.configure("2") do |config|
config.vm.define "xenial" do |xenial|
xenial.vm.box = "ubuntu/xenial64"
xenial.vm.hostname = 'xenial'
xenial.vm.network "public_network", bridge: NETWORK_BRIDGE
xenial.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
xenial.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty32"
trusty.vm.hostname = 'trusty'
trusty.vm.network "public_network", bridge: NETWORK_BRIDGE
trusty.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
trusty.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "centos7" do |centos7|
centos7.vm.box = "centos/7"
centos7.vm.hostname = 'centos7'
centos7.vm.network "public_network", bridge: NETWORK_BRIDGE
centos7.vm.provision :shell, :inline => "sudo yum -y update"
centos7.vm.provision :shell, :inline => "yum install -y wget"
centos7.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
centos7.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "centos6" do |centos6|
centos6.vm.box = "centos/6"
centos6.vm.hostname = 'centos6'
centos6.vm.network "public_network", bridge: NETWORK_BRIDGE
centos6.vm.provision :shell, :inline => "sudo yum -y update"
centos6.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
centos6.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "jessie" do |jessie|
jessie.vm.box = "debian/jessie64"
jessie.vm.hostname = 'debian8'
jessie.vm.network "public_network", bridge: NETWORK_BRIDGE
jessie.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
jessie.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "stretch" do |stretch|
stretch.vm.box = "debian/stretch64"
stretch.vm.hostname = 'debian9'
stretch.vm.network "public_network", bridge: NETWORK_BRIDGE
stretch.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
stretch.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "fedora27" do |fedora27|
fedora27.vm.box = "fedora/27-cloud-base"
fedora27.vm.hostname = 'fedora27'
fedora27.vm.network "public_network", bridge: NETWORK_BRIDGE
fedora27.vm.provision :shell, :inline => "dnf -y install wget"
fedora27.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
fedora27.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
config.vm.define "fedora26" do |fedora26|
fedora26.vm.box = "fedora/26-cloud-base"
fedora26.vm.hostname = 'fedora26'
fedora26.vm.network "public_network", bridge: NETWORK_BRIDGE
fedora26.vm.provision :shell, :inline => "dnf -y install wget"
fedora26.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
fedora26.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
end
end
+7 -1
View File
@@ -190,7 +190,13 @@ class LdapSync extends Command
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (!empty($location))) {
$user->location_id = e($location->id);
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
$user->notes = 'Imported from LDAP';
@@ -55,6 +55,7 @@ class ResetDemoSettings extends Command
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 1;
$settings->alt_barcode = 'C128';
$settings->skin = '';
$settings->email_domain = 'snipeitapp.com';
$settings->email_format = 'filastname';
$settings->username_format = 'filastname';
@@ -77,7 +77,7 @@ class SendExpirationAlerts extends Command
$this->info(count($expiring_licenses).' expiring licenses');
$license_data['count'] = count($expiring_licenses);
$license_data['count'] = $expiring_licenses->count();
$license_data['email_content'] = '';
foreach ($expiring_licenses as $license) {
+62 -2
View File
@@ -235,7 +235,7 @@ class Helper
*/
public static function statusLabelList()
{
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('deployable', 'desc')
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('default_label', 'desc')->orderBy('name','asc')->orderBy('deployable','desc')
->pluck('name', 'id')->toArray();
return $statuslabel_list;
}
@@ -690,7 +690,7 @@ class Helper
$array['status'] = $status;
$array['messages'] = $messages;
if (($messages) && (count($messages) > 0)) {
if (($messages) && (is_array($messages)) && (count($messages) > 0)) {
$array['messages'] = $messages;
}
($payload) ? $array['payload'] = $payload : $array['payload'] = null;
@@ -787,6 +787,66 @@ class Helper
}
public static function filetype_icon($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return "fa fa-file-image-o";
break;
case 'doc':
case 'docx':
return "fa fa-file-word-o";
break;
case 'xls':
case 'xlsx':
return "fa fa-file-excel-o";
break;
case 'zip':
case 'rar':
return "fa fa-file-archive-o";
break;
case 'pdf':
return "fa fa-file-pdf-o";
break;
case 'txt':
return "fa fa-file-text-o";
break;
case 'lic':
return "fa fa-floppy-o";
break;
default:
return "fa fa-file-o";
}
}
return "fa fa-file-o";
}
public static function show_file_inline($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return true;
break;
default:
return false;
}
}
return false;
}
}
+12 -24
View File
@@ -12,7 +12,6 @@ use DB;
use Gate;
use Input;
use Lang;
use Mail;
use Redirect;
use Illuminate\Http\Request;
use Slack;
@@ -259,10 +258,17 @@ class AccessoriesController extends Controller
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
$this->authorize('checkout', $accessory);
if ($accessory->category) {
$this->authorize('checkout', $accessory);
// Get the dropdown of users and then pass it to the checkout view
return view('accessories/checkout', compact('accessory'));
}
return redirect()->back()->with('error', 'The category type for this accessory is not valid. Edit the accessory and select a valid accessory category.');
// Get the dropdown of users and then pass it to the checkout view
return view('accessories/checkout', compact('accessory'));
}
@@ -313,16 +319,6 @@ class AccessoriesController extends Controller
$data['expected_checkin'] = '';
$data['note'] = $logaction->note;
$data['require_acceptance'] = $accessory->requireAcceptance();
// TODO: Port this to new mail notifications
if ((($accessory->requireAcceptance()=='1') || ($accessory->getEula())) && ($user->email!='')) {
Mail::send('emails.accept-accessory', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_accessory_delivery'));
});
}
// Redirect to the new accessory page
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
@@ -369,7 +365,7 @@ class AccessoriesController extends Controller
// Check if the accessory exists
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
$accessory = Accessory::find($accessory_user->accessory_id);
@@ -387,20 +383,12 @@ class AccessoriesController extends Controller
$data['log_id'] = $logaction->id;
$data['first_name'] = e($user->first_name);
$data['last_name'] = e($user->last_name);
$data['item_name'] = e($accessory->name);
$data['checkin_date'] = e($logaction->created_at);
$data['item_tag'] = '';
$data['note'] = e($logaction->note);
if ((($accessory->checkin_email()=='1')) && ($user->email!='')) {
Mail::send('emails.checkin-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_Accessory_Checkin'));
});
}
if ($backto=='user') {
return redirect()->route("users.show", $return_to)->with('success', trans('admin/accessories/message.checkin.success'));
}
@@ -56,6 +56,8 @@ class AssetMaintenancesController extends Controller
'start_date',
'completion_date',
'notes',
'asset_tag',
'asset_name',
'user_id'
];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
@@ -65,6 +67,12 @@ class AssetMaintenancesController extends Controller
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
case 'asset_tag':
$maintenances = $maintenances->OrderByTag($order);
break;
case 'asset_name':
$maintenances = $maintenances->OrderByAssetName($order);
break;
default:
$maintenances = $maintenances->orderBy($sort, $order);
break;
@@ -32,7 +32,21 @@ class AssetModelsController extends Controller
$this->authorize('view', AssetModel::class);
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','assets_count'];
$assetmodels = AssetModel::select(['models.id','models.image','models.name','model_number','eol','models.notes','models.created_at','category_id','manufacturer_id','depreciation_id','fieldset_id', 'models.deleted_at'])
$assetmodels = AssetModel::select([
'models.id',
'models.image',
'models.name',
'model_number',
'eol',
'models.notes',
'models.created_at',
'category_id',
'manufacturer_id',
'depreciation_id',
'fieldset_id',
'models.deleted_at',
'models.updated_at',
])
->with('category','depreciation', 'manufacturer','fieldset')
->withCount('assets');
@@ -137,7 +151,7 @@ class AssetModelsController extends Controller
$assetmodel->fieldset_id = $request->get("custom_fieldset_id");
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/assetmodels/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
@@ -170,7 +184,7 @@ class AssetModelsController extends Controller
}
$assetmodel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/assetmodels/message.delete.success')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.delete.success')));
}
+103 -36
View File
@@ -77,12 +77,15 @@ class AssetsController extends Controller
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkouts_count',
'checkins_count',
'user_requests_count',
];
$filter = array();
if ($request->has('filter')) {
$filter = json_decode($request->input('filter'));
$filter = json_decode($request->input('filter'), true);
}
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
@@ -92,11 +95,7 @@ class AssetsController extends Controller
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->withCount('checkins', 'checkouts', 'userRequests');
// These are used by the API to query against specific ID numbers.
@@ -106,6 +105,10 @@ class AssetsController extends Controller
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
if ($request->input('requestable')=='true') {
$assets->where('assets.requestable', '=', '1');
}
if ($request->has('model_id')) {
$assets->InModelList([$request->input('model_id')]);
}
@@ -116,7 +119,6 @@ class AssetsController extends Controller
if ($request->has('location_id')) {
$assets->where('assets.location_id', '=', $request->input('location_id'));
// dd($assets->toSql());
}
if ($request->has('supplier_id')) {
@@ -215,7 +217,8 @@ class AssetsController extends Controller
}
if (count($filter) > 0) {
if ((!is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter);
} elseif ($request->has('search')) {
$assets->TextSearch($request->input('search'));
@@ -274,6 +277,44 @@ class AssetsController extends Controller
}
/**
* Returns JSON with information about an asset (by tag) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $tag
* @since [v4.2.1]
* @return JsonResponse
*/
public function showByTag($tag)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->where('asset_tag',$tag)->first()) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 404);
}
/**
* Returns JSON with information about an asset (by serial) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $serial
* @since [v4.2.1]
* @return JsonResponse
*/
public function showBySerial($serial)
{
if ($assets = Asset::with('assetstatus')->with('assignedTo')
->withTrashed()->where('serial',$serial)->get()) {
$this->authorize('view', $assets);
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 404);
}
/**
* Returns JSON with information about an asset for detail view.
*
@@ -284,11 +325,12 @@ class AssetsController extends Controller
*/
public function show($id)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->findOrFail($id)) {
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->withCount('checkins', 'checkouts', 'userRequests')->findOrFail($id)) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
}
@@ -313,6 +355,9 @@ class AssetsController extends Controller
'assets.status_id'
])->with('model', 'assetstatus', 'assignedTo')->NotArchived());
if ($request->has('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
}
if ($request->has('search')) {
$assets = $assets->AssignedSearch($request->input('search'));
@@ -420,7 +465,7 @@ class AssetsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('create', Asset::class);
$this->authorize('edit', Asset::class);
if ($asset = Asset::find($id)) {
($request->has('model_id')) ?
@@ -629,42 +674,19 @@ class AssetsController extends Controller
$asset->assigned_to = null;
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
$asset->name = e(Input::get('name'));
$asset->name = Input::get('name');
$asset->location_id = $asset->rtd_location_id;
if ($request->has('location_id')) {
$asset->location_id = $request->input('location_id');
}
$asset->location_id = $asset->rtd_location_id;
if (Input::has('status_id')) {
$asset->status_id = e(Input::get('status_id'));
$asset->status_id = Input::get('status_id');
}
// Was the asset updated?
if ($asset->save()) {
$logaction = $asset->logCheckin($target, e(request('note')));
$data['log_id'] = $logaction->id;
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
$data['item_name'] = $asset->present()->name();
$data['checkin_date'] = $logaction->created_at;
$data['item_tag'] = $asset->asset_tag;
$data['item_serial'] = $asset->serial;
$data['note'] = $logaction->note;
$data['manufacturer_name'] = $asset->model->manufacturer->name;
$data['model_name'] = $asset->model->name;
$data['model_number'] = $asset->model->model_number;
if ((($asset->checkin_email()=='1')) && (isset($user)) && (!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'));
$m->subject(trans('mail.Confirm_Asset_Checkin'));
});
}
$asset->logCheckin($target, e(request('note')));
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
}
@@ -720,5 +742,50 @@ class AssetsController extends Controller
}
/**
* Returns JSON listing of all requestable assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return JsonResponse
*/
public function requestable(Request $request)
{
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$assets->TextSearch($request->input('search'));
switch ($request->input('sort')) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
default:
$assets->orderBy('assets.created_at', $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
}
@@ -35,6 +35,10 @@ class ComponentsController extends Controller
$components->where('company_id','=',$request->input('company_id'));
}
if ($request->has('category_id')) {
$components->where('category_id','=',$request->input('category_id'));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
@@ -93,13 +97,11 @@ class ComponentsController extends Controller
public function show($id)
{
$this->authorize('view', Component::class);
$component = Component::find($id);
$component = Component::findOrFail($id);
if ($component) {
return (new ComponentsTransformer)->transformComponent($component);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist')));
}
@@ -179,7 +179,7 @@ class ConsumablesController extends Controller
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
'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'),
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
];
}
@@ -26,9 +26,7 @@ class CustomFieldsController extends Controller
{
$this->authorize('index', CustomFields::class);
$fields = CustomField::get();
$total = count($fields);
return (new CustomFieldsTransformer)->transformCustomFields($fields, $total);
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
}
/**
@@ -44,9 +44,7 @@ class CustomFieldsetsController extends Controller
{
$this->authorize('index', CustomFieldset::class);
$fieldsets = CustomFieldset::withCount(['fields', 'models'])->get();
$total = count($fieldsets);
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $total);
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
}
@@ -156,8 +154,26 @@ class CustomFieldsetsController extends Controller
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($id);
$fields = $set->fields->get();
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
}
/**
* Return JSON containing a list of fields belonging to a fieldset with the
* default values for a given model
*
* @param $modelId
* @param $fieldsetId
* @return string JSON
*/
public function fieldsWithDefaultValues($fieldsetId, $modelId)
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($fieldsetId);
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFieldsWithDefaultValues($fields, $modelId, $fields->count());
}
}
+30 -5
View File
@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Session;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Artisan;
use App\Models\Asset;
class ImportController extends Controller
{
@@ -57,6 +58,35 @@ class ImportController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
}
//TODO: is there a lighter way to do this?
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
$import->header_row = $reader->fetchOne(0);
//duplicate headers check
$duplicate_headers = [];
for($i = 0; $i<count($import->header_row); $i++) {
$header = $import->header_row[$i];
if(in_array($header, $import->header_row)) {
$found_at = array_search($header, $import->header_row);
if($i > $found_at) {
//avoid reporting duplicates twice, e.g. "1 is same as 17! 17 is same as 1!!!"
//as well as "1 is same as 1!!!" (which is always true)
//has to be > because otherwise the first result of array_search will always be $i itself(!)
array_push($duplicate_headers,"Duplicate header '$header' detected, first at column: ".($found_at+1).", repeats at column: ".($i+1));
}
}
}
if(count($duplicate_headers) > 0) {
return response()->json(Helper::formatStandardApiResponse('error',null, implode("; ",$duplicate_headers)), 500); //should this be '4xx'?
}
// Grab the first row to display via ajax as the user picks fields
$import->first_row = $reader->fetchOne(1);
$date = date('Y-m-d-his');
$fixed_filename = str_slug($file->getClientOriginalName());
try {
@@ -71,11 +101,6 @@ class ImportController extends Controller
$file_name = date('Y-m-d-his').'-'.$fixed_filename;
$import->file_path = $file_name;
$import->filesize = filesize($path.'/'.$file_name);
//TODO: is there a lighter way to do this?
$reader = Reader::createFromPath("{$path}/{$file_name}");
$import->header_row = $reader->fetchOne(0);
// Grab the first row to display via ajax as the user picks fields
$import->first_row = $reader->fetchOne(1);
$import->save();
$results[] = $import;
}
+44 -10
View File
@@ -2,13 +2,15 @@
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Models\Company;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Company;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class LicensesController extends Controller
{
@@ -121,6 +123,14 @@ class LicensesController extends Controller
public function store(Request $request)
{
//
$this->authorize('create', License::class);
$license = new License;
$license->fill($request->all());
if($license->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $license->getErrors()));
}
/**
@@ -132,13 +142,10 @@ class LicensesController extends Controller
*/
public function show($id)
{
$license = License::find($id);
if (isset($license->id)) {
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
$this->authorize('view', $license);
return (new LicensesTransformer)->transformLicense($license);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
$this->authorize('view', License::class);
$license = License::findOrFail($id);
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
return (new LicensesTransformer)->transformLicense($license);
}
@@ -154,6 +161,16 @@ class LicensesController extends Controller
public function update(Request $request, $id)
{
//
$this->authorize('edit', License::class);
$license = License::findOrFail($id);
$license->fill($request->all());
if ($license->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.update.success')));
}
return Helper::formatStandardApiResponse('error', null, $license->getErrors());
}
/**
@@ -167,6 +184,23 @@ class LicensesController extends Controller
public function destroy($id)
{
//
$license = License::findOrFail($id);
$this->authorize('delete', $license);
if($license->assigned_seats_count == 0) {
// Delete the license and the associated license seats
DB::table('license_seats')
->where('id', $license->id)
->update(array('assigned_to' => null,'asset_id' => null));
$licenseSeats = $license->licenseseats();
$licenseSeats->delete();
$license->delete();
// Redirect to the licenses management page
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/licenses/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
}
/**
@@ -25,14 +25,20 @@ class ManufacturersController extends Controller
$allowed_columns = ['id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count'];
$manufacturers = Manufacturer::select(
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image')
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'deleted_at')
)->withCount('assets')->withCount('licenses')->withCount('consumables')->withCount('accessories');
if ($request->input('deleted')=='true') {
$manufacturers->onlyTrashed();
}
if ($request->has('search')) {
$manufacturers = $manufacturers->TextSearch($request->input('search'));
}
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\CheckoutRequest;
use App\Http\Controllers\Controller;
use Auth;
use App\Helpers\Helper;
class ProfileController extends Controller
{
/**
* Display a listing of requested assets.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.3.0]
*
* @return Array
*/
public function requestedAssets()
{
$checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
$results = [];
$results['total'] = $checkoutRequests->count();
foreach ($checkoutRequests as $checkoutRequest) {
$results['rows'][] = [
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
'name' => $checkoutRequest->itemRequested()->present()->name(),
'type' => $checkoutRequest->itemType(),
'qty' => $checkoutRequest->quantity,
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
];
}
return $results;
}
}
@@ -28,7 +28,6 @@ class ReportsController extends Controller
if (($request->has('target_type')) && ($request->has('target_id'))) {
$actionlogs = $actionlogs->where('target_id','=',$request->input('target_id'))
->where('target_type','=',"App\\Models\\".ucwords($request->input('target_type')));
}
if (($request->has('item_type')) && ($request->has('item_id'))) {
@@ -40,6 +39,10 @@ class ReportsController extends Controller
$actionlogs = $actionlogs->where('action_type','=',$request->input('action_type'))->orderBy('created_at', 'desc');
}
if ($request->has('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
}
$allowed_columns = [
'id',
'created_at',
@@ -8,6 +8,9 @@ use App\Models\Ldap;
use Validator;
use App\Models\Setting;
use Mail;
use App\Notifications\SlackTest;
use Notification;
use App\Notifications\MailTest;
class SettingsController extends Controller
{
@@ -96,6 +99,29 @@ class SettingsController extends Controller
}
public function slacktest()
{
if ($settings = Setting::getSettings()->slack_channel=='') {
\Log::debug('Slack is not enabled. Cannot test.');
return response()->json(['message' => 'Slack is not enabled, cannot test.'], 400);
}
\Log::debug('Preparing to test slack connection');
try {
Notification::send($settings = Setting::getSettings(), new SlackTest());
return response()->json(['message' => 'Success'], 200);
} catch (\Exception $e) {
\Log::debug('Slack connection failed');
return response()->json(['message' => $e->getMessage()], 400);
}
}
/**
* Test the email configuration
*
@@ -107,11 +133,7 @@ class SettingsController extends Controller
{
if (!config('app.lock_passwords')) {
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
Notification::send(Setting::first(), new MailTest());
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
@@ -22,7 +22,7 @@ class StatuslabelsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Statuslabel::class);
$allowed_columns = ['id','name','created_at', 'assets_count','color'];
$allowed_columns = ['id','name','created_at', 'assets_count','color','default_label'];
$statuslabels = Statuslabel::withCount('assets');
@@ -56,7 +56,7 @@ class StatuslabelsController extends Controller
$request->except('deployable', 'pending','archived');
if (!$request->has('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]));
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]),500);
}
$statuslabel = new Statuslabel;
@@ -25,7 +25,7 @@ class SuppliersController extends Controller
$allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count'];
$suppliers = Supplier::select(
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image')
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
+53 -21
View File
@@ -28,30 +28,32 @@ class UsersController extends Controller
$this->authorize('view', User::class);
$users = User::select([
'users.id',
'users.employee_num',
'users.two_factor_enrolled',
'users.jobtitle',
'users.email',
'users.phone',
'users.activated',
'users.address',
'users.avatar',
'users.city',
'users.state',
'users.country',
'users.zip',
'users.username',
'users.location_id',
'users.manager_id',
'users.first_name',
'users.last_name',
'users.created_at',
'users.notes',
'users.company_id',
'users.last_login',
'users.country',
'users.created_at',
'users.deleted_at',
'users.department_id',
'users.activated',
'users.avatar',
'users.email',
'users.employee_num',
'users.first_name',
'users.id',
'users.jobtitle',
'users.last_login',
'users.last_name',
'users.location_id',
'users.manager_id',
'users.notes',
'users.permissions',
'users.phone',
'users.state',
'users.two_factor_enrolled',
'users.updated_at',
'users.username',
'users.zip',
])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables')
->withCount('assets','licenses','accessories','consumables');
@@ -69,7 +71,7 @@ class UsersController extends Controller
if ($request->has('location_id')) {
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
if ($request->has('group_id')) {
$users = $users->ByGroup($request->get('group_id'));
}
@@ -192,7 +194,9 @@ class UsersController extends Controller
$this->authorize('view', User::class);
$user = new User;
$user->fill($request->all());
$user->password = bcrypt($request->input('password'));
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$user->password = bcrypt($request->get('password', $tmp_pass));
if ($user->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
@@ -288,4 +292,32 @@ class UsersController extends Controller
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
/**
* Reset the user's two-factor status
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $userId
* @return string JSON
*/
public function postTwoFactorReset(Request $request)
{
$this->authorize('edit', User::class);
if ($request->has('id')) {
try {
$user = User::find($request->get('id'));
$user->two_factor_secret = null;
$user->two_factor_enrolled = 0;
$user->save();
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
} catch (\Exception $e) {
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
}
}
return response()->json(['message' => 'No ID provided'], 500);
}
}
+51 -2
View File
@@ -110,6 +110,10 @@ class AssetModelsController extends Controller
// Was it created?
if ($model->save()) {
if ($this->shouldAddDefaultValues($request->input())) {
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
}
// Redirect to the new model page
return redirect()->route("models.index")->with('success', trans('admin/models/message.create.success'));
}
@@ -166,7 +170,7 @@ class AssetModelsController extends Controller
*/
public function edit($modelId = null)
{
$this->authorize('edit', AssetModel::class);
$this->authorize('update', AssetModel::class);
if ($item = AssetModel::find($modelId)) {
$category_type = 'asset';
$view = View::make('models/edit', compact('item','category_type'));
@@ -190,7 +194,7 @@ class AssetModelsController extends Controller
*/
public function update(ImageUploadRequest $request, $modelId = null)
{
$this->authorize('edit', AssetModel::class);
$this->authorize('update', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
// Redirect to the models management page
@@ -206,10 +210,16 @@ class AssetModelsController extends Controller
$model->notes = $request->input('notes');
$model->requestable = $request->input('requestable', '0');
$this->removeCustomFieldsDefaultValues($model);
if ($request->input('custom_fieldset')=='') {
$model->fieldset_id = null;
} else {
$model->fieldset_id = $request->input('custom_fieldset');
if ($this->shouldAddDefaultValues($request->input())) {
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
}
}
$old_image = $model->image;
@@ -531,4 +541,43 @@ class AssetModelsController extends Controller
}
/**
* Returns true if a fieldset is set, 'add default values' is ticked and if
* any default values were entered into the form.
*
* @param array $input
* @return boolean
*/
private function shouldAddDefaultValues(array $input)
{
return !empty($input['add_default_values'])
&& !empty($input['default_values'])
&& !empty($input['custom_fieldset']);
}
/**
* Adds default values to a model (as long as they are truthy)
*
* @param AssetModel $model
* @param array $defaultValues
* @return void
*/
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues)
{
foreach ($defaultValues as $customFieldId => $defaultValue) {
if ($defaultValue) {
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
}
}
}
/**
* Removes all default values
*
* @return void
*/
private function removeCustomFieldsDefaultValues(AssetModel $model)
{
$model->defaultValues()->detach();
}
}
+84 -45
View File
@@ -302,8 +302,13 @@ class AssetsController extends Controller
if ($request->has('image_delete')) {
unlink(public_path().'/uploads/assets/'.$asset->image);
$asset->image = '';
try {
unlink(public_path().'/uploads/assets/'.$asset->image);
$asset->image = '';
} catch (\Exception $e) {
\Log::error($e);
}
}
@@ -460,8 +465,14 @@ class AssetsController extends Controller
$asset->location_id = ($target) ? $target->id : '';
} elseif (request('checkout_to_type')=='asset') {
if (request('assigned_asset') == $assetId) {
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
}
$target = Asset::where('id','!=',$assetId)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id;
// Override with the asset's location_id if it has one
if ($target->location_id!='') {
$asset->location_id = ($target) ? $target->location_id : '';
@@ -585,14 +596,6 @@ class AssetsController extends Controller
$data['model_name'] = $asset->model->name;
$data['model_number'] = $asset->model->model_number;
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'));
$m->subject(trans('mail.Confirm_Asset_Checkin'));
});
}
if ($backto=='user') {
return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
}
@@ -950,8 +953,7 @@ class AssetsController extends Controller
if ($request->hasFile('assetfile')) {
foreach ($request->file('assetfile') as $file) {
$extension = $file->getClientOriginalExtension();
$filename = 'hardware-'.$asset->id.'-'.str_random(8);
$filename .= '-'.str_slug($file->getClientOriginalName()).'.'.$extension;
$filename = 'hardware-'.$asset->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
$file->move($destinationPath, $filename);
$asset->logUpload($filename, e(Input::get('notes')));
}
@@ -1002,28 +1004,43 @@ class AssetsController extends Controller
* @since [v1.0]
* @return View
*/
public function displayFile($assetId = null, $fileId = null)
public function displayFile($assetId = null, $fileId = null, $download = true)
{
$asset = Asset::find($assetId);
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
$log = Actionlog::find($fileId);
$file = $log->get_src('assets');
$filetype = Helper::checkUploadIsImage($file);
if (!$log = Actionlog::find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
if ($filetype) {
$contents = file_get_contents($file);
return Response::make($contents)->header('Content-Type', $filetype);
$file = $log->get_src('assets');
if ($log->action_type =='audit') {
$file = $log->get_src('audits');
}
if (!file_exists($file)) {
return response('File '.$file.' not found on server', 404)
->header('Content-Type', 'text/plain');
}
if ($download != 'true') {
if ($contents = file_get_contents($file)) {
return Response::make($contents)->header('Content-Type', $filetype);
}
return JsonResponse::create(["error" => "Failed validation: "], 500);
}
return Response::download($file);
}
// Prepare the error message
$error = trans('admin/hardware/message.does_not_exist', compact('id'));
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', $error);
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist', compact('id')));
}
/**
@@ -1200,23 +1217,25 @@ class AssetsController extends Controller
public function postBulkCheckout(Request $request)
{
$this->validate($request, [
"assigned_to" => 'required'
]);
$user = User::find(e(Input::get('assigned_to')));
$admin = Auth::user();
if (!$user) {
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.user_does_not_exist'));
// Find checkout to type
if (request('checkout_to_type')=='location') {
$target = Location::find(request('assigned_location'));
} elseif (request('checkout_to_type')=='asset') {
$target = Asset::find(request('assigned_asset'));
} elseif (request('checkout_to_type')=='user') {
$target = User::find(request('assigned_user'));
}
if (!is_array(Input::get('selected_assets'))) {
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
}
$asset_ids = array_filter(Input::get('selected_assets'));
foreach ($asset_ids as $asset_id) {
if ($target->id == $asset_id) {
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
}
}
if ((Input::has('checkout_at')) && (Input::get('checkout_at')!= date("Y-m-d"))) {
$checkout_at = e(Input::get('checkout_at'));
} else {
@@ -1231,21 +1250,19 @@ class AssetsController extends Controller
$errors = [];
DB::transaction(function () use ($user, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids) {
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids) {
foreach ($asset_ids as $asset_id) {
$asset = Asset::find($asset_id);
$this->authorize('checkout', $asset);
$error = $asset->checkOut($user, $admin, $checkout_at, $expected_checkin, e(Input::get('note')), null);
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e(Input::get('note')), null);
if ($user->location_id!='') {
$asset->location_id = $user->location_id;
if ($target->location_id!='') {
$asset->location_id = $target->location_id;
$asset->unsetEventDispatcher();
$asset->save();
}
if ($error) {
array_merge_recursive($errors, $asset->getErrors()->toArray());
}
@@ -1280,7 +1297,7 @@ class AssetsController extends Controller
}
public function auditStore(Request $request, $id)
public function auditStore(AssetFileRequest $request, $id)
{
$this->authorize('audit', Asset::class);
@@ -1290,11 +1307,13 @@ class AssetsController extends Controller
);
$validator = \Validator::make($request->all(), $rules);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
}
$asset = Asset::findOrFail($id);
// We don't want to log this as a normal update, so let's bypass that
$asset->unsetEventDispatcher();
@@ -1302,17 +1321,37 @@ class AssetsController extends Controller
$asset->last_audit_date = date('Y-m-d h:i:s');
if ($asset->save()) {
$asset->logAudit(request('note'), request('location_id'));
$filename = '';
if ($request->hasFile('image')) {
$file = $request->file('image');
try {
$destinationPath = config('app.private_uploads').'/audits';
$extension = $file->getClientOriginalExtension();
$filename = 'audit-'.$asset->id.'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
$file->move($destinationPath, $filename);
} catch (\Exception $e) {
\Log::error($e);
}
}
$asset->logAudit($request->input('note'), $request->input('location_id'), $filename);
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.audit.success'));
}
}
public function getRequestedIndex($id = null)
public function getRequestedIndex($user_id = null)
{
if ($id) {
$requestedItems = CheckoutRequest::where('user_id', $id)->with('user', 'requestedItem')->get();
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->whereNull('canceled_at')->with('user', 'requestedItem');
if ($user_id) {
$requestedItems->where('user_id', $user_id)->get();
}
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->get();
$requestedItems = $requestedItems->orderBy('created_at', 'desc')->get();
return view('hardware/requested', compact('requestedItems'));
}
+35 -4
View File
@@ -50,17 +50,36 @@ class LoginController extends Controller
\Session::put('backUrl', \URL::previous());
}
function showLoginForm()
function showLoginForm(Request $request)
{
$this->loginViaRemoteUser($request);
if (Auth::check()) {
return redirect()->intended('dashboard');
}
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
return view('auth.login');
}
private function loginViaRemoteUser(Request $request)
{
$remote_user = $request->server('REMOTE_USER');
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
LOG::debug("Authenticatiing via REMOTE_USER.");
try {
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->first();
LOG::debug("Remote user auth lookup complete");
if(!is_null($user)) Auth::login($user, true);
} catch(Exception $e) {
LOG::error("There was an error authenticating the Remote user: " . $e->getMessage());
}
}
}
private function login_via_ldap(Request $request)
private function loginViaLdap(Request $request)
{
LOG::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
@@ -114,6 +133,10 @@ class LoginController extends Controller
*/
public function login(Request $request)
{
if (Setting::getSettings()->login_common_disabled == "1") {
return view('errors.403');
}
$validator = $this->validator(Input::all());
if ($validator->fails()) {
@@ -134,7 +157,7 @@ class LoginController extends Controller
if (Setting::getSettings()->ldap_enabled=='1') {
LOG::debug("LDAP is enabled.");
try {
$user = $this->login_via_ldap($request);
$user = $this->loginViaLdap($request);
Auth::login($user, true);
// If the user was unable to login via LDAP, log the error and let them fall through to
@@ -252,7 +275,15 @@ class LoginController extends Controller
public function logout(Request $request)
{
$request->session()->forget('2fa_authed');
Auth::logout();
$settings = Setting::getSettings();
$customLogoutUrl = $settings->login_remote_user_custom_logout_url ;
if ($settings->login_remote_user_enabled == '1' && $customLogoutUrl != '') {
return redirect()->away($customLogoutUrl);
}
return redirect()->route('login')->with('success', 'You have successfully logged out!');
}
@@ -194,7 +194,7 @@ class ComponentsController extends Controller
public function destroy($componentId)
{
if (is_null($component = Component::find($componentId))) {
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
$this->authorize('delete', $component);
@@ -7,13 +7,11 @@ use App\Models\Company;
use App\Models\Consumable;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckoutNotification;
use Auth;
use Config;
use DB;
use Input;
use Lang;
use Mail;
use Redirect;
use Slack;
use Str;
@@ -279,14 +277,6 @@ class ConsumablesController extends Controller
$data['note'] = $logaction->note;
$data['require_acceptance'] = $consumable->requireAcceptance();
if ((($consumable->requireAcceptance()=='1') || ($consumable->getEula())) && $user->email!='') {
Mail::send('emails.accept-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_consumable_delivery'));
});
}
// Redirect to the new consumable page
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
@@ -78,6 +78,7 @@ class CustomFieldsController extends Controller
"help_text" => $request->get("help_text"),
"field_values" => $request->get("field_values"),
"field_encrypted" => $request->get("field_encrypted", 0),
"show_in_email" => $request->get("show_in_email", 0),
"user_id" => Auth::user()->id
]);
@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
use App\Http\Transformers\ImportsTransformer;
use App\Models\Import;
use Illuminate\Http\Request;
use App\Models\Asset;
class ImportsController extends Controller
{
+28 -4
View File
@@ -419,6 +419,8 @@ class LicensesController extends Controller
if (!$return_to) {
$return_to = Asset::find($licenseSeat->asset_id);
}
\Log::debug($licenseSeat->assigned_to);
// Update the asset data
$licenseSeat->assigned_to = null;
$licenseSeat->asset_id = null;
@@ -506,7 +508,7 @@ class LicensesController extends Controller
foreach (Input::file('licensefile') as $file) {
$rules = array(
'licensefile' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar,rtf,xml,lic|max:2000'
'licensefile' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar,rtf,xml,lic'
);
$validator = Validator::make(array('licensefile'=> $file), $rules);
@@ -514,8 +516,7 @@ class LicensesController extends Controller
return redirect()->back()->with('error', trans('admin/licenses/message.upload.invalidfiles'));
}
$extension = $file->getClientOriginalExtension();
$filename = 'license-'.$license->id.'-'.str_random(8);
$filename .= '-'.str_slug($file->getClientOriginalName()).'.'.$extension;
$filename = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
$upload_success = $file->move($destinationPath, $filename);
//Log the upload to the log
@@ -581,7 +582,7 @@ class LicensesController extends Controller
* @param int $fileId
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function displayFile($licenseId = null, $fileId = null)
public function displayFile($licenseId = null, $fileId = null, $download = true)
{
$license = License::find($licenseId);
@@ -591,8 +592,31 @@ class LicensesController extends Controller
$this->authorize('view', $license);
$log = Actionlog::find($fileId);
$file = $log->get_src('licenses');
if ($file =='') {
return response('File not found on server', 404)
->header('Content-Type', 'text/plain');
}
$mimetype = \File::mimeType($file);
if (!file_exists($file)) {
return response('File '.$file.' not found on server', 404)
->header('Content-Type', 'text/plain');
}
if ($download != 'true') {
if ($contents = file_get_contents($file)) {
return Response::make($contents)->header('Content-Type', $mimetype);
}
return JsonResponse::create(["error" => "Failed validation: "], 500);
}
return Response::download($file);
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
}
@@ -242,6 +242,32 @@ class ManufacturersController extends Controller
return redirect()->route('manufacturers.index')->with('error', $error);
}
/**
* Restore a given Manufacturer (mark as un-deleted)
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.1.15]
* @param int $manufacturers_id
* @return Redirect
*/
public function restore($manufacturers_id)
{
$this->authorize('create', Manufacturer::class);
$manufacturer = Manufacturer::onlyTrashed()->where('id',$manufacturers_id)->first();
if ($manufacturer) {
// Not sure why this is necessary - it shouldn't fail validation here, but it fails without this, so....
$manufacturer->setValidating(false);
if ($manufacturer->restore()) {
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.restore.success'));
}
return redirect()->back()->with('error', 'Could not restore.');
}
return redirect()->back()->with('error', trans('admin/manufacturers/message.does_not_exist'));
}
+57 -130
View File
@@ -82,136 +82,6 @@ class ReportsController extends Controller
return $response;
}
/**
* Display asset report view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @return View
*/
public function getAssetsReport()
{
$settings = \App\Models\Setting::first();
return view('reports/asset', compact('assets'))->with('settings', $settings);
}
/**
* Exports the assets to CSV
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @return \Illuminate\Http\Response
*/
public function exportAssetReport(Request $request)
{
\Debugbar::disable();
$customfields = CustomField::get();
$response = new StreamedResponse(function () use ($customfields, $request) {
// Open output stream
$handle = fopen('php://output', 'w');
$assets = Asset::with('assignedTo', 'location','defaultLoc','assignedTo','model','supplier','assetstatus','model.manufacturer');
// This is used by the sidenav, mostly
switch ($request->input('status')) {
case 'Deleted':
$assets->withTrashed()->Deleted();
break;
case 'Pending':
$assets->Pending();
break;
case 'RTD':
$assets->RTD();
break;
case 'Undeployable':
$assets->Undeployable();
break;
case 'Archived':
$assets->Archived();
break;
case 'Requestable':
$assets->RequestableAssets();
break;
case 'Deployed':
$assets->Deployed();
break;
}
$headers=[
trans('general.company'),
trans('admin/hardware/table.asset_tag'),
trans('admin/hardware/form.manufacturer'),
trans('general.category'),
trans('admin/hardware/form.model'),
trans('general.model_no'),
trans('general.name'),
trans('admin/hardware/table.serial'),
trans('general.status'),
trans('admin/hardware/table.purchase_date'),
trans('admin/hardware/table.purchase_cost'),
trans('admin/hardware/form.order'),
trans('general.supplier'),
trans('admin/hardware/table.checkoutto'),
trans('general.type'),
trans('admin/hardware/table.checkout_date'),
trans('admin/hardware/table.location'),
trans('general.notes'),
];
foreach ($customfields as $field) {
$headers[]=$field->name;
}
fputcsv($handle, $headers);
$assets->orderBy('created_at', 'DESC')->chunk(500, function($assets) use($handle, $customfields) {
foreach ($assets as $asset) {
// Add a new row with data
$values=[
($asset->company) ? $asset->company->name : '',
$asset->asset_tag,
($asset->model->manufacturer) ? $asset->model->manufacturer->name : '',
($asset->model->category) ? $asset->model->category->name : '',
($asset->model) ? $asset->model->name : '',
($asset->model->model_number) ? $asset->model->model_number : '',
($asset->name) ? $asset->name : '',
($asset->serial) ? $asset->serial : '',
($asset->assetstatus) ? e($asset->present()->statusText) : '',
($asset->purchase_date) ? e($asset->purchase_date) : '',
($asset->purchase_cost > 0) ? Helper::formatCurrencyOutput($asset->purchase_cost) : '',
($asset->order_number) ? e($asset->order_number) : '',
($asset->supplier) ? e($asset->supplier->name) : '',
($asset->checkedOutToUser() && $asset->assigned) ? e($asset->assigned->getFullNameAttribute()) : ($asset->assigned ? e($asset->assigned->display_name) : ''),
($asset->checkedOutToUser() && $asset->assigned) ? 'user' : e($asset->assignedType()),
($asset->last_checkout!='') ? e($asset->last_checkout) : '',
($asset->location) ? e($asset->location->name) : '',
($asset->notes) ? e($asset->notes) : '',
];
foreach ($customfields as $field) {
$values[]=$asset->{$field->db_column_name()};
}
fputcsv($handle, $values);
}
});
// Close the output stream
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition'
=> 'attachment; filename="'.(($request->has('status')) ? trim($request->input('status')) : 'all').'-assets-'.date('Y-m-d-his').'.csv"',
]);
return $response;
}
/**
* Show depreciation report for assets.
*
@@ -494,6 +364,27 @@ class ReportsController extends Controller
if ($request->has('location')) {
$header[] = trans('admin/hardware/table.location');
}
if ($request->has('location_address')) {
$header[] = trans('general.address');
$header[] = trans('general.address');
$header[] = trans('general.city');
$header[] = trans('general.state');
$header[] = trans('general.country');
$header[] = trans('general.zip');
}
if ($request->has('rtd_location')) {
$header[] = trans('admin/hardware/form.default_location');
}
if ($request->has('rtd_location_address')) {
$header[] = trans('general.address');
$header[] = trans('general.address');
$header[] = trans('general.city');
$header[] = trans('general.state');
$header[] = trans('general.country');
$header[] = trans('general.zip');
}
if ($request->has('assigned_to')) {
$header[] = trans('admin/hardware/table.checkoutto');
@@ -508,6 +399,10 @@ class ReportsController extends Controller
$header[] = 'Employee No.';
}
if ($request->has('department')) {
$header[] = trans('general.department');
}
if ($request->has('status')) {
$header[] = trans('general.status');
}
@@ -662,6 +557,29 @@ class ReportsController extends Controller
$row[] = ($asset->location) ? $asset->location->present()->name() : '';
}
if ($request->has('location_address')) {
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address2 : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->city : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->state : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->country : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->zip : '';
}
if ($request->has('rtd_location')) {
$row[] = ($asset->location) ? $asset->defaultLoc->present()->name() : '';
}
if ($request->has('rtd_location_address')) {
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address2 : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->city : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->state : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->country : '';
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->zip : '';
}
if ($request->has('assigned_to')) {
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? e($asset->assigned->getFullNameAttribute()) : ($asset->assigned ? e($asset->assigned->display_name) : '');
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : e($asset->assignedType());
@@ -685,6 +603,15 @@ class ReportsController extends Controller
}
}
if ($request->has('department')) {
if ($asset->checkedOutToUser()) {
$row[] = (($asset->assignedto) && ($asset->assignedto->department)) ? $asset->assignedto->department->name : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
if ($request->has('status')) {
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
}
+25 -5
View File
@@ -22,6 +22,7 @@ use App\Http\Requests\SetupUserRequest;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SettingsLdapRequest;
use App\Helpers\Helper;
use App\Notifications\FirstAdminNotification;
/**
* This controller handles all actions related to Settings for
@@ -57,9 +58,10 @@ class SettingsController extends Controller
$protocol = array_key_exists('HTTPS', $_SERVER) && ( $_SERVER['HTTPS'] == "on") ? 'https://' : 'http://';
$host = $_SERVER['SERVER_NAME'];
if (($protocol === 'http://' && $_SERVER['SERVER_PORT'] != '80') || ($protocol === 'https://' && $_SERVER['SERVER_PORT'] != '443')) {
$host .= ':' . $_SERVER['SERVER_PORT'];
$host = array_key_exists('SERVER_NAME', $_SERVER) ? $_SERVER['SERVER_NAME'] : null;
$port = array_key_exists('SERVER_PORT', $_SERVER) ? $_SERVER['SERVER_PORT'] : null;
if (($protocol === 'http://' && $port != '80') || ($protocol === 'https://' && $port != '443')) {
$host .= ':' . $port;
}
$pageURL = $protocol . $host . $_SERVER['REQUEST_URI'];
@@ -185,11 +187,19 @@ class SettingsController extends Controller
$settings->save();
if (Input::get('email_creds')=='1') {
Mail::send(['text' => 'emails.firstadmin'], $data, function ($m) use ($data) {
$data = array();
$data['email'] = $user->email;
$data['username'] = $user->username;
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['password'] = $request->input('password');
$user->notify(new FirstAdminNotification($data));
/*Mail::send(['text' => 'emails.firstadmin'], $data, function ($m) use ($data) {
$m->to($data['email'], $data['first_name']);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.your_credentials'));
});
});*/
}
@@ -324,6 +334,7 @@ class SettingsController extends Controller
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
$setting->load_remote = $request->input('load_remote', '0');
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
$setting->dashboard_message = $request->input('dashboard_message');
$setting->email_domain = $request->input('email_domain');
@@ -385,6 +396,7 @@ class SettingsController extends Controller
$setting->header_color = $request->input('header_color');
$setting->support_footer = $request->input('support_footer');
$setting->footer_text = $request->input('footer_text');
$setting->skin = $request->input('skin');
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
@@ -473,6 +485,11 @@ class SettingsController extends Controller
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
$setting->pwd_secure_complexity = '';
# remote user login
$setting->login_remote_user_enabled = (int)$request->input('login_remote_user_enabled');
$setting->login_common_disabled= (int)$request->input('login_common_disabled');
$setting->login_remote_user_custom_logout_url = $request->input('login_remote_user_custom_logout_url');
if ($request->has('pwd_secure_complexity')) {
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
}
@@ -560,8 +577,11 @@ class SettingsController extends Controller
$alert_email = rtrim($request->input('alert_email'), ',');
$alert_email = trim($alert_email);
$admin_cc_email = rtrim($request->input('admin_cc_email'), ',');
$admin_cc_email = trim($admin_cc_email);
$setting->alert_email = $alert_email;
$setting->admin_cc_email = $admin_cc_email;
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
$setting->alert_interval = $request->input('alert_interval');
$setting->alert_threshold = $request->input('alert_threshold');
@@ -93,9 +93,9 @@ class StatuslabelsController extends Controller
$statusLabel->archived = $statusType['archived'];
$statusLabel->color = Input::get('color');
$statusLabel->show_in_nav = Input::get('show_in_nav', 0);
$statusLabel->default_label = Input::get('default_label', 0);
// Was the asset created?
if ($statusLabel->save()) {
// Redirect to the new Statuslabel page
return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.create.success'));
@@ -185,6 +185,7 @@ class StatuslabelsController extends Controller
$statuslabel->archived = $statustype['archived'];
$statuslabel->color = Input::get('color');
$statuslabel->show_in_nav = Input::get('show_in_nav', 0);
$statuslabel->default_label = Input::get('default_label', 0);
// Was the asset created?
+40 -48
View File
@@ -34,6 +34,7 @@ use View;
use Illuminate\Http\Request;
use Gate;
use Artisan;
use App\Notifications\WelcomeNotification;
/**
* This controller handles all actions related to Users for
@@ -146,13 +147,16 @@ class UsersController extends Controller
$data['email'] = e($request->input('email'));
$data['username'] = e($request->input('username'));
$data['first_name'] = e($request->input('first_name'));
$data['last_name'] = e($request->input('last_name'));
$data['password'] = e($request->input('password'));
Mail::send('emails.send-login', $data, function ($m) use ($user) {
$user->notify(new WelcomeNotification($data));
/* Mail::send('emails.send-login', $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.welcome', ['name' => $user->first_name]));
});
});*/
}
return redirect::route('users.index')->with('success', trans('admin/users/message.success.create'));
}
@@ -192,15 +196,18 @@ class UsersController extends Controller
// Send the credentials through email
$data = array();
$data['email'] = $request->input('email');
$data['username'] = $request->input('username');
$data['first_name'] = $request->input('first_name');
$data['last_name'] = $request->input('last_name');
$data['last_name'] = e($request->input('last_name'));
$data['password'] = $request->input('password');
Mail::send('emails.send-login', $data, function ($m) use ($user) {
$user->notify(new WelcomeNotification($data));
/*Mail::send('emails.send-login', $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.welcome', ['name' => $user->first_name]));
});
});*/
}
return JsonResponse::create($user);
@@ -385,9 +392,9 @@ class UsersController extends Controller
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
}
if (count($user->assets) > 0) {
if ($user->assets->count() > 0) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . count($user->assets) . ' assets associated with them.');
return redirect()->route('users.index')->with('error', 'This user still has ' . count($user->assets->count()) . ' assets associated with them.');
}
if ($user->licenses()->count() > 0) {
@@ -431,23 +438,19 @@ class UsersController extends Controller
public function postBulkEdit(Request $request)
{
$this->authorize('update', User::class);
if ((!Input::has('ids')) || (count(Input::input('ids')) == 0)) {
return redirect()->back()->with('error', 'No users selected');
} else {
if (($request->has('ids')) && (count($request->input('ids')) > 0)) {
$statuslabel_list = Helper::statusLabelList();
$user_raw_array = array_keys(Input::get('ids'));
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
$users = User::whereIn('id', $user_raw_array)->with('groups', 'assets', 'licenses', 'accessories')->get();
if ($request->input('bulk_actions')=='edit') {
if ($request->input('bulk_actions') == 'edit') {
return view('users/bulk-edit', compact('users'))
->with('groups', Group::pluck('name', 'id'));
}
return view('users/confirm-bulk-delete', compact('users', 'statuslabel_list'));
}
return redirect()->back()->with('error', 'No users selected');
}
@@ -461,15 +464,13 @@ class UsersController extends Controller
public function postBulkEditSave(Request $request)
{
$this->authorize('update', User::class);
if ((!Input::has('ids')) || (count(Input::input('ids')) == 0)) {
return redirect()->back()->with('error', 'No users selected');
} else {
$user_raw_array = Input::get('ids');
if (($request->has('ids')) && (count($request->input('ids')) > 0)) {
$user_raw_array = $request->input('ids');
$update_array = array();
$manager_conflict = false;
$users = User::whereIn('id', $user_raw_array)->where('id','!=',Auth::user()->id)->get();
$users = User::whereIn('id', $user_raw_array)->where('id', '!=', Auth::user()->id)->get();
if ($request->has('location_id')) {
$update_array['location_id'] = $request->input('location_id');
@@ -485,14 +486,12 @@ class UsersController extends Controller
}
if ($request->has('manager_id')) {
// Do not allow a manager update if the selected manager is one of the users being
// edited.
if (!array_key_exists($request->input('manager_id'), $user_raw_array)) {
$update_array['manager_id'] = $request->input('manager_id');
} else {
$manager_conflict = true;
}
@@ -502,8 +501,9 @@ class UsersController extends Controller
$update_array['activated'] = $request->input('activated');
}
// Save the updated info
if (count($update_array) > 0) {
User::whereIn('id', $user_raw_array)->where('id','!=',Auth::user()->id)->update($update_array);
User::whereIn('id', $user_raw_array)->where('id', '!=', Auth::user()->id)->update($update_array);
}
// Only sync groups if groups were selected
@@ -513,13 +513,17 @@ class UsersController extends Controller
}
}
}
if ($manager_conflict) {
if ($manager_conflict) {
return redirect()->route('users.index')
->with('warning', trans('admin/users/message.bulk_manager_warn'));
}
return redirect()->route('users.index')
->with('warning', trans('admin/users/message.bulk_manager_warn'));
->with('success', trans('admin/users/message.success.update_bulk'));
}
return redirect()->route('users.index')
->with('success', trans('admin/users/message.success.update_bulk'));
return redirect()->back()->with('error', 'No users selected');
}
@@ -852,16 +856,21 @@ class UsersController extends Controller
// Send the credentials through email
if ($row[3] != '') {
$data = array();
$data['email'] = trim(e($row[4]));
$data['username'] = trim(e($row[2]));
$data['first_name'] = trim(e($row[0]));
$data['last_name'] = trim(e($row[1]));
$data['password'] = $pass;
if ($newuser['email']) {
Mail::send('emails.send-login', $data, function ($m) use ($newuser) {
$user = User::where('username', $row[2])->first();
$user->notify(new WelcomeNotification($data));
/*Mail::send('emails.send-login', $data, function ($m) use ($newuser) {
$m->to($newuser['email'], $newuser['first_name'] . ' ' . $newuser['last_name']);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.welcome', ['name' => $newuser['first_name']]));
});
});*/
}
}
}
@@ -1138,23 +1147,6 @@ class UsersController extends Controller
}
public function postTwoFactorReset(Request $request)
{
if (Gate::denies('users.edit')) {
return response()->json(['message' => trans('general.insufficient_permissions')], 500);
}
try {
$user = User::find($request->get('id'));
$user->two_factor_secret = null;
$user->two_factor_enrolled = 0;
$user->save();
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
} catch (\Exception $e) {
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
}
}
/**
* LDAP form processing.
*
+61 -132
View File
@@ -12,6 +12,8 @@ use App\Models\Consumable;
use App\Models\License;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetNotification;
use App\Notifications\RequestAssetCancelationNotification;
use Auth;
use Config;
use DB;
@@ -59,7 +61,7 @@ class ViewAssetsController extends Controller
$error = trans('admin/users/message.user_not_found', compact('id'));
// Redirect to the user management page
return redirect()->route('users')->with('error', $error);
return redirect()->route('users.index')->with('error', $error);
}
}
@@ -80,116 +82,71 @@ class ViewAssetsController extends Controller
{
$item = null;
$fullItemType = 'App\\Models\\' . studly_case($itemType);
if ($itemType == "asset_model") {
$itemType = "model";
}
$item = call_user_func(array($fullItemType, 'find'), $itemId);
$user = Auth::user();
$quantity = $data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $item->id;
$logaction->item_type = $fullItemType;
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
if ($user->location_id) {
$logaction->location_id = $user->location_id;
}
$logaction->target_id = $data['user_id'] = Auth::user()->id;
$logaction->target_type = User::class;
$data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
$data['requested_by'] = $user->present()->fullName();
$data['item_name'] = $item->name;
$data['item'] = $item;
$data['item_type'] = $itemType;
$data['target'] = Auth::user();
if ($fullItemType == Asset::class) {
$data['item_url'] = route('hardware.show', $item->id);
$slackMessage = ' Asset <'.url('/').'/hardware/'.$item->id.'/view'.'|'.$item->present()->name().'> requested by <'.url('/').'/users/'.$item->user_id.'/view'.'|'.$user->present()->fullName().'>.';
} else {
$data['item_url'] = route("view/${itemType}", $item->id);
$slackMessage = $quantity. ' ' . class_basename(strtoupper($logaction->item_type)).' <'.$data['item_url'].'|'.$item->name.'> requested by <'.url('/').'/user/'.$item->id.'/view'.'|'.$user->present()->fullName().'>.';
}
$settings = Setting::getSettings();
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => $settings->botname,
'channel' => $settings->slack_channel,
'link_names' => true
];
$slackClient = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
}
if ($item->isRequestedBy($user)) {
$item->cancelRequest();
$log = $logaction->logaction('request_canceled');
if ($item_request = $item->isRequestedBy($user)) {
$item->cancelRequest();
$data['item_quantity'] = $item_request->qty;
$logaction->logaction('request_canceled');
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
Mail::send('emails.asset-canceled', $data, function ($m) use ($user, $settings) {
$m->to(explode(',', $settings->alert_email), $settings->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Item_Request_Canceled'));
});
}
if ($settings->slack_endpoint) {
try {
$slackClient->attach([
'color' => 'good',
'fields' => [
[
'title' => 'CANCELED:',
'value' => $slackMessage
]
]
])->send('Item Request Canceled');
} catch (Exception $e) {
}
$settings->notify(new RequestAssetCancelationNotification($data));
}
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
} else {
$item->request();
$log = $logaction->logaction('requested');
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
$m->to(explode(',', $settings->alert_email), $settings->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Item_Requested'));
});
$logaction->logaction('requested');
$settings->notify(new RequestAssetNotification($data));
}
if ($settings->slack_endpoint) {
try {
$slackClient->attach([
'color' => 'good',
'fields' => [
[
'title' => 'REQUESTED:',
'value' => $slackMessage
]
]
])->send('Item Requested');
} catch (Exception $e) {
}
}
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
}
}
public function getRequestAsset($assetId = null)
{
@@ -197,74 +154,45 @@ class ViewAssetsController extends Controller
// Check if the asset exists and is requestable
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
// Redirect to the asset management page
return redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
return redirect()->route('requestable-assets')
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
} elseif (!Company::isCurrentUserHasAccess($asset)) {
return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions'));
return redirect()->route('requestable-assets')
->with('error', trans('general.insufficient_permissions'));
}
// If it's requested, cancel the request.
$data['item'] = $asset;
$data['target'] = Auth::user();
$data['item_quantity'] = 1;
$settings = Setting::getSettings();
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
if ($user->location_id) {
$logaction->location_id = $user->location_id;
}
$logaction->target_id = $data['user_id'] = Auth::user()->id;
$logaction->target_type = User::class;
// If it's already requested, cancel the request.
if ($asset->isRequestedBy(Auth::user())) {
$asset->cancelRequest();
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
$logaction->logaction('request canceled');
$settings->notify(new RequestAssetCancelationNotification($data));
return redirect()->route('requestable-assets')
->with('success')->with('success', trans('admin/hardware/message.requests.cancel-success'));
} else {
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = Asset::class;
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
$data['asset_type'] = 'hardware';
if ($user->location_id) {
$logaction->location_id = $user->location_id;
}
$logaction->target_id = $data['user_id'] = Auth::user()->id;
$logaction->target_type = User::class;
$log = $logaction->logaction('requested');
$data['requested_by'] = $user->present()->fullName();
$data['asset_name'] = $asset->present()->name();
$settings = Setting::getSettings();
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
$m->to(explode(',', $settings->alert_email), $settings->site_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.asset_requested'));
});
}
$logaction->logaction('requested');
$asset->request();
$settings->notify(new RequestAssetNotification($data));
if ($settings->slack_endpoint) {
$slack_settings = [
'username' => $settings->botname,
'channel' => $settings->slack_channel,
'link_names' => true
];
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
try {
$client->attach([
'color' => 'good',
'fields' => [
[
'title' => 'REQUESTED:',
'value' => class_basename(strtoupper($logaction->item_type)).' asset <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.$asset->present()->name().'> requested by <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.Auth::user()->present()->fullName().'>.'
]
]
])->send('Asset Requested');
} catch (Exception $e) {
}
}
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
}
@@ -273,13 +201,10 @@ class ViewAssetsController extends Controller
public function getRequestedAssets()
{
$checkoutrequests = CheckoutRequest::all();
return view('account/requested-items', compact($checkoutrequests));
return view('account/requested');
}
// Get the acceptance screen
public function getAcceptAsset($logID = null)
{
@@ -297,10 +222,12 @@ class ViewAssetsController extends Controller
$user = Auth::user();
if ($user->id != $findlog->item->assigned_to) {
// TODO - Fix this for non-assets
if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) {
return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted'));
}
$item = $findlog->item;
// Check if the asset exists
@@ -336,7 +263,7 @@ class ViewAssetsController extends Controller
$user = Auth::user();
if ($user->id != $findlog->item->assigned_to) {
if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) {
return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted'));
}
@@ -388,9 +315,11 @@ class ViewAssetsController extends Controller
->where('id', $findlog->id)
->update(array('accepted_id' => $logaction->id));
if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) {
$affected_asset = $logaction->item;
$affected_asset->accepted = $accepted;
$affected_asset->save();
}
if ($update_checkout) {
return redirect()->to('account/view-assets')->with('success', $return_msg);
+1 -1
View File
@@ -29,7 +29,7 @@ class CheckForSetup
} else {
if (!($request->is('setup*')) && !($request->is('.env'))) {
return redirect(url('/').'/setup')->with('Request', $request);
return redirect(url('/').'/setup');
}
return $next($request);
+2 -1
View File
@@ -23,8 +23,9 @@ class AssetFileRequest extends Request
*/
public function rules()
{
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
return [
'file.*' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar|max:2000'
'file.*' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar|max:'.$max_file_size,
];
}
+2 -2
View File
@@ -24,8 +24,8 @@ class ImageUploadRequest extends Request
public function rules()
{
return [
'image' => 'mimes:png,gif,jpg,jpeg,svg|max:2000',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg|max:2000',
'image' => 'mimes:png,gif,jpg,jpeg,svg',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg',
];
}
+4 -1
View File
@@ -35,7 +35,10 @@ class SaveUserRequest extends Request
{
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('store');
if ($this->request->get('ldap_import') == false)
{
$rules['password'] = Setting::passwordComplexityRulesSaving('store');
}
break;
}
@@ -32,7 +32,7 @@ class AccessoriesTransformer
'notes' => ($accessory->notes) ? e($accessory->notes) : null,
'qty' => ($accessory->qty) ? (int) $accessory->qty : null,
'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null,
'purchase_cost' => ($accessory->purchase_cost) ? e($accessory->purchase_cost) : null,
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
'remaining_qty' => $accessory->numRemaining(),
@@ -22,10 +22,20 @@ class ActionlogsTransformer
public function transformActionlog (Actionlog $actionlog, $settings = null)
{
$icon = $actionlog->present()->icon();
if ($actionlog->filename!='') {
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
}
$array = [
'id' => (int) $actionlog->id,
'icon' => $actionlog->present()->icon(),
'image' => (method_exists($actionlog->item, 'getImageUrl')) ? $actionlog->item->getImageUrl() : null,
'icon' => $icon,
'file' => ($actionlog->filename!='') ?
[
'url' => route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]),
'filename' => $actionlog->filename,
'inlineable' => (bool) \App\Helpers\Helper::show_file_inline($actionlog->filename),
] : null,
'item' => ($actionlog->item) ? [
'id' => (int) $actionlog->item->id,
'name' => e($actionlog->item->getDisplayNameAttribute()),
@@ -59,12 +69,11 @@ class ActionlogsTransformer
];
return $array;
}
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
{
@@ -22,8 +22,18 @@ class AssetMaintenancesTransformer
{
$array = [
'id' => (int) $assetmaintenance->id,
'asset_name' => ($assetmaintenance->asset) ? ['id' => $assetmaintenance->asset->id,'name'=> e($assetmaintenance->asset->name)] : null,
'asset' => ($assetmaintenance->asset) ? [
'id' => (int) $assetmaintenance->asset->id,
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
'asset_tag'=> e($assetmaintenance->asset->asset_tag)
] : null,
'title' => ($assetmaintenance->title) ? e($assetmaintenance->title) : null,
'location' => (($assetmaintenance->asset) && ($assetmaintenance->asset->location)) ? [
'id' => (int) $assetmaintenance->asset->location->id,
'name'=> e($assetmaintenance->asset->location->name),
] : 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),
@@ -34,7 +34,7 @@ class AssetModelsTransformer
'id' => (int) $assetmodel->depreciation->id,
'name'=> e($assetmodel->depreciation->name)
] : null,
'assets_count' => $assetmodel->assets_count,
'assets_count' => (int) $assetmodel->assets_count,
'category' => ($assetmodel->category) ? [
'id' => (int) $assetmodel->category->id,
'name'=> e($assetmodel->category->name)
+39 -1
View File
@@ -77,11 +77,14 @@ class AssetsTransformer
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost),
'checkins_count' => (int) $asset->checkins_count,
'checkouts_count' => (int) $asset->checkouts_count,
'user_requests_count' => (int) $asset->user_requests_count,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
];
if (($asset->model) && ($asset->model->fieldset) && (count($asset->model->fieldset->fields)> 0)) {
if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) {
$fields_array = array();
foreach ($asset->model->fieldset->fields as $field) {
@@ -160,4 +163,39 @@ class AssetsTransformer
'type' => $asset->assignedType()
] : null;
}
public function transformRequestedAssets(Collection $assets, $total)
{
$array = array();
foreach ($assets as $asset) {
$array[] = self::transformRequestedAsset($asset);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformRequestedAsset(Asset $asset) {
$array = [
'id' => (int) $asset->id,
'name' => e($asset->name),
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
'model' => ($asset->model) ? e($asset->model->name) : null,
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'location' => ($asset->location) ? e($asset->location->name) : null,
'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
];
$permissions_array['available_actions'] = [
'cancel' => ($asset->isRequestedBy(\Auth::user())) ? true : false,
'request' => ($asset->isRequestedBy(\Auth::user())) ? false : true,
];
$array += $permissions_array;
return $array;
}
}
@@ -18,14 +18,34 @@ class CustomFieldsTransformer
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
/**
* Builds up an array of formatted custom fields
* @param Collection $fields
* @param int $modelId
* @param int $total
* @return array
*/
public function transformCustomFieldsWithDefaultValues (Collection $fields, $modelId, $total)
{
$array = [];
foreach ($fields as $field) {
$array[] = self::transformCustomFieldWithDefaultValue($field, $modelId);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformCustomField (CustomField $field)
{
$array = [
'id' => $field->id,
'name' => e($field->name),
'db_column_name' => e($field->db_column_name()),
'format' => e($field->format),
'field_values' => ($field->field_values) ? e($field->field_values) : null,
'field_values_array' => ($field->field_values) ? explode("\r\n", e($field->field_values)) : null,
'type' => e($field->element),
'required' => $field->pivot ? $field->pivot->required : false,
'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'),
@@ -33,5 +53,22 @@ class CustomFieldsTransformer
return $array;
}
/**
* Returns the core data for a field, including the default value it has
* when attributed to a certain model
*
* @param CustomField $field
* @param int $modelId
* @return array
*/
public function transformCustomFieldWithDefaultValue (CustomField $field, $modelId)
{
return [
'id' => $field->id,
'name' => e($field->name),
'type' => e($field->element),
'field_values_array' => ($field->field_values) ? explode("\r\n", e($field->field_values)) : null,
'default_value' => $field->defaultValue($modelId),
];
}
}
@@ -36,10 +36,12 @@ class ManufacturersTransformer
'accessories_count' => (int) $manufacturer->accessories_count,
'created_at' => Helper::getFormattedDateObject($manufacturer->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($manufacturer->updated_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($manufacturer->deleted_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', Manufacturer::class) ? true : false,
'update' => (($manufacturer->deleted_at=='') && (Gate::allows('update', Manufacturer::class))) ? true : false,
'restore' => (($manufacturer->deleted_at!='') && (Gate::allows('create', Manufacturer::class))) ? true : false,
'delete' => (Gate::allows('delete', Manufacturer::class) && ($manufacturer->assets_count == 0) && ($manufacturer->licenses_count==0) && ($manufacturer->consumables_count==0) && ($manufacturer->accessories_count==0) && ($manufacturer->deleted_at=='')) ? true : false,
];
@@ -26,6 +26,7 @@ class StatuslabelsTransformer
'type' => $statuslabel->getStatuslabelType(),
'color' => ($statuslabel->color) ? e($statuslabel->color) : null,
'show_in_nav' => ($statuslabel->show_in_nav=='1') ? true : false,
'default_label' => ($statuslabel->default_label =='1') ? true : false,
'assets_count' => (int) $statuslabel->assets_count,
'notes' => e($statuslabel->notes),
'created_at' => Helper::getFormattedDateObject($statuslabel->created_at, 'datetime'),
+1 -1
View File
@@ -73,7 +73,7 @@ class UsersTransformer
$array += $permissions_array;
$numGroups = count($user->groups);
$numGroups = $user->groups->count();
if($numGroups > 0)
{
$groups["total"] = $numGroups;
+3 -3
View File
@@ -31,9 +31,8 @@ class AssetImporter extends ItemImporter
$this->item['custom_fields'][$customField->db_column_name()] = $customFieldValue;
$this->log('Custom Field '. $customField->name.': '.$customFieldValue);
} else {
// This removes custom fields when updating if the column doesn't exist in file.
// Commented out becausee not sure if it's needed anywhere.
// $this->item['custom_fields'][$customField->db_column_name()] = '';
// Clear out previous data.
$this->item['custom_fields'][$customField->db_column_name()] = null;
}
}
}
@@ -106,6 +105,7 @@ class AssetImporter extends ItemImporter
$asset->{$custom_field} = $val;
}
}
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
// $asset->unsetEventDispatcher();
if ($asset->save()) {
+46 -27
View File
@@ -55,7 +55,7 @@ abstract class Importer
'supplier' => 'supplier',
'termination_date' => 'termination date',
'warranty_months' => 'warranty',
'name' => 'name',
'full_name' => 'full name',
'email' => 'email',
'username' => 'username'
];
@@ -91,15 +91,14 @@ abstract class Importer
$this->fieldMap = $this->defaultFieldMap;
// By default the importer passes a url to the file.
// However, for testing we also support passing a string directly
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
if (is_file($file)) {
$this->csv = Reader::createFromPath($file);
} else {
$this->csv = Reader::createFromString($file);
}
$this->csv->setNewLine('\r\n');
if (! ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$this->tempPassword = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
}
// Cached Values for import lookups
@@ -117,6 +116,17 @@ abstract class Importer
$nameLookup[$field['name']] = $field;
return $nameLookup;
});
// Remove any custom fields that do not exist in the header row. This prevents nulling out values that shouldn't exist.
// In detail, we compare the lower case name of custom fields (indexed by name) to the keys in the header row. This
// results in an array with only custom fields that are in the file.
if ($this->customFields) {
$this->customFields = array_intersect_key(
array_change_key_case($this->customFields),
array_change_key_case(array_flip($headerRow))
);
}
DB::transaction(function () use (&$results) {
Model::unguard();
@@ -153,7 +163,7 @@ abstract class Importer
$this->log("Custom Key: ${key}");
if (array_key_exists($key, $array)) {
$val = e(Encoding::toUTF8(trim($array[ $key ])));
$val = Encoding::toUTF8(trim($array[ $key ]));
}
// $this->log("${key}: ${val}");
return $val;
@@ -169,6 +179,7 @@ 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];
@@ -200,7 +211,7 @@ abstract class Importer
public function array_smart_custom_field_fetch(array $array, $key)
{
$index_name = strtolower($key->name);
return array_key_exists($index_name, $array) ? e(trim($array[$index_name])) : false;
return array_key_exists($index_name, $array) ? trim($array[$index_name]) : false;
}
protected function log($string)
@@ -231,11 +242,23 @@ abstract class Importer
*/
protected function createOrFetchUser($row)
{
$user_name = $this->findCsvMatch($row, "name");
$user_name = $this->findCsvMatch($row, "full_name");
$user_email = $this->findCsvMatch($row, "email");
$user_username = $this->findCsvMatch($row, "username");
$first_name = '';
$last_name = '';
if(empty($user_name) && empty($user_email) && empty($user_username)) {
$this->log('No user data provided - skipping user creation, just adding asset');
//$user_username = '';
return false;
}
// A username was given.
if( !empty($user_username)) {
$user = User::where('username', $user_username)->first();
if($user) {
return $user;
}
}
// A number was given instead of a name
if (is_numeric($user_name)) {
$this->log('User '.$user_name.' is not a name - assume this user already exists');
@@ -244,28 +267,24 @@ abstract class Importer
return $user;
}
$this->log('User with id'.$user_name.' does not exist. Continuing through our processes');
} elseif (empty($user_name)) {
$this->log('No user data provided - skipping user creation, just adding asset');
//$user_username = '';
return false;
} else {
$user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
$first_name = $user_email_array['first_name'];
$last_name = $user_email_array['last_name'];
}
// Generate data based on user name.
$user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
$first_name = $user_email_array['first_name'];
$last_name = $user_email_array['last_name'];
if ($user_email=='') {
if (Setting::getSettings()->email_domain) {
$user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
}
if (empty($user_email)) {
if (Setting::getSettings()->email_domain) {
$user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
}
}
if ($user_username=='') {
if ($this->usernameFormat =='email') {
$user_username = $user_email;
} else {
$user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
$user_username = $user_name_array['username'];
}
if (empty($user_username)) {
if ($this->usernameFormat =='email') {
$user_username = $user_email;
} else {
$user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
$user_username = $user_name_array['username'];
}
}
$user = new User;
+24 -5
View File
@@ -4,6 +4,8 @@ namespace App\Models;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use App\Notifications\CheckinAccessoryNotification;
use App\Notifications\CheckoutAccessoryNotification;
/**
* Model for Accessories.
@@ -23,6 +25,13 @@ class Accessory extends SnipeModel
'requestable' => 'boolean'
];
/**
* Set static properties to determine which checkout/checkin handlers we should use
*/
public static $checkoutClass = CheckoutAccessoryNotification::class;
public static $checkinClass = CheckinAccessoryNotification::class;
/**
* Accessory validation rules
*/
@@ -68,6 +77,8 @@ class Accessory extends SnipeModel
];
public function supplier()
{
return $this->belongsTo('\App\Models\Supplier', 'supplier_id');
@@ -106,6 +117,13 @@ class Accessory extends SnipeModel
return $this->hasMany('\App\Models\Actionlog', 'item_id')->where('item_type', Accessory::class)->orderBy('created_at', 'desc')->withTrashed();
}
public function getImageUrl() {
if ($this->image) {
return url('/').'/uploads/accessories/'.$this->image;
}
return false;
}
public function users()
{
@@ -163,26 +181,27 @@ class Accessory extends SnipeModel
*/
public function scopeTextSearch($query, $search)
{
$search = explode('+', $search);
return $query->where(function ($query) use ($search) {
foreach ($search as $search) {
$query->whereHas('category', function ($query) use ($search) {
$query->where('categories.name', 'LIKE', '%'.$search.'%');
})->orWhere(function ($query) use ($search) {
$query->whereHas('company', function ($query) use ($search) {
$query->where('companies.name', 'LIKE', '%'.$search.'%');
});
})->orWhere(function ($query) use ($search) {
$query->whereHas('manufacturer', function ($query) use ($search) {
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
});
})->orWhere(function ($query) use ($search) {
$query->whereHas('location', function ($query) use ($search) {
$query->where('locations.name', 'LIKE', '%'.$search.'%');
});
})->orWhere('accessories.name', 'LIKE', '%'.$search.'%')
->orWhere('accessories.model_number', 'LIKE', '%'.$search.'%')
->orWhere('accessories.order_number', 'LIKE', '%'.$search.'%')
->orWhere('accessories.purchase_cost', '=', $search);
}
->orWhere('accessories.order_number', 'LIKE', '%'.$search.'%');
});
}
+5 -2
View File
@@ -133,8 +133,11 @@ class Actionlog extends SnipeModel
**/
public function get_src($type = 'assets', $fieldname = 'filename')
{
$file = config('app.private_uploads') . '/' . $type . '/' . $this->{$fieldname};
return $file;
if ($this->filename!='') {
$file = config('app.private_uploads') . '/' . $type . '/' . $this->{$fieldname};
return $file;
}
return false;
}
+66 -16
View File
@@ -11,8 +11,9 @@ use Config;
use Illuminate\Database\Eloquent\SoftDeletes;
use Log;
use Watson\Validating\ValidatingTrait;
use Illuminate\Notifications\Notifiable;
use DB;
use App\Notifications\CheckinAssetNotification;
use App\Notifications\CheckoutAssetNotification;
/**
* Model for Assets.
@@ -22,25 +23,33 @@ use DB;
class Asset extends Depreciable
{
protected $presenter = 'App\Presenters\AssetPresenter';
use Loggable, Requestable, Presentable, Notifiable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
const LOCATION = 'location';
const ASSET = 'asset';
const USER = 'user';
/**
* The database table used by the model.
*
* @var string
*/
/**
* Set static properties to determine which checkout/checkin handlers we should use
*/
public static $checkoutClass = CheckoutAssetNotification::class;
public static $checkinClass = CheckinAssetNotification::class;
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'assets';
/**
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* @var boolean
*/
/**
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* @var boolean
*/
protected $injectUniqueIdentifier = true;
// We set these as protected dates so that they will be easily accessible via Carbon
@@ -85,6 +94,7 @@ class Asset extends Depreciable
'assigned_type',
'company_id',
'image',
'location_id',
'model_id',
'name',
'notes',
@@ -340,6 +350,38 @@ class Asset extends Depreciable
->withTrashed();
}
/**
* Get checkouts
*/
public function checkouts()
{
return $this->assetlog()->where('action_type', '=', 'checkout')
->orderBy('created_at', 'desc')
->withTrashed();
}
/**
* Get checkins
*/
public function checkins()
{
return $this->assetlog()
->where('action_type', '=', 'checkin from')
->orderBy('created_at', 'desc')
->withTrashed();
}
/**
* Get user requests
*/
public function userRequests()
{
return $this->assetlog()
->where('action_type', '=', 'requested')
->orderBy('created_at', 'desc')
->withTrashed();
}
/**
* assetmaintenances
@@ -607,7 +649,7 @@ class Asset extends Depreciable
public function scopeRTD($query)
{
return $query->whereNULL('assigned_to')
return $query->whereNULL('assets.assigned_to')
->whereHas('assetstatus', function ($query) {
$query->where('deployable', '=', 1)
->where('pending', '=', 0)
@@ -1008,9 +1050,17 @@ class Asset extends Depreciable
});
});
}
if ($fieldname =='supplier') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%' . $search_val . '%');
});
});
}
}
if (($fieldname!='category') && ($fieldname!='location')
if (($fieldname!='category') && ($fieldname!='location') && ($fieldname!='supplier')
&& ($fieldname!='status_label') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')) {
$query->orWhere('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
}
+36 -7
View File
@@ -52,7 +52,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
return [
trans('admin/asset_maintenances/general.maintenance') => trans('admin/asset_maintenances/general.maintenance'),
trans('admin/asset_maintenances/general.repair') => trans('admin/asset_maintenances/general.repair'),
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade')
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade'),
'PAT test' => 'PAT test',
];
}
@@ -154,12 +155,12 @@ class AssetMaintenance extends Model implements ICompanyableChild
return $query->where(function ($query) use ($search) {
$query->where('title', 'LIKE', '%'.$search.'%')
->orWhere('notes', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenance_type', 'LIKE', '%'.$search.'%')
->orWhere('cost', 'LIKE', '%'.$search.'%')
->orWhere('start_date', 'LIKE', '%'.$search.'%')
->orWhere('completion_date', 'LIKE', '%'.$search.'%');
$query->where('asset_maintenances.title', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenances.notes', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenances.asset_maintenance_type', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenances.cost', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenances.start_date', 'LIKE', '%'.$search.'%')
->orWhere('asset_maintenances.completion_date', 'LIKE', '%'.$search.'%');
});
}
@@ -177,4 +178,32 @@ class AssetMaintenance extends Model implements ICompanyableChild
->orderBy('users.first_name', $order)
->orderBy('users.last_name', $order);
}
/**
* Query builder scope to order on asset tag
*
* @param Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderByTag($query, $order)
{
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
->orderBy('assets.asset_tag', $order);
}
/**
* Query builder scope to order on asset tag
*
* @param Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderByAssetName($query, $order)
{
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
->orderBy('assets.name', $order);
}
}
+25 -1
View File
@@ -55,7 +55,18 @@ class AssetModel extends SnipeModel
*
* @var array
*/
protected $fillable = ['name','manufacturer_id','category_id','eol', 'user_id', 'fieldset_id', 'model_number', 'notes'];
protected $fillable = [
'category_id',
'depreciation_id',
'eol',
'fieldset_id',
'image',
'manufacturer_id',
'model_number',
'name',
'notes',
'user_id',
];
public function assets()
{
@@ -87,6 +98,19 @@ class AssetModel extends SnipeModel
return $this->belongsTo('\App\Models\CustomFieldset', 'fieldset_id');
}
public function defaultValues()
{
return $this->belongsToMany('\App\Models\CustomField', 'models_custom_fields')->withPivot('default_value');
}
public function getImageUrl() {
if ($this->image) {
return url('/').'/uploads/models/'.$this->image;
}
return false;
}
/**
* -----------------------------------------------
* BEGIN QUERY SCOPES
+9 -1
View File
@@ -53,7 +53,15 @@ class Category extends SnipeModel
*
* @var array
*/
protected $fillable = ['name','category_type', 'user_id', 'use_default_eula','checkin_email','require_acceptance'];
protected $fillable = [
'category_type',
'checkin_email',
'eula_text',
'name',
'require_acceptance',
'use_default_eula',
'user_id',
];
public function has_models()
+2 -1
View File
@@ -1,12 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
class CheckoutRequest extends Model
{
//
use SoftDeletes;
protected $fillable = ['user_id'];
protected $table = 'checkout_requests';
+10 -1
View File
@@ -19,6 +19,14 @@ class Component extends SnipeModel
protected $dates = ['deleted_at', 'purchase_date'];
protected $table = 'components';
/**
* Set static properties to determine which checkout/checkin handlers we should use
*/
public static $checkoutClass = null;
public static $checkinClass = null;
/**
* Category validation rules
*/
@@ -54,8 +62,9 @@ class Component extends SnipeModel
'purchase_cost',
'purchase_date',
'min_amt',
'order_number',
'qty',
'serial'
'serial',
];
public function location()
+16
View File
@@ -4,6 +4,7 @@ namespace App\Models;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use App\Notifications\CheckoutConsumableNotification;
class Consumable extends SnipeModel
{
@@ -18,6 +19,11 @@ class Consumable extends SnipeModel
'requestable' => 'boolean'
];
/**
* Set static properties to determine which checkout/checkin handlers we should use
*/
public static $checkoutClass = CheckoutConsumableNotification::class;
public static $checkinClass = null;
/**
@@ -50,10 +56,12 @@ class Consumable extends SnipeModel
protected $fillable = [
'category_id',
'company_id',
'item_no',
'location_id',
'manufacturer_id',
'name',
'order_number',
'model_number',
'purchase_cost',
'purchase_date',
'qty',
@@ -107,6 +115,14 @@ class Consumable extends SnipeModel
return $this->hasMany('\App\Models\Actionlog', 'item_id')->where('item_type', Consumable::class)->orderBy('created_at', 'desc')->withTrashed();
}
public function getImageUrl() {
if ($this->image) {
return url('/').'/uploads/consumables/'.$this->image;
}
return false;
}
public function users()
{
+20
View File
@@ -146,6 +146,26 @@ class CustomField extends Model
return $this->belongsTo('\App\Models\User');
}
public function defaultValues()
{
return $this->belongsToMany('\App\Models\AssetModel', 'models_custom_fields')->withPivot('default_value');
}
/**
* Returns the default value for a given model using the defaultValues
* relationship
*
* @param int $modelId
* @return string
*/
public function defaultValue($modelId)
{
return $this->defaultValues->filter(function ($item) use ($modelId) {
return $item->pivot->asset_model_id == $modelId;
})->map(function ($item) {
return $item->pivot->default_value;
})->first();
}
public function check_format($value)
{
+2 -2
View File
@@ -262,13 +262,13 @@ class Ldap extends Model
$search_results = ldap_search($ldapconn, $base_dn, '('.$filter.')');
if (!$search_results) {
return redirect()->route('users')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn));
}
// Get results from page
$results = ldap_get_entries($ldapconn, $search_results);
if (!$results) {
return redirect()->route('users')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn));
}
// Add results to result set
+18 -19
View File
@@ -40,7 +40,6 @@ class License extends Depreciable
'seats' => 'required|min:1|max:1000000|integer',
'license_email' => 'email|nullable|max:120',
'license_name' => 'string|nullable|max:100',
'note' => 'string|nullable',
'notes' => 'string|nullable',
'company_id' => 'integer|nullable',
);
@@ -51,25 +50,25 @@ class License extends Depreciable
* @var array
*/
protected $fillable = [
'name',
'serial',
'purchase_date',
'purchase_cost',
'order_number',
'seats',
'notes',
'user_id',
'depreciation_id',
'license_name', //actually licensed_to
'license_email',
'supplier_id',
'expiration_date',
'purchase_order',
'termination_date',
'maintained',
'reassignable',
'company_id',
'manufacturer_id'
'depreciation_id',
'expiration_date',
'license_email',
'license_name', //actually licensed_to
'maintained',
'manufacturer_id',
'name',
'notes',
'order_number',
'purchase_cost',
'purchase_date',
'purchase_order',
'reassignable',
'seats',
'serial',
'supplier_id',
'termination_date',
'user_id',
];
public static function boot()
+8
View File
@@ -4,6 +4,8 @@ namespace App\Models;
use App\Models\Loggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Notifications\CheckoutLicenseNotification;
use App\Notifications\CheckinLicenseNotification;
class LicenseSeat extends Model implements ICompanyableChild
{
@@ -15,6 +17,12 @@ class LicenseSeat extends Model implements ICompanyableChild
protected $guarded = 'id';
protected $table = 'license_seats';
/**
* Set static properties to determine which checkout/checkin handlers we should use
*/
public static $checkoutClass = CheckoutLicenseNotification::class;
public static $checkinClass = CheckinLicenseNotification::class;
public function getCompanyableParents()
{
return ['asset', 'license'];
+14 -2
View File
@@ -20,7 +20,7 @@ class Location extends SnipeModel
protected $table = 'locations';
protected $rules = array(
'name' => 'required|min:2|max:255|unique_undeleted',
'city' => 'min:3|max:255|nullable',
'city' => 'min:2|max:255|nullable',
'country' => 'min:2|max:2|nullable',
'address' => 'max:80|nullable',
'address2' => 'max:80|nullable',
@@ -45,7 +45,19 @@ class Location extends SnipeModel
*
* @var array
*/
protected $fillable = ['name','parent_id','address','address2','city','state', 'country','zip','ldap_ou'];
protected $fillable = [
'name',
'parent_id',
'address',
'address2',
'city',
'state',
'country',
'zip',
'ldap_ou',
'currency',
'image',
];
protected $hidden = ['user_id'];
public function users()
+48 -14
View File
@@ -6,11 +6,17 @@ use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutRequest;
use App\Models\User;
use App\Notifications\CheckinNotification;
use App\Notifications\CheckinAssetNotification;
use App\Notifications\AuditNotification;
use App\Notifications\CheckoutNotification;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CheckoutAccessoryNotification;
use App\Notifications\CheckinAccessoryNotification;
use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseNotification;
use App\Notifications\CheckinLicenseNotification;
use Illuminate\Support\Facades\Auth;
trait Loggable
{
@@ -32,6 +38,7 @@ trait Loggable
*/
public function logCheckout($note, $target /* What are we checking out to? */)
{
$settings = Setting::getSettings();
$log = new Actionlog;
$log = $this->determineLogItemType($log);
$log->user_id = Auth::user()->id;
@@ -43,13 +50,11 @@ trait Loggable
$log->target_type = get_class($target);
$log->target_id = $target->id;
$target_class = get_class($target);
// Figure out what the target is
if ($target_class == Location::class) {
// We can checkout to a location
if ($log->target_type == Location::class) {
$log->location_id = $target->id;
} elseif ($target_class== Asset::class) {
} elseif ($log->target_type == Asset::class) {
$log->location_id = $target->rtd_location_id;
} else {
$log->location_id = $target->location_id;
@@ -60,18 +65,25 @@ trait Loggable
$params = [
'item' => $log->item,
'target_type' => $log->target_type,
'target' => $target,
'admin' => $log->user,
'note' => $note,
'log_id' => $log->id
'log_id' => $log->id,
'settings' => $settings,
];
if ($settings = Setting::getSettings()) {
$settings->notify(new CheckoutNotification($params));
}
$checkoutClass = null;
if (method_exists($target, 'notify')) {
$target->notify(new CheckoutNotification($params));
$target->notify(new static::$checkoutClass($params));
}
// Send to the admin, if settings dictate
$recipient = new \App\Models\Recipients\AdminRecipient();
if (($settings->admin_cc_email!='') && (static::$checkoutClass!='')) {
$recipient->notify(new static::$checkoutClass($params));
}
return $log;
@@ -100,9 +112,11 @@ trait Loggable
*/
public function logCheckin($target, $note)
{
$settings = Setting::getSettings();
$log = new Actionlog;
$log->target_type = get_class($target);
$log->target_id = $target->id;
if (static::class == LicenseSeat::class) {
$log->item_type = License::class;
$log->item_id = $this->license_id;
@@ -110,17 +124,35 @@ trait Loggable
$log->item_type = static::class;
$log->item_id = $this->id;
}
$log->location_id = null;
$log->note = $note;
$log->user_id = Auth::user()->id;
$log->logaction('checkin from');
$params = [
'target' => $target,
'item' => $log->item,
'admin' => $log->user,
'note' => $note
'note' => $note,
'target_type' => $log->target_type,
'settings' => $settings,
];
Setting::getSettings()->notify(new CheckinNotification($params));
$checkinClass = null;
if (method_exists($target, 'notify')) {
$target->notify(new static::$checkinClass($params));
}
// Send to the admin, if settings dictate
$recipient = new \App\Models\Recipients\AdminRecipient();
if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) {
$recipient->notify(new static::$checkinClass($params));
}
return $log;
}
@@ -131,7 +163,7 @@ trait Loggable
* @since [v4.0]
* @return \App\Models\Actionlog
*/
public function logAudit($note, $location_id)
public function logAudit($note, $location_id, $filename = null)
{
$log = new Actionlog;
$location = Location::find($location_id);
@@ -145,10 +177,12 @@ trait Loggable
$log->location_id = ($location_id) ? $location_id : null;
$log->note = $note;
$log->user_id = Auth::user()->id;
$log->filename = $filename;
$log->logaction('audit');
$params = [
'item' => $log->item,
'filename' => $log->filename,
'admin' => $log->user,
'location' => ($location) ? $location->name : '',
'note' => $note
+9 -4
View File
@@ -21,9 +21,7 @@ class Manufacturer extends SnipeModel
'support_email' => 'email|nullable'
);
protected $hidden = ['user_id','deleted_at'];
protected $hidden = ['user_id'];
/**
* Whether the model should inject it's identifier to the unique
@@ -40,7 +38,14 @@ class Manufacturer extends SnipeModel
*
* @var array
*/
protected $fillable = ['name','url','support_url','support_phone','support_email'];
protected $fillable = [
'name',
'image',
'support_email',
'support_phone',
'support_url',
'url',
];
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Models\Recipients;
use App\Models\Setting;
class AdminRecipient extends Recipient{
public function __construct()
{
$settings = Setting::getSettings();
$this->email = $settings->admin_cc_email;
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Models\Recipients;
use App\Models\Setting;
class AlertRecipient extends Recipient{
public function __construct()
{
$settings = Setting::getSettings();
$this->email = $settings->alert_email;
}
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Models\Recipients;
use Illuminate\Notifications\Notifiable;
abstract class Recipient {
use Notifiable;
protected $email;
}
+9 -6
View File
@@ -19,9 +19,7 @@ trait Requestable
public function isRequestedBy(User $user)
{
$requests = $this->requests->where('user_id', $user->id);
return $requests->count() > 0;
return $this->requests->where('canceled_at', NULL)->where('user_id', $user->id)->first();
}
public function scopeRequestedBy($query, User $user)
@@ -31,15 +29,20 @@ trait Requestable
});
}
public function request()
public function request($qty = 1)
{
$this->requests()->save(
new CheckoutRequest(['user_id' => Auth::id()])
new CheckoutRequest(['user_id' => Auth::id(), 'qty' => $qty])
);
}
public function deleteRequest()
{
$this->requests()->where('user_id', Auth::id())->delete();
}
public function cancelRequest()
{
$this->requests()->where('user_id', Auth::id())->delete();
$this->requests()->where('user_id', Auth::id())->update(['canceled_at' => \Carbon\Carbon::now()]);
}
}
+25 -14
View File
@@ -13,15 +13,16 @@ class Setting extends Model
use ValidatingTrait;
protected $rules = [
"brand" => 'required|min:1|numeric',
"qr_text" => 'max:31|nullable',
"logo_img" => 'mimes:jpeg,bmp,png,gif',
"alert_email" => 'email_array|nullable',
"default_currency" => 'required',
"locale" => 'required',
"slack_endpoint" => 'url|required_with:slack_channel|nullable',
"slack_channel" => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
"slack_botname" => 'string|nullable',
'brand' => 'required|min:1|numeric',
'qr_text' => 'max:31|nullable',
'logo_img' => 'mimes:jpeg,bmp,png,gif',
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email|nullable',
'default_currency' => 'required',
'locale' => 'required',
'slack_endpoint' => 'url|required_with:slack_channel|nullable',
'slack_channel' => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
'slack_botname' => 'string|nullable',
'labels_per_page' => 'numeric',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
@@ -34,11 +35,14 @@ class Setting extends Model
'labels_fontsize' => 'numeric|min:5',
'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",
"custom_forgot_pass_url" => "url|nullable",
'login_remote_user_enabled' => 'numeric|nullable',
'login_common_disabled' => 'numeric|nullable',
'login_remote_user_custom_logout_url' => 'string|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',
'custom_forgot_pass_url' => 'url|nullable',
];
protected $fillable = ['site_name','email_domain','email_format','username_format'];
@@ -180,6 +184,13 @@ class Setting extends Model
return $this->slack_endpoint;
}
public function routeNotificationForMail()
{
// At this point the endpoint is the same for everything.
// In the future this may want to be adapted for individual notifications.
return config('mail.reply_to.address');
}
public static function passwordComplexityRulesSaving($action = 'update')
{
$security_rules = '';
+7 -1
View File
@@ -27,7 +27,13 @@ class Statuslabel extends SnipeModel
'archived' => 'required',
);
protected $fillable = ['name', 'deployable', 'pending', 'archived'];
protected $fillable = [
'archived',
'deployable',
'name',
'notes',
'pending',
];
/**
+2 -2
View File
@@ -14,7 +14,7 @@ class Supplier extends SnipeModel
protected $table = 'suppliers';
protected $rules = array(
'name' => 'required|min:3|max:255|unique_undeleted',
'name' => 'required|min:1|max:255|unique_undeleted',
'address' => 'max:50|nullable',
'address2' => 'max:50|nullable',
'city' => 'max:255|nullable',
@@ -23,7 +23,7 @@ class Supplier extends SnipeModel
'fax' => 'min:7|max:35|nullable',
'phone' => 'min:7|max:35|nullable',
'contact' => 'max:100|nullable',
'notes' => 'max:255|nullable',
'notes' => 'max:191|nullable', // Default string length is 191 characters..
'email' => 'email|max:150|nullable',
'zip' => 'max:10|nullable',
'url' => 'sometimes|nullable|string|max:250',
+52 -25
View File
@@ -28,24 +28,26 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
protected $table = 'users';
protected $injectUniqueIdentifier = true;
protected $fillable = [
'email',
'last_name',
'company_id',
'department_id',
'employee_num',
'jobtitle',
'location_id',
'password',
'phone',
'username',
'first_name',
'activated',
'address',
'city',
'state',
'company_id',
'country',
'zip',
'activated',
'department_id',
'email',
'employee_num',
'first_name',
'jobtitle',
'last_name',
'ldap_import',
'locale',
'location_id',
'manager_id',
'password',
'phone',
'state',
'username',
'zip',
];
protected $casts = [
@@ -148,6 +150,18 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
return $this->last_name . ", " . $this->first_name . " (" . $this->username . ")";
}
/**
* The url for slack notifications.
* Used by Notifiable trait.
* @return mixed
*/
public function routeNotificationForSlack()
{
// At this point the endpoint is the same for everything.
// In the future this may want to be adapted for individual notifications.
$this->endpoint = \App\Models\Setting::getSettings()->slack_endpoint;
return $this->endpoint;
}
/**
@@ -279,7 +293,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
*/
public function checkoutRequests()
{
return $this->belongsToMany(Asset::class, 'checkout_requests');
return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at');
}
public function throttle()
@@ -330,19 +344,32 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
public static function generateFormattedNameFromFullName($format = 'filastname', $users_name)
{
list($first_name, $last_name) = explode(" ", $users_name, 2);
// Assume filastname by default
$username = str_slug(substr($first_name, 0, 1).$last_name);
if ($format=='firstname.lastname') {
$username = str_slug($first_name).'.'.str_slug($last_name);
// If there was only one name given
if (strpos($users_name, ' ') === false) {
$first_name = $users_name;
$last_name = '';
$username = $users_name;
} elseif ($format=='firstname_lastname') {
$username = str_slug($first_name).'_'.str_slug($last_name);
} else {
} elseif ($format=='firstname') {
$username = str_slug($first_name);
list($first_name, $last_name) = explode(" ", $users_name, 2);
// Assume filastname by default
$username = str_slug(substr($first_name, 0, 1).$last_name);
if ($format=='firstname.lastname') {
$username = str_slug($first_name) . '.' . str_slug($last_name);
} elseif ($format=='lastnamefirstinitial') {
$username = str_slug($last_name.substr($first_name, 0, 1));
} elseif ($format=='firstname_lastname') {
$username = str_slug($first_name).'_'.str_slug($last_name);
} elseif ($format=='firstname') {
$username = str_slug($first_name);
}
}
$user['first_name'] = $first_name;
@@ -0,0 +1,127 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckinAccessoryNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->note = '';
$this->target_type = $params['target'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint) {
$notifyBy[] = 'slack';
}
// Make sure the target is a user and that its appropriate to send them an email
if (($this->target_type == \App\Models\User::class) && (($this->item->requireAcceptance() == '1') || ($this->item->getEula())))
{
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
return (new SlackMessage)
->content(':arrow_down: :keyboard: Accessory Checked In')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)->markdown('notifications.markdown.checkin-accessory',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
])
->subject('Accessory checked in');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,126 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
class CheckinAssetNotification extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->note = '';
$this->expected_checkin = '';
$this->target_type = $params['target_type'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
\Log::debug('use slack');
$notifyBy[] = 'slack';
}
// Make sure the target is a user and that its appropriate to send them an email
if ((($this->target->email!='') && ($this->target_type == 'App\Models\User')) && (($this->item->requireAcceptance() == '1') || ($this->item->getEula())))
{
\Log::debug('use email');
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack()
{
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname!='') ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
trans('general.administrator') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
trans('general.status') => $item->assetstatus->name,
trans('general.location') => $item->location->name,
];
return (new SlackMessage)
->content(':arrow_down: :computer: Asset Checked In')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$message = (new MailMessage)->markdown('notifications.markdown.checkin-asset',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
'expected_checkin' => $this->expected_checkin,
])
->subject('Asset checked in');
return $message;
}
}
@@ -0,0 +1,119 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckinLicenseNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->note = '';
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
$notifyBy[] = 'slack';
}
$notifyBy[] = 'mail';
return $notifyBy;
}
public function toSlack($notifiable)
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
return (new SlackMessage)
->content(':arrow_down: :floppy_disk: License Checked In')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)->markdown('notifications.markdown.checkin-license',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
])
->subject('License checked in');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
-97
View File
@@ -1,97 +0,0 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class CheckinNotification 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';
}
$item = $this->params['item'];
if (class_basename(get_class($this->params['item']))=='Asset') {
if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance() == '1'))
|| (method_exists($item, 'getEula') && ($item->getEula()))
) {
$notifyBy[] = 'mail';
}
}
return $notifyBy;
}
public function toSlack($notifiable)
{
return (new SlackMessage)
->success()
->content(class_basename(get_class($this->params['item'])) . " Checked In")
->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'];
$attachment->title($item->name, $item->present()->viewUrl())
->fields($fields);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\sMessages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', 'https://laravel.com')
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,136 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutAccessoryNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->log_id = $params['log_id'];
$this->note = '';
$this->last_checkout = '';
$this->expected_checkin = '';
$this->target_type = $params['target_type'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
$notifyBy[] = 'slack';
}
// Make sure the target is a user and that its appropriate to send them an email
if ((($this->target->email!='') && ($this->target_type == 'App\Models\User')) && (($this->item->requireAcceptance() == '1') || ($this->item->getEula())))
{
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack($notifiable)
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
return (new SlackMessage)
->content(':arrow_up: :keyboard: Accessory Checked Out')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
\Log::debug($this->item->getImageUrl());
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
return (new MailMessage)->markdown('notifications.markdown.checkout-accessory',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => url('/').'/account/accept-asset/'.$this->log_id,
])
->subject(trans('mail.Confirm_accessory_delivery'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,146 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class CheckoutAssetNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->log_id = $params['log_id'];
$this->note = '';
$this->last_checkout = '';
$this->expected_checkin = '';
$this->target_type = $params['target_type'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
if ($this->item->last_checkout) {
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false);
}
if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
\Log::debug('use slack');
$notifyBy[] = 'slack';
}
// Make sure the target is a user and that its appropriate to send them an email
if (($this->target_type == \App\Models\User::class) && (($this->item->requireAcceptance() == '1') || ($this->item->getEula())))
{
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if (($this->expected_checkin) && ($this->expected_checkin!='')) {
$fields['Expected Checkin'] = $this->expected_checkin;
}
return (new SlackMessage)
->content(':arrow_up: :computer: Asset Checked Out')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$message = (new MailMessage)->markdown('notifications.markdown.checkout-asset',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'log_id' => $this->note,
'target' => $this->target,
'fields' => $fields,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => url('/').'/account/accept-asset/'.$this->log_id,
'last_checkout' => $this->last_checkout,
'expected_checkin' => $this->expected_checkin,
])
->subject(trans('mail.Confirm_asset_delivery'));
return $message;
}
}
@@ -0,0 +1,130 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutConsumableNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->log_id = $params['log_id'];
$this->note = '';
$this->last_checkout = '';
$this->expected_checkin = '';
$this->target_type = $params['target_type'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
$notifyBy[] = 'slack';
}
// Make sure the target is a user and that its appropriate to send them an email
if ((($this->target->email!='') && ($this->target_type == 'App\Models\User')) && (($this->item->requireAcceptance() == '1') || ($this->item->getEula())))
{
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack($notifiable)
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
return (new SlackMessage)
->content(':arrow_up: :paperclip: Consumable Checked Out')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
\Log::debug($this->item->getImageUrl());
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
return (new MailMessage)->markdown('notifications.markdown.checkout-consumable',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'log_id' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => url('/').'/account/accept-asset/'.$this->log_id,
])
->subject(trans('mail.Confirm_consumable_delivery'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,123 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutLicenseNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->admin = $params['admin'];
$this->log_id = $params['log_id'];
$this->note = '';
$this->target_type = $params['target_type'];
$this->settings = $params['settings'];
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
$notifyBy[] = 'slack';
}
if ($this->target_type == \App\Models\User::class) {
$notifyBy[] = 'mail';
}
return $notifyBy;
}
public function toSlack($notifiable)
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
return (new SlackMessage)
->content(':arrow_up: :floppy_disk: License Checked Out')
->from($botname)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)->markdown('notifications.markdown.checkout-license',
[
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
])
->subject(trans('mail.Confirm_license_delivery'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
-133
View File
@@ -1,133 +0,0 @@
<?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\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutNotification 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';
}
$item = $this->params['item'];
if (class_basename(get_class($this->params['item']))=='Asset') {
if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance() == '1'))
|| (method_exists($item, 'getEula') && ($item->getEula()))
) {
$notifyBy[] = 'mail';
}
}
return $notifyBy;
}
public function toSlack($notifiable)
{
return (new SlackMessage)
->success()
->content(class_basename(get_class($this->params['item'])) . " Checked Out")
->attachment(function ($attachment) use ($notifiable) {
$item = $this->params['item'];
$admin_user = $this->params['admin'];
$target = $this->params['target'];
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin_user->present()->viewUrl().'|'.$admin_user->present()->fullName().'>'
];
array_key_exists('note', $this->params) && $fields['Notes'] = $this->params['note'];
$attachment->title($item->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)
{
if (class_basename(get_class($this->params['item']))=='Asset') {
//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->expected_checkin->format('Y-m-d') : '',
'item_tag' => $item->asset_tag,
'note' => $this->params['note'],
'item_serial' => $item->serial,
'require_acceptance' => method_exists($item, 'requireAcceptance') ? $item->requireAcceptance() : '',
'log_id' => $this->params['log_id'],
'manufacturer_name' => $item->model->manufacturer->name,
'model_name' => $item->model->name,
'model_number' => $item->model->model_number,
];
if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance() == '1'))
|| (method_exists($item, 'getEula') && ($item->getEula()))
) {
return (new MailMessage)
->view('emails.accept-asset', $data)
->subject(trans('mail.Confirm_asset_delivery'));
}
}
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,67 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class FirstAdminNotification extends Notification
{
use Queueable;
private $_data = array();
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(array $content)
{
$this->_data['email'] = $content['email'];
$this->_data['first_name'] = $content['first_name'];
$this->_data['last_name'] = $content['last_name'];
$this->_data['username'] = $content['username'];
$this->_data['password'] = $content['password'];
$this->_data['url'] = url('/');
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject(trans('mail.welcome', ['name' => $this->_data['first_name'] . ' ' . $this->_data['last_name'] ]))
->markdown('notifications.FirstAdmin', $this->_data);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace App\Notifications;
use App\Models\Settings;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class MailTest extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject(trans('mail.test_email'))
->markdown('notifications.Test');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
@@ -0,0 +1,137 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
class RequestAssetCancelationNotification extends Notification
{
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->note = '';
$this->last_checkout = '';
$this->item_quantity = $params['item_quantity'];
$this->expected_checkin = '';
$this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
false);
$this->settings = Setting::getSettings();
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
if ($this->item->last_checkout) {
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false);
}
if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
\Log::debug('use slack');
$notifyBy[] = 'slack';
}
$notifyBy[] = 'mail';
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$item = $this->item;
$note = $this->note;
$qty = $this->item_quantity;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'QTY' => $qty,
'Canceled By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
];
if (($this->expected_checkin) && ($this->expected_checkin!='')) {
$fields['Expected Checkin'] = $this->expected_checkin;
}
return (new SlackMessage)
->content( trans('mail.a_user_canceled'))
->from($botname)
->attachment(function ($attachment) use ($item, $note, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
[
'item' => $this->item,
'note' => $this->note,
'requested_by' => $this->target,
'requested_date' => $this->requested_date,
'fields' => $fields,
'qty' => $this->item_quantity,
'last_checkout' => $this->last_checkout,
'expected_checkin' => $this->expected_checkin,
'intro_text' => trans('mail.a_user_canceled'),
])
->subject(trans('Item Request Canceled'));
return $message;
}
}
@@ -0,0 +1,134 @@
<?php
namespace App\Notifications;
use App\Models\Setting;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
class RequestAssetNotification extends Notification
{
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct($params)
{
$this->target = $params['target'];
$this->item = $params['item'];
$this->item_type = $params['item_type'];
$this->item_quantity = $params['item_quantity'];
$this->note = '';
$this->last_checkout = '';
$this->expected_checkin = '';
$this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
false);
$this->settings = Setting::getSettings();
if (array_key_exists('note', $params)) {
$this->note = $params['note'];
}
if ($this->item->last_checkout) {
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false);
}
if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false);
}
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->slack_endpoint!='') {
\Log::debug('use slack');
$notifyBy[] = 'slack';
}
$notifyBy[] = 'mail';
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$qty = $this->item_quantity;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'QTY' => $qty,
'Requested By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
];
return (new SlackMessage)
->content(trans('mail.Item_Requested'))
->from($botname)
->attachment(function ($attachment) use ($item, $note, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$fields = [];
// Check if the item has custom fields associated with it
if (($this->item->model) && ($this->item->model->fieldset)) {
$fields = $this->item->model->fieldset->fields;
}
$message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
[
'item' => $this->item,
'note' => $this->note,
'requested_by' => $this->target,
'requested_date' => $this->requested_date,
'fields' => $fields,
'last_checkout' => $this->last_checkout,
'expected_checkin' => $this->expected_checkin,
'intro_text' => trans('mail.a_user_requested'),
'qty' => $this->item_quantity,
])
->subject(trans('mail.Item_Requested'));
return $message;
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
use App\Models\Setting;
class SlackTest extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['slack'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return [
//
];
}
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$settings = Setting::getSettings();
return (new SlackMessage)
->from($settings->slack_botname, ':heart:')
->to($settings->slack_channel)
->image('https://snipeitapp.com/favicon.ico')
->content('Oh hai! Looks like your Slack integration with Snipe-IT is working!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

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