Compare commits

..

630 Commits

Author SHA1 Message Date
snipe
d8a8e1cc09 Bumped version 2018-08-28 11:43:56 -07:00
snipe
522dc1db2a Fixed #6124 2018-08-23 21:05:10 -07:00
snipe
db907815ff Removed check for active in password reset form 2018-08-21 18:40:27 -07:00
snipe
ae6abdddad Check the user is active before displaying password reset
This would only come into play if an inactive user already received a password reset email and then the system was upgraded to prevent those emails from being sent to inactive users
2018-08-14 19:04:47 -07:00
snipe
63c9fbe10c Temporarily disbable notifying users on import 2018-08-14 18:27:46 -07:00
snipe
101dfd01f2 Bumped hash 2018-08-14 18:22:53 -07:00
snipe
5db5134ae0 Set activated to default on when new user is created 2018-08-14 18:14:41 -07:00
snipe
5294489b0e Fixed HTML typo 2018-08-14 18:14:29 -07:00
snipe
05b2b8fb59 Tweaked code/language for password reset 2018-08-14 18:09:33 -07:00
snipe
0100c56046 Only allow password reset if user is active 2018-08-14 17:46:29 -07:00
Jordi Boggiano
e81b221fd1 Fix license identifier (#6043) 2018-08-08 20:36:45 -07:00
snipe
f374ac1bf7 Removed duplicate BACKUP_ENV in example env 2018-08-01 15:31:43 -07:00
snipe
524c6c502e Features/restore deleted cmd (#5982)
* Delete content from login attempts table

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

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

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

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

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

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

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

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

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

* Fixes the checkin_email method on the License model

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

* Improved expiring assets notification

* Nicee low inventory notification

* Refactored stupid language strings

* Oops

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

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

* Remove dead code.

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

* Reverting assetcontroller

Not sure exactly what happened here…

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

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

* Allows search to handle multiple search terms better

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

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

* Remove apiStore method that is unusued since api controllers.

* Use proper model exception

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

* Refactor AssetsController methods.

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

Fix weird missing things.

* Fix Unit test failing after date changes.

* Pass valid string to be translated.

* Some method cleanup for codacy.

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

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

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

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

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

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

* Adds missing dates to $dates in models

* Removes duplicated „where“ conditions

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

* Removes the old text search methods

* Adds back additional conditions to the search

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

* Removes unnecessary check for the deleted_at attribute

* Fixes typo in comments

* suppresses errors from Codacy

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

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

See: https://select2.org/placeholders

* Removes empty option from multiple select

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

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

* Adds the option to clear select2 instances

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

See: https://select2.org/placeholders

* Removes the „Clear selection“ option from select lists

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

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

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

* Fixes checkout notification routing

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

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

* adds a check for EULAs

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

* adds permission checks to custom fieldsets

* adds separate permissions for custom fieldsets

* check for permissions in views

* Removes custom fieldsets from permissions config

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

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

* simplifies the authorization check for the custom fields overview

* removes special handling of custom fieldsets in base policy

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

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

* adds permission checks for depreciations

* adds permission check for all reports

* fixes permissions for departments

* fixes permission naming (edit -> update)

* fixes authorization checking wrong permission in API

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

* adds authorization checks for select2 lists

* adds missing authorization checks for api

* fixes user authorization check for creating users

* adds additional check viewing assets on showing a users assets

* Removes authorization checks for select2 lists

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

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

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

* Changes made per Snipe's direction

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

* Import location checkout. Frontend changes.

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

* Move userinfo method to UsersController

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

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

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

* disable redirection to asset maintenances view

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

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

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

* Update API to use de-normed fields

* Increment counters for checkin;/checkout

* Derp.

* Added request increment/decrementer

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

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

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

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

* fedora support. strict versioning.

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

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

* less spaces!

* rename functions

* move progress spinner variables into progress ()

* $hosts was used in one place

* missed this one

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

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

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

* Adds default value functionality

* Adds built assets

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

* Updates built assets

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

* Added department to custom asset export
Updates build assets

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

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

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

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

* Simplify Logic

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

* Removed old commented items

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

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

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

* Only show asset name in email if it has one

* Refactor requested method to only include non-canceled requests

* Refactored requestable assets to log request and cancelation

* Added softdeletes on checkout requests

* Differentiate between canceling and deleting requests

* Added asset request cancelation notification

* Added timestamps and corrected unique key on requests table

* Improved requests view

* Re-use blade for cancel/request email

* Refactored BS table formatter for requested assets

* Location name min reduced to 2

* Added PAT test as maintenance option

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

* Better slack message

* Added getImageUrl method for assets

* Include qty in request notifications

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

* Removed old asset request/cancel emails

* Added user profile asset request routes

* Added profile controller requested assets method

* Added blade link to requested assets for profile view

* Sort user history desc

* Added requested assets blade

* Added canceled at to checkoutRequest method

* Include qty in request

* Fixed comment, removed allowed_columns

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

* Fixed return type in method doc

* Fixed version number

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

* Seed the Setting object with default values.

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

* Seed the Setting object with default values.

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

* Added “show images in email” to settings

* Added nicer HTML emails

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

* Remove old notification for accessory checkout

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

* Add method for image url for accessories

* Added accessory checkout email blade

* Make accessory email notification on checkout screen consistent with assets

* Added native consumables notifications

* Fixes for asset notification

* Updated notification blades with correct-er fields

* Updated notifications

* License checkin notification - does not work yet

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

* Added alternate “cc” email for admins

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

* Fix tests

* Fixed consumable URL

* Removed unused notification

* Pass target type in params

* Show slack status

* Pass additional parameters

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

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

* Slack is broken :( Trying to fix

Will try a git bisect

* Put preview back into checkout

* Pulled old archaic mail

* Removed debugging

* Fixed wrong email title

* Fixed slack endpoint not firing

* Poobot, we hardly knew ye.

* Removed old, manual mail from API

* Typo :-/

* Code cleanup

* Use defined formatted date in JSON

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

* Removed debugging

* Use date formatter

* Fixed target_type

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

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

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

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

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

* Missing dependency for upgrade.php #4726

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

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

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

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

* Add show URL in Emails condition

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

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

* Update dark-green.css

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

* API tests for asset models and related cleanup/improvements

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

* API Category tests.

* Manufacturers API Test.

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

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

* Add Checkin/Checkout api tests.

* Accessories api test

* Add Companies API Test.

* Return ModelNotFound as a 404.

* Cleanups/simplficiations/updates.

* Locations api test.

* currency and image should be fillable on location.

* Update components api test.

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

* order_number should be fillable in component.

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

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

* Add test for assets method

* API status label test.

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

* Add serial to update.

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

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

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

* Fix functional tests

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

* Importer name/full name fixes.

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

Also update sample csvs and remove an outdated sample.

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

* Use sqlite/different db setup for unit tests.

* Fix assets api test.

* Fix Components API test.

* increase travis memory limit for functional tests.

* Use travis config for api tests as well.

* Fix memory limit file.

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

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

* Use maintenances API for table listings

* NIcer layout for allowed_columns in maintenances API

* Fixed #5014 - bootstrap cookie issues

* Fixed #5015 - bug when saving settings

* Refactored datatable code to use data attributes

* Updated dashboard with new table code

* Added - Order by group user count

* Updated groups to use new table attributes

* New license listing table code

* More bootstrap table implementations

* More BS table refactoring

* Improved bootstrap assigned assets

* New bootstrap for reports

* Misc BS fixes

* FIxed small issue with asset history display

* Removed multisort option

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

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

## What is CodeTriage?

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

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

## Why am I getting this PR?

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

## What does adding a badge accomplish?

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

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

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

## Have a question or comment?

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

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

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

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

* Actually disable the event dispatcher.

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

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

* Actually disable the event dispatcher.

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

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

* revert CustomFieldsetsController

* fieldset associate / disassociate api

* fix variable names and payload

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

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

This results in manager_id being ignored. I can't see any problem with
allowing a user's manager to be modified using the API, so this change
allows it.
2018-01-22 13:12:02 -08:00
snipe
93ec7068df Removed a few additional unused badges 2018-01-20 08:54:49 -08:00
snipe
5f65f993a0 Small refactor for AssetPresenter method 2018-01-20 08:52:50 -08:00
snipe
a9441521a4 Small refactor for Setting method 2018-01-20 08:52:50 -08:00
snipe
2dede67ae9 Removed unusued badges 2018-01-20 08:52:49 -08:00
Daniel Meltzer
6bf17e7d47 Reset the item between rows of import to avoid stale data. (#4868) 2018-01-20 08:40:01 -08:00
1612 changed files with 49618 additions and 12491 deletions

View File

@@ -856,6 +856,800 @@
"contributions": [
"code"
]
},
{
"login": "CronKz",
"name": "CronKz",
"avatar_url": "https://avatars0.githubusercontent.com/u/34064225?v=4",
"profile": "https://github.com/CronKz",
"contributions": [
"code",
"translation"
]
},
{
"login": "tdb",
"name": "Tim Bishop",
"avatar_url": "https://avatars1.githubusercontent.com/u/585486?v=4",
"profile": "https://github.com/tdb",
"contributions": [
"code"
]
},
{
"login": "seanmcilvenna",
"name": "Sean McIlvenna",
"avatar_url": "https://avatars2.githubusercontent.com/u/5384694?v=4",
"profile": "https://www.seanmcilvenna.com",
"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"
]
},
{
"login": "doekman",
"name": "Doeke Zanstra",
"avatar_url": "https://avatars1.githubusercontent.com/u/1086388?v=4",
"profile": "https://github.com/doekman",
"contributions": [
"code"
]
},
{
"login": "SjamonDaal",
"name": "Djamon Staal",
"avatar_url": "https://avatars1.githubusercontent.com/u/4325936?v=4",
"profile": "https://www.sdhd.nl/",
"contributions": [
"code"
]
},
{
"login": "EarlRamirez",
"name": "Earl Ramirez",
"avatar_url": "https://avatars3.githubusercontent.com/u/12306859?v=4",
"profile": "https://github.com/EarlRamirez",
"contributions": [
"code"
]
},
{
"login": "RichardRay",
"name": "Richard Ray Thomas",
"avatar_url": "https://avatars2.githubusercontent.com/u/8671456?v=4",
"profile": "https://github.com/RichardRay",
"contributions": [
"code"
]
},
{
"login": "thelamer",
"name": "Ryan Kuba",
"avatar_url": "https://avatars3.githubusercontent.com/u/1852688?v=4",
"profile": "https://www.taisun.io/",
"contributions": [
"code"
]
},
{
"login": "ParadoxGuitarist",
"name": "Brian Monroe",
"avatar_url": "https://avatars1.githubusercontent.com/u/6751928?v=4",
"profile": "https://github.com/ParadoxGuitarist",
"contributions": [
"code"
]
},
{
"login": "plexorama",
"name": "plexorama",
"avatar_url": "https://avatars1.githubusercontent.com/u/605167?v=4",
"profile": "https://github.com/plexorama",
"contributions": [
"code"
]
},
{
"login": "tilldeeke",
"name": "Till Deeke",
"avatar_url": "https://avatars2.githubusercontent.com/u/1795149?v=4",
"profile": "https://tilldeeke.de",
"contributions": [
"code"
]
},
{
"login": "5quirrel",
"name": "5quirrel",
"avatar_url": "https://avatars0.githubusercontent.com/u/12634129?v=4",
"profile": "https://github.com/5quirrel",
"contributions": [
"code"
]
},
{
"login": "jasonlshelton",
"name": "Jason",
"avatar_url": "https://avatars1.githubusercontent.com/u/13071957?v=4",
"profile": "https://github.com/jasonlshelton",
"contributions": [
"code"
]
},
{
"login": "chemfy",
"name": "Antti",
"avatar_url": "https://avatars3.githubusercontent.com/u/7128321?v=4",
"profile": "https://github.com/chemfy",
"contributions": [
"code"
]
},
{
"login": "DeusMaximus",
"name": "DeusMaximus",
"avatar_url": "https://avatars3.githubusercontent.com/u/10080364?v=4",
"profile": "https://github.com/DeusMaximus",
"contributions": [
"code"
]
},
{
"login": "A-ROYAL",
"name": "a-royal",
"avatar_url": "https://avatars2.githubusercontent.com/u/16384611?v=4",
"profile": "https://github.com/A-ROYAL",
"contributions": [
"translation"
]
},
{
"login": "albertoaldrigo",
"name": "Alberto Aldrigo",
"avatar_url": "https://avatars0.githubusercontent.com/u/5358208?v=4",
"profile": "https://github.com/albertoaldrigo",
"contributions": [
"translation"
]
},
{
"login": "RealEnder",
"name": "Alex Stanev",
"avatar_url": "https://avatars0.githubusercontent.com/u/1412342?v=4",
"profile": "http://alex.stanev.org/blog",
"contributions": [
"translation"
]
},
{
"login": "sirrus",
"name": "Andreas Rehm",
"avatar_url": "https://avatars0.githubusercontent.com/u/177295?v=4",
"profile": "http://devel.itsolution2.de",
"contributions": [
"translation"
]
},
{
"login": "xelan",
"name": "Andreas Erhard",
"avatar_url": "https://avatars0.githubusercontent.com/u/5080535?v=4",
"profile": "https://github.com/xelan",
"contributions": [
"translation"
]
},
{
"login": "angeldeejay",
"name": "Andrés Vanegas Jiménez",
"avatar_url": "https://avatars2.githubusercontent.com/u/142350?v=4",
"profile": "https://github.com/angeldeejay",
"contributions": [
"translation"
]
},
{
"login": "aschiavon91",
"name": "Antonio Schiavon",
"avatar_url": "https://avatars0.githubusercontent.com/u/3910403?v=4",
"profile": "https://github.com/aschiavon91",
"contributions": [
"translation"
]
},
{
"login": "benunter",
"name": "benunter",
"avatar_url": "https://avatars0.githubusercontent.com/u/10464547?v=4",
"profile": "https://github.com/benunter",
"contributions": [
"translation"
]
},
{
"login": "rudashi",
"name": "Borys Żmuda",
"avatar_url": "https://avatars1.githubusercontent.com/u/5038647?v=4",
"profile": "http://catweb24.pl",
"contributions": [
"translation"
]
},
{
"login": "chibacityblues",
"name": "chibacityblues",
"avatar_url": "https://avatars0.githubusercontent.com/u/5539359?v=4",
"profile": "https://github.com/chibacityblues",
"contributions": [
"translation"
]
},
{
"login": "cwlin0416",
"name": "Chien Wei Lin",
"avatar_url": "https://avatars1.githubusercontent.com/u/1954830?v=4",
"profile": "https://github.com/cwlin0416",
"contributions": [
"translation"
]
},
{
"login": "Againstreality",
"name": "Christian Schuster",
"avatar_url": "https://avatars3.githubusercontent.com/u/11700533?v=4",
"profile": "https://github.com/Againstreality",
"contributions": [
"translation"
]
},
{
"login": "kopi-item",
"name": "Christian Stefanus",
"avatar_url": "https://avatars1.githubusercontent.com/u/4308704?v=4",
"profile": "http://chriss.webhostid.com",
"contributions": [
"translation"
]
},
{
"login": "wxcafe",
"name": "wxcafé",
"avatar_url": "https://avatars3.githubusercontent.com/u/3009327?v=4",
"profile": "http://wxcafe.net",
"contributions": [
"translation"
]
},
{
"login": "dpyroc",
"name": "dpyroc",
"avatar_url": "https://avatars3.githubusercontent.com/u/35761525?v=4",
"profile": "https://github.com/dpyroc",
"contributions": [
"translation"
]
},
{
"login": "da-friedl",
"name": "Daniel Friedlmaier",
"avatar_url": "https://avatars1.githubusercontent.com/u/2153639?v=4",
"profile": "http://www.friedlmaier.net",
"contributions": [
"translation"
]
},
{
"login": "danielheene",
"name": "Daniel Heene",
"avatar_url": "https://avatars1.githubusercontent.com/u/2947640?v=4",
"profile": "https://github.com/danielheene",
"contributions": [
"translation"
]
},
{
"login": "danielcb",
"name": "danielcb",
"avatar_url": "https://avatars3.githubusercontent.com/u/319022?v=4",
"profile": "https://github.com/danielcb",
"contributions": [
"translation"
]
},
{
"login": "dominiksenti",
"name": "Dominik Senti",
"avatar_url": "https://avatars3.githubusercontent.com/u/15846537?v=4",
"profile": "https://github.com/dominiksenti",
"contributions": [
"translation"
]
},
{
"login": "EpixFr",
"name": "Eric Gautheron",
"avatar_url": "https://avatars0.githubusercontent.com/u/25570954?v=4",
"profile": "http://www.konectik.com",
"contributions": [
"translation"
]
},
{
"login": "Erlpil",
"name": "Erlend Pilø",
"avatar_url": "https://avatars1.githubusercontent.com/u/5732623?v=4",
"profile": "https://erlpil.com",
"contributions": [
"translation"
]
},
{
"login": "frapposelli",
"name": "Fabio Rapposelli",
"avatar_url": "https://avatars0.githubusercontent.com/u/541832?v=4",
"profile": "http://fabio.technology",
"contributions": [
"translation"
]
},
{
"login": "fgbs",
"name": "Felipe Barros",
"avatar_url": "https://avatars2.githubusercontent.com/u/3605240?v=4",
"profile": "https://github.com/fgbs",
"contributions": [
"translation"
]
},
{
"login": "possebon",
"name": "Fernando Possebon",
"avatar_url": "https://avatars0.githubusercontent.com/u/257745?v=4",
"profile": "https://github.com/possebon",
"contributions": [
"translation"
]
},
{
"login": "gdraque",
"name": "gdraque",
"avatar_url": "https://avatars3.githubusercontent.com/u/2540832?v=4",
"profile": "https://github.com/gdraque",
"contributions": [
"translation"
]
},
{
"login": "georgwallisch",
"name": "Georg Wallisch",
"avatar_url": "https://avatars0.githubusercontent.com/u/23440381?v=4",
"profile": "https://github.com/georgwallisch",
"contributions": [
"translation"
]
},
{
"login": "jgroblesr85",
"name": "Gerardo Robles",
"avatar_url": "https://avatars1.githubusercontent.com/u/9852832?v=4",
"profile": "https://github.com/jgroblesr85",
"contributions": [
"translation"
]
},
{
"login": "mrgluek",
"name": "Gluek",
"avatar_url": "https://avatars2.githubusercontent.com/u/11082640?v=4",
"profile": "https://t.me/Gluek",
"contributions": [
"translation"
]
},
{
"login": "AdnanAbuShahad",
"name": "AdnanAbuShahad",
"avatar_url": "https://avatars0.githubusercontent.com/u/6847946?v=4",
"profile": "https://github.com/AdnanAbuShahad",
"contributions": [
"translation"
]
},
{
"login": "hafidzi",
"name": "Hafidzi My",
"avatar_url": "https://avatars1.githubusercontent.com/u/3580608?v=4",
"profile": "https://hafidzi.my",
"contributions": [
"translation"
]
},
{
"login": "fofwisdom",
"name": "Harim Park",
"avatar_url": "https://avatars2.githubusercontent.com/u/205521?v=4",
"profile": "https://github.com/fofwisdom",
"contributions": [
"translation"
]
},
{
"login": "Kentsson",
"name": "Henrik Kentsson",
"avatar_url": "https://avatars2.githubusercontent.com/u/3333841?v=4",
"profile": "http://www.kentsson.se",
"contributions": [
"translation"
]
},
{
"login": "husnulyaqien",
"name": "Husnul Yaqien",
"avatar_url": "https://avatars0.githubusercontent.com/u/36551034?v=4",
"profile": "https://github.com/husnulyaqien",
"contributions": [
"translation"
]
},
{
"login": "abaalkh",
"name": "Ibrahim",
"avatar_url": "https://avatars1.githubusercontent.com/u/2372747?v=4",
"profile": "http://abaalkhail.org",
"contributions": [
"translation"
]
},
{
"login": "igolman",
"name": "igolman",
"avatar_url": "https://avatars0.githubusercontent.com/u/1389334?v=4",
"profile": "https://github.com/igolman",
"contributions": [
"translation"
]
},
{
"login": "itangiang",
"name": "itangiang",
"avatar_url": "https://avatars1.githubusercontent.com/u/3257070?v=4",
"profile": "https://github.com/itangiang",
"contributions": [
"translation"
]
},
{
"login": "jarby1211",
"name": "jarby1211",
"avatar_url": "https://avatars2.githubusercontent.com/u/14814254?v=4",
"profile": "https://github.com/jarby1211",
"contributions": [
"translation"
]
},
{
"login": "JohnWillker",
"name": "Jhonn Willker",
"avatar_url": "https://avatars3.githubusercontent.com/u/6719357?v=4",
"profile": "http://jwillker.com",
"contributions": [
"translation"
]
},
{
"login": "joxelito94",
"name": "Jose",
"avatar_url": "https://avatars2.githubusercontent.com/u/10983635?v=4",
"profile": "https://github.com/joxelito94",
"contributions": [
"translation"
]
},
{
"login": "laopangzi",
"name": "laopangzi",
"avatar_url": "https://avatars0.githubusercontent.com/u/5206122?v=4",
"profile": "https://github.com/laopangzi",
"contributions": [
"translation"
]
},
{
"login": "lstrojny",
"name": "Lars Strojny",
"avatar_url": "https://avatars2.githubusercontent.com/u/79707?v=4",
"profile": "http://usrportage.de",
"contributions": [
"translation"
]
},
{
"login": "MarcosBL",
"name": "MarcosBL",
"avatar_url": "https://avatars0.githubusercontent.com/u/389801?v=4",
"profile": "http://twitter.com/marcosbl",
"contributions": [
"translation"
]
},
{
"login": "mariejoyacajes",
"name": "marie joy cajes",
"avatar_url": "https://avatars3.githubusercontent.com/u/35664606?v=4",
"profile": "https://github.com/mariejoyacajes",
"contributions": [
"translation"
]
},
{
"login": "msjohansen",
"name": "Mark S. Johansen",
"avatar_url": "https://avatars2.githubusercontent.com/u/3052816?v=4",
"profile": "http://www.markjohansen.dk",
"contributions": [
"translation"
]
},
{
"login": "stubben",
"name": "Martin Stub",
"avatar_url": "https://avatars2.githubusercontent.com/u/982885?v=4",
"profile": "http://martinstub.dk",
"contributions": [
"translation"
]
},
{
"login": "meyerf99",
"name": "Meyer Flavio",
"avatar_url": "https://avatars2.githubusercontent.com/u/28959963?v=4",
"profile": "https://github.com/meyerf99",
"contributions": [
"translation"
]
},
{
"login": "MicaelRodrigues",
"name": "Micael Rodrigues",
"avatar_url": "https://avatars3.githubusercontent.com/u/796443?v=4",
"profile": "https://github.com/MicaelRodrigues",
"contributions": [
"translation"
]
},
{
"login": "mikaelssen",
"name": "Mikael Rasmussen",
"avatar_url": "https://avatars0.githubusercontent.com/u/10481331?v=4",
"profile": "http://rubixy.com/",
"contributions": [
"translation"
]
},
{
"login": "IxFail",
"name": "IxFail",
"avatar_url": "https://avatars1.githubusercontent.com/u/1544552?v=4",
"profile": "https://github.com/IxFail",
"contributions": [
"translation"
]
},
{
"login": "MohammedFota",
"name": "Mohammed Fota",
"avatar_url": "https://avatars3.githubusercontent.com/u/18483118?v=4",
"profile": "http://www.mohammedfota.com",
"contributions": [
"translation"
]
},
{
"login": "omego",
"name": "Moayad Alserihi",
"avatar_url": "https://avatars0.githubusercontent.com/u/227080?v=4",
"profile": "https://github.com/omego",
"contributions": [
"translation"
]
},
{
"login": "saymd",
"name": "saymd",
"avatar_url": "https://avatars0.githubusercontent.com/u/1680266?v=4",
"profile": "https://github.com/saymd",
"contributions": [
"translation"
]
},
{
"login": "pooot",
"name": "Patrik Larsson",
"avatar_url": "https://avatars0.githubusercontent.com/u/1826808?v=4",
"profile": "https://nordsken.se",
"contributions": [
"translation"
]
},
{
"login": "drcryo",
"name": "drcryo",
"avatar_url": "https://avatars1.githubusercontent.com/u/20584746?v=4",
"profile": "https://github.com/drcryo",
"contributions": [
"translation"
]
},
{
"login": "pawel1615",
"name": "pawel1615",
"avatar_url": "https://avatars1.githubusercontent.com/u/19408004?v=4",
"profile": "https://github.com/pawel1615",
"contributions": [
"translation"
]
},
{
"login": "bodrovics",
"name": "bodrovics",
"avatar_url": "https://avatars2.githubusercontent.com/u/23340468?v=4",
"profile": "https://github.com/bodrovics",
"contributions": [
"translation"
]
},
{
"login": "priatna",
"name": "priatna",
"avatar_url": "https://avatars0.githubusercontent.com/u/3257654?v=4",
"profile": "https://github.com/priatna",
"contributions": [
"translation"
]
},
{
"login": "ProfFan",
"name": "Fan Jiang",
"avatar_url": "https://avatars1.githubusercontent.com/u/5358374?v=4",
"profile": "https://amayume.net",
"contributions": [
"translation"
]
},
{
"login": "ragnarcx",
"name": "ragnarcx",
"avatar_url": "https://avatars1.githubusercontent.com/u/22555451?v=4",
"profile": "https://github.com/ragnarcx",
"contributions": [
"translation"
]
},
{
"login": "reinvanhaaren",
"name": "Rein van Haaren",
"avatar_url": "https://avatars2.githubusercontent.com/u/18654582?v=4",
"profile": "http://www.reinvanhaaren.nl/",
"contributions": [
"translation"
]
},
{
"login": "dheche",
"name": "Teguh Dwicaksana",
"avatar_url": "https://avatars1.githubusercontent.com/u/386672?v=4",
"profile": "http://dheche.songolimo.net",
"contributions": [
"translation"
]
},
{
"login": "FRaccie",
"name": "fraccie",
"avatar_url": "https://avatars2.githubusercontent.com/u/2572552?v=4",
"profile": "https://github.com/FRaccie",
"contributions": [
"translation"
]
},
{
"login": "vinzruzell",
"name": "vinzruzell",
"avatar_url": "https://avatars0.githubusercontent.com/u/35182720?v=4",
"profile": "https://github.com/vinzruzell",
"contributions": [
"translation"
]
},
{
"login": "vipsystem",
"name": "Kevin Austin",
"avatar_url": "https://avatars1.githubusercontent.com/u/7883603?v=4",
"profile": "http://kevinaustin.com",
"contributions": [
"translation"
]
},
{
"login": "wira-sandy",
"name": "Wira Sandy",
"avatar_url": "https://avatars3.githubusercontent.com/u/3861828?v=4",
"profile": "http://azuraweb.xyz",
"contributions": [
"translation"
]
},
{
"login": "GrayHoax",
"name": "Илья",
"avatar_url": "https://avatars2.githubusercontent.com/u/8663789?v=4",
"profile": "https://github.com/GrayHoax",
"contributions": [
"translation"
]
},
{
"login": "godusevpn",
"name": "GodUseVPN",
"avatar_url": "https://avatars3.githubusercontent.com/u/30119111?v=4",
"profile": "https://github.com/godusevpn",
"contributions": [
"translation"
]
},
{
"login": "EngrZhou",
"name": "周周",
"avatar_url": "https://avatars1.githubusercontent.com/u/745576?v=4",
"profile": "https://github.com/EngrZhou",
"contributions": [
"translation"
]
},
{
"login": "takuy",
"name": "Sam",
"avatar_url": "https://avatars3.githubusercontent.com/u/1631095?v=4",
"profile": "https://github.com/takuy",
"contributions": [
"code"
]
},
{
"login": "Azerothian",
"name": "Azerothian",
"avatar_url": "https://avatars1.githubusercontent.com/u/264022?v=4",
"profile": "https://www.illisian.com.au",
"contributions": [
"code"
]
}
]
}

View File

@@ -105,3 +105,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

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
.env.unit-tests Normal file
View File

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

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

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

View File

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

32
.github/stale.yml vendored
View File

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

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

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

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

View File

@@ -18,7 +18,9 @@ patch \
curl \
vim \
git \
cron \
mysql-client \
cron \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -64,7 +66,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 ###################

View File

@@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=develop)](https://travis-ci.org/snipe/snipe-it) [![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready+for+dev&title=Ready+for+development)](http://waffle.io/snipe/snipe-it) [![Maintenance](https://img.shields.io/maintenance/yes/2017.svg)]() [![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) [![Zenhub](https://img.shields.io/badge/Shipping_faster_with-ZenHub-5e60ba.svg)](https://zenhub.io) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-92-orange.svg?style=flat-square)](#contributors)
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=master)](https://travis-ci.org/snipe/snipe-it) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-180-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
@@ -50,6 +50,20 @@ 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)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-II instance
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:
@@ -69,7 +83,19 @@ 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://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

84
Vagrantfile vendored Normal file
View File

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

View File

@@ -16,7 +16,7 @@ class LdapSync extends Command
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--summary} {--json_summary}';
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
/**
* The console command description.
@@ -67,36 +67,25 @@ class LdapSync extends Command
$summary = array();
try {
$results = Ldap::findLdapUsers();
} catch (\Exception $e) {
if ($this->option('json_summary')) {
try {
if ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
} else {
$search_base = null;
}
$results = Ldap::findLdapUsers($search_base);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::error($e);
return [];
}
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
$ldap_ou_lengths = array();
foreach ($ldap_ou_locations as $location) {
$ldap_ou_lengths[] = strlen($location["ldap_ou"]);
}
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
}
// Inject location information fields
for ($i = 0; $i < $results["count"]; $i++) {
$results[$i]["ldap_location_override"] = false;
$results[$i]["location_id"] = 0;
}
/* Determine which location to assign users to by default. */
$location = NULL;
if ($this->option('location')!='') {
$location = Location::where('name', '=', $this->option('location'))->first();
@@ -106,40 +95,61 @@ class LdapSync extends Command
$location = Location::where('id', '=', $this->option('location_id'))->first();
LOG::debug('Location ID '.$this->option('location_id').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
} else {
$location = NULL;
}
}
if (!isset($location)) {
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
$location_users[$i]["ldap_location_override"] = true;
$location_users[$i]["location_id"] = $ldap_loc["id"];
$usernames[] = $location_users[$i][$ldap_result_username][0];
/* Process locations with explicitly defined OUs, if doing a full import. */
if ($this->option('base_dn')=='') {
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
$ldap_ou_lengths = array();
foreach ($ldap_ou_locations as $location) {
$ldap_ou_lengths[] = strlen($location["ldap_ou"]);
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
}
// Inject location information fields
for ($i = 0; $i < $results["count"]; $i++) {
$results[$i]["ldap_location_override"] = false;
$results[$i]["location_id"] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
$location_users[$i]["ldap_location_override"] = true;
$location_users[$i]["location_id"] = $ldap_loc["id"];
$usernames[] = $location_users[$i][$ldap_result_username][0];
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
}
/* Create user account entries in Snipe-IT */
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$pass = bcrypt($tmp_pass);
for ($i = 0; $i < $results["count"]; $i++) {
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
@@ -179,8 +189,14 @@ class LdapSync extends Command
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} else if ($location) {
$user->location_id = e($location->id);
} elseif ((isset($location)) && (!empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
$user->notes = 'Imported from LDAP';
@@ -207,9 +223,9 @@ class LdapSync extends Command
if ($this->option('summary')) {
for ($x = 0; $x < count($summary); $x++) {
if ($summary[$x]['status']=='error') {
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was not imported: '.$summary[$x]['note']);
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was '.strtoupper($summary[$x]['createorupdate']).'.');
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} else if ($this->option('json_summary')) {

View File

@@ -79,6 +79,7 @@ class PaveIt extends Command
DB::statement('delete from accessories_users');
DB::statement('delete from asset_logs');
DB::statement('delete from asset_maintenances');
DB::statement('delete from login_attempts');
DB::statement('delete from asset_uploads');
DB::statement('delete from action_logs');
DB::statement('delete from checkout_requests');

View File

@@ -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';

View File

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

View File

@@ -4,8 +4,10 @@ namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\Setting;
use Illuminate\Console\Command;
use App\Notifications\ExpectedCheckinNotification;
use App\Notifications\ExpectedCheckinAdminNotification;
use Carbon\Carbon;
class SendExpectedCheckinAlerts extends Command
@@ -42,21 +44,28 @@ class SendExpectedCheckinAlerts extends Command
*/
public function fire()
{
$settings = Setting::getSettings();
$whenNotify = Carbon::now()->addDays(7);
$assets = Asset::with('assignedTo')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
$assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
$this->info($whenNotify.' is deadline');
$this->info($assets->count().' assets');
foreach ($assets as $asset) {
if ($asset->assignedTo && $asset->checkoutOutToUser()) {
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
//$this->info($asset);
if ($asset->assigned && $asset->checkedOutToUser()) {
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
}
}
// Send a rollup to the admin, if settings dictate
$recipient = new \App\Models\Recipients\AlertRecipient();
if (($assets) && ($assets->count() > 0) && ($settings->alert_email!='')) {
$recipient->notify(new ExpectedCheckinAdminNotification($assets));
}

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,8 @@ class Kernel extends ConsoleKernel
Commands\ResetDemoSettings::class,
Commands\SyncAssetLocations::class,
Commands\RegenerateAssetTags::class,
Commands\SyncAssetCounters::class,
Commands\RestoreDeletedUsers::class,
];
/**

View File

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

View File

@@ -137,95 +137,6 @@ class Helper
return floatval($floatString);
}
/**
* Get the list of models in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function modelList()
{
$models = AssetModel::with('manufacturer')->get();
$model_array[''] = trans('general.select_model');
foreach ($models as $model) {
$model_array[$model->id] = $model->present()->modelName();
}
return $model_array;
}
/**
* Get the list of companies in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function companyList()
{
$company_list = array('' => trans('general.select_company')) + DB::table('companies')
->orderBy('name', 'asc')
->pluck('name', 'id')
->toArray();
return $company_list;
}
/**
* Get the list of categories in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function categoryList($category_type = null)
{
$categories = Category::orderBy('name', 'asc')
->whereNull('deleted_at')
->orderBy('name', 'asc');
if (!empty($category_type)) {
$categories = $categories->where('category_type', '=', $category_type);
}
$category_list = array('' => trans('general.select_category')) + $categories->pluck('name', 'id')->toArray();
return $category_list;
}
/**
* Get the list of categories in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function departmentList()
{
$departments = Department::orderBy('name', 'asc')
->whereNull('deleted_at')
->orderBy('name', 'asc');
return array('' => trans('general.select_department')) + $departments->pluck('name', 'id')->toArray();
}
/**
* Get the list of suppliers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function suppliersList()
{
$supplier_list = array('' => trans('general.select_supplier')) + Supplier::orderBy('name', 'asc')
->orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $supplier_list;
}
/**
* Get the list of status labels in an array to make a dropdown menu
*
@@ -235,42 +146,11 @@ 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;
}
/**
* Get the list of locations in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function locationsList()
{
$location_list = array('' => trans('general.select_location')) + Location::orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $location_list;
}
/**
* Get the list of manufacturers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function manufacturerList()
{
$manufacturer_list = array('' => trans('general.select_manufacturer')) +
Manufacturer::orderBy('name', 'asc')
->pluck('name', 'id')->toArray();
return $manufacturer_list;
}
/**
* Get the list of status label types in an array to make a dropdown menu
*
@@ -289,24 +169,6 @@ class Helper
return $statuslabel_types;
}
/**
* Get the list of managers in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function managerList()
{
$manager_list = array('' => trans('general.select_user')) +
User::where('deleted_at', '=', null)
->orderBy('last_name', 'asc')
->orderBy('first_name', 'asc')->get()
->pluck('complete_name', 'id')->toArray();
return $manager_list;
}
/**
* Get the list of depreciations in an array to make a dropdown menu
*
@@ -330,58 +192,17 @@ class Helper
*/
public static function categoryTypeList()
{
$category_types = array('' => '','accessory' => 'Accessory', 'asset' => 'Asset', 'consumable' => 'Consumable','component' => 'Component');
$category_types = array(
'' => '',
'accessory' => 'Accessory',
'asset' => 'Asset',
'consumable' => 'Consumable',
'component' => 'Component',
'license' => 'License'
);
return $category_types;
}
/**
* Get the list of users in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function usersList()
{
$users_list = array( '' => trans('general.select_user')) +
Company::scopeCompanyables(User::where('deleted_at', '=', null))
->where('show_in_list', '=', 1)
->orderBy('last_name', 'asc')
->orderBy('first_name', 'asc')->get()
->pluck('complete_name', 'id')->toArray();
return $users_list;
}
/**
* Get the list of assets in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function assetsList()
{
$assets_list = array('' => trans('general.select_asset')) + Asset::orderBy('name', 'asc')
->whereNull('deleted_at')
->pluck('name', 'id')->toArray();
return $assets_list;
}
/**
* Get the detailed list of assets in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return array
*/
public static function detailedAssetList()
{
$assets = array('' => trans('general.select_asset')) + Company::scopeCompanyables(Asset::with('assignedTo', 'model'), 'assets.company_id')->get()->pluck('detailed_name', 'id')->toArray();
return $assets;
}
/**
* Get the list of custom fields in an array to make a dropdown menu
*
@@ -406,7 +227,7 @@ class Helper
{
$keys = array_keys(CustomField::$PredefinedFormats);
$stuff = array_combine($keys, $keys);
return $stuff+["" => trans('admin/custom_fields/general.custom_format')];
return $stuff;
}
/**
@@ -469,7 +290,7 @@ class Helper
$all_count = 0;
foreach ($consumables as $consumable) {
$avail = $consumable->qty - $consumable->consumable_assignment_count; //$consumable->numRemaining();
$avail = $consumable->numRemaining();
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($consumable->qty > 0) {
$percent = number_format((($avail / $consumable->qty) * 100), 0);
@@ -690,7 +511,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;
@@ -731,5 +552,124 @@ class Helper
}
// Nicked from Drupal :)
// Returns a file size limit in bytes based on the PHP upload_max_filesize
// and post_max_size
public static function file_upload_max_size() {
static $max_size = -1;
if ($max_size < 0) {
// Start with post_max_size.
$post_max_size = Helper::parse_size(ini_get('post_max_size'));
if ($post_max_size > 0) {
$max_size = $post_max_size;
}
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}
}
return $max_size;
}
public static function file_upload_max_size_readable() {
static $max_size = -1;
if ($max_size < 0) {
// Start with post_max_size.
$post_max_size = Helper::parse_size(ini_get('post_max_size'));
if ($post_max_size > 0) {
$max_size = ini_get('post_max_size');
}
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = ini_get('upload_max_filesize');
}
}
return $max_size;
}
public static function parse_size($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
}
public static function filetype_icon($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return "fa fa-file-image-o";
break;
case 'doc':
case 'docx':
return "fa fa-file-word-o";
break;
case 'xls':
case 'xlsx':
return "fa fa-file-excel-o";
break;
case 'zip':
case 'rar':
return "fa fa-file-archive-o";
break;
case 'pdf':
return "fa fa-file-pdf-o";
break;
case 'txt':
return "fa fa-file-text-o";
break;
case 'lic':
return "fa fa-floppy-o";
break;
default:
return "fa fa-file-o";
}
}
return "fa fa-file-o";
}
public static function show_file_inline($filename) {
$extension = substr(strrchr($filename,'.'),1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
return true;
break;
default:
return false;
}
}
return false;
}
}

View File

@@ -12,7 +12,6 @@ use DB;
use Gate;
use Input;
use Lang;
use Mail;
use Redirect;
use Illuminate\Http\Request;
use Slack;
@@ -167,10 +166,8 @@ class AccessoriesController extends Controller
$accessory->supplier_id = request('supplier_id');
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
@@ -259,10 +256,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 +317,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 +363,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 +381,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'));
}

View File

@@ -24,7 +24,7 @@ class AccessoriesController extends Controller
$this->authorize('view', Accessory::class);
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
$accessories = Accessory::whereNull('accessories.deleted_at')->with('category', 'company', 'manufacturer', 'users', 'location');
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
if ($request->has('search')) {
$accessories = $accessories->TextSearch($request->input('search'));

View File

@@ -40,10 +40,26 @@ class AssetMaintenancesController extends Controller
$maintenances = $maintenances->TextSearch(e($request->input('search')));
}
if ($request->has('asset_id')) {
$maintenances->where('asset_id', '=', $request->input('asset_id'));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['id','title','asset_maintenance_time','asset_maintenance_type','cost','start_date','completion_date','notes','user_id'];
$allowed_columns = [
'id',
'title',
'asset_maintenance_time',
'asset_maintenance_type',
'cost',
'start_date',
'completion_date',
'notes',
'asset_tag',
'asset_name',
'user_id'
];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
@@ -51,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;

View File

@@ -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');
@@ -131,13 +145,13 @@ class AssetModelsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', AssetModel::class);
$this->authorize('update', AssetModel::class);
$assetmodel = AssetModel::findOrFail($id);
$assetmodel->fill($request->all());
$assetmodel->fieldset_id = $request->get("custom_fieldset_id");
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/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')));
}
@@ -194,6 +208,7 @@ class AssetModelsController extends Controller
'models.category_id',
])->with('manufacturer','category');
$settings = \App\Models\Setting::getSettings();
if ($request->has('search')) {
$assetmodels = $assetmodels->SearchByManufacturerOrCat($request->input('search'));
@@ -202,8 +217,24 @@ class AssetModelsController extends Controller
$assetmodels = $assetmodels->OrderCategory('ASC')->OrderManufacturer('ASC')->orderby('models.name', 'asc')->orderby('models.model_number', 'asc')->paginate(50);
foreach ($assetmodels as $assetmodel) {
$assetmodel->use_text = (($assetmodel->category) ? e($assetmodel->category->name) : '').': '.$assetmodel->present()->modelName;
$assetmodel->use_image = ($assetmodel->image) ? url('/').'/uploads/models/'.$assetmodel->image : null;
$assetmodel->use_text = '';
if ($settings->modellistCheckedValue('category')) {
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
}
if ($settings->modellistCheckedValue('manufacturer')) {
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
}
$assetmodel->use_text .= e($assetmodel->name);
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
}
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? url('/').'/uploads/models/'.$assetmodel->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($assetmodels);

View File

@@ -77,12 +77,15 @@ class AssetsController extends Controller
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkout_counter',
'checkin_counter',
'requests_counter',
];
$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
@@ -95,10 +98,6 @@ class AssetsController extends Controller
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// These are used by the API to query against specific ID numbers.
// They are also used by the individual searches on detail pages like
// locations, etc.
@@ -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')) {
@@ -199,17 +201,24 @@ class AssetsController extends Controller
break;
default:
if ($settings->show_archived_in_list!='1') {
if ((!$request->has('status_id')) && ($settings->show_archived_in_list!='1')) {
// terrible workaround for complex-query Laravel bug in fulltext
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.archived', '=', 0);
});
// If there is a status ID, don't take show_archived_in_list into consideration
} else {
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id");
});
}
}
if (count($filter) > 0) {
if ((!is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter);
} elseif ($request->has('search')) {
$assets->TextSearch($request->input('search'));
@@ -244,6 +253,8 @@ class AssetsController extends Controller
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
@@ -266,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.
*
@@ -276,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);
}
}
@@ -305,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'));
@@ -412,7 +465,7 @@ class AssetsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('create', Asset::class);
$this->authorize('update', Asset::class);
if ($asset = Asset::find($id)) {
($request->has('model_id')) ?
@@ -549,7 +602,7 @@ class AssetsController extends Controller
$error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type')=='asset') {
$target = Asset::where('id','!=',$assetId)->find(request('assigned_asset'));
$target = Asset::where('id','!=',$asset_id)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id;
// Override with the asset's location_id if it has one
if ($target->location_id!='') {
@@ -621,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')));
}
@@ -712,5 +742,51 @@ 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)
{
$this->authorize('viewRequestable', Asset::class);
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$assets->TextSearch($request->input('search'));
switch ($request->input('sort')) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
default:
$assets->orderBy('assets.created_at', $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
}

View File

@@ -21,10 +21,10 @@ class CategoriesController extends Controller
public function index(Request $request)
{
$this->authorize('view', Category::class);
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count', 'image'];
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count','licenses_count', 'image'];
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email','image'])
->withCount('assets', 'accessories', 'consumables', 'components');
->withCount('assets', 'accessories', 'consumables', 'components','licenses');
if ($request->has('search')) {
$categories = $categories->TextSearch($request->input('search'));
@@ -92,7 +92,7 @@ class CategoriesController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Category::class);
$this->authorize('update', Category::class);
$category = Category::findOrFail($id);
$category->fill($request->all());

View File

@@ -104,7 +104,7 @@ class CompaniesController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Company::class);
$this->authorize('update', Company::class);
$company = Company::findOrFail($id);
$company->fill($request->all());

View File

@@ -24,7 +24,7 @@ class ComponentsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Component::class);
$components = Company::scopeCompanyables(Component::select('components.*')->whereNull('components.deleted_at')
$components = Company::scopeCompanyables(Component::select('components.*')
->with('company', 'location', 'category'));
if ($request->has('search')) {
@@ -35,6 +35,14 @@ 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'));
}
if ($request->has('location_id')) {
$components->where('location_id','=',$request->input('location_id'));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
@@ -93,13 +101,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')));
}
@@ -114,7 +120,7 @@ class ComponentsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Component::class);
$this->authorize('update', Component::class);
$component = Component::findOrFail($id);
$component->fill($request->all());

View File

@@ -24,7 +24,6 @@ class ConsumablesController extends Controller
$this->authorize('index', Consumable::class);
$consumables = Company::scopeCompanyables(
Consumable::select('consumables.*')
->whereNull('consumables.deleted_at')
->with('company', 'location', 'category', 'users', 'manufacturer')
);
@@ -121,7 +120,7 @@ class ConsumablesController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Consumable::class);
$this->authorize('update', Consumable::class);
$consumable = Consumable::findOrFail($id);
$consumable->fill($request->all());
@@ -179,7 +178,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() : '',
];
}

View File

@@ -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());
}
/**
@@ -59,9 +57,15 @@ class CustomFieldsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', CustomField::class);
$this->authorize('update', CustomField::class);
$field = CustomField::findOrFail($id);
$data = $request->all();
/**
* Updated values for the field,
* without the "field_encrypted" flag, preventing the change of encryption status
* @var array
*/
$data = $request->except(['field_encrypted']);
$validator = Validator::make($data, $field->validationRules());
if ($validator->fails()) {
@@ -108,6 +112,9 @@ class CustomFieldsController extends Controller
public function postReorder(Request $request, $id)
{
$fieldset = CustomFieldset::find($id);
$this->authorize('update', $fieldset);
$fields = array();
$order_array = array();
@@ -125,6 +132,40 @@ class CustomFieldsController extends Controller
}
public function associate(Request $request, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset_id = $request->input('fieldset_id');
foreach ($field->fieldset as $fieldset) {
if ($fieldset->id == $fieldset_id) {
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
}
$fieldset = CustomFieldset::findOrFail($fieldset_id);
$fieldset->fields()->attach($field->id, ["required" => ($request->input('required') == "on"), "order" => $request->input('order', $fieldset->fields->count())]);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
public function disassociate(Request $request, $field_id)
{
$this->authorize('update', CustomFieldset::class);
$field = CustomField::findOrFail($field_id);
$fieldset_id = $request->input('fieldset_id');
foreach ($field->fieldset as $fieldset) {
if ($fieldset->id == $fieldset_id) {
$fieldset->fields()->detach($field->id);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
}
$fieldset = CustomFieldset::findOrFail($fieldset_id);
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
}
/**
* Delete a custom field.
@@ -137,6 +178,8 @@ class CustomFieldsController extends Controller
{
$field = CustomField::findOrFail($field_id);
$this->authorize('delete', $field);
if ($field->fieldset->count() >0) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Field is in use.'));
}

View File

@@ -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());
}
@@ -81,7 +79,7 @@ class CustomFieldsetsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', CustomFieldset::class);
$this->authorize('update', CustomFieldset::class);
$fieldset = CustomFieldset::findOrFail($id);
$fieldset->fill($request->all());
@@ -126,7 +124,7 @@ class CustomFieldsetsController extends Controller
{
$this->authorize('delete', CustomFieldset::class);
$fieldset = CustomFieldset::findOrFail($id);
$modelsCount = $fieldset->models->count();
$fieldsCount = $fieldset->fields->count();
@@ -141,7 +139,41 @@ class CustomFieldsetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, 'Unspecified error'));
}
/**
* Return JSON containing a list of fields belonging to a fieldset.
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param $fieldsetId
* @return string JSON
*/
public function fields($id)
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($id);
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
}
/**
* Return JSON containing a list of fields belonging to a fieldset with the
* default values for a given model
*
* @param $modelId
* @param $fieldsetId
* @return string JSON
*/
public function fieldsWithDefaultValues($fieldsetId, $modelId)
{
$this->authorize('view', CustomFieldset::class);
$set = CustomFieldset::findOrFail($fieldsetId);
$fields = $set->fields;
return (new CustomFieldsTransformer)->transformCustomFieldsWithDefaultValues($fields, $modelId, $fields->count());
}
}

View File

@@ -22,17 +22,17 @@ class DepartmentsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Department::class);
$allowed_columns = ['id','name','image'];
$allowed_columns = ['id','name','image','users_count'];
$departments = Department::select([
'id',
'name',
'location_id',
'company_id',
'manager_id',
'created_at',
'updated_at',
'image'
'departments.id',
'departments.name',
'departments.location_id',
'departments.company_id',
'departments.manager_id',
'departments.created_at',
'departments.updated_at',
'departments.image'
])->with('users')->with('location')->with('manager')->with('company')->withCount('users');
if ($request->has('search')) {
@@ -43,7 +43,18 @@ class DepartmentsController extends Controller
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$departments->orderBy($sort, $order);
switch ($request->input('sort')) {
case 'location':
$departments->OrderLocation($order);
break;
case 'manager':
$departments->OrderManager($order);
break;
default:
$departments->orderBy($sort, $order);
break;
}
$total = $departments->count();
$departments = $departments->skip($offset)->take($limit)->get();
@@ -103,6 +114,8 @@ class DepartmentsController extends Controller
{
$department = Department::findOrFail($id);
$this->authorize('delete', $department);
if ($department->users->count() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/departments/message.assoc_users')));
}

View File

@@ -88,7 +88,7 @@ class DepreciationsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Depreciation::class);
$this->authorize('update', Depreciation::class);
$depreciation = Depreciation::findOrFail($id);
$depreciation->fill($request->all());

View File

@@ -20,7 +20,7 @@ class GroupsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Group::class);
$allowed_columns = ['id','name','created_at'];
$allowed_columns = ['id','name','created_at', 'users_count'];
$groups = Group::select('id','name','permissions','created_at','updated_at')->withCount('users');
@@ -88,7 +88,7 @@ class GroupsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Group::class);
$this->authorize('update', Group::class);
$group = Group::findOrFail($id);
$group->fill($request->all());

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

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
{
@@ -23,7 +25,7 @@ class LicensesController extends Controller
public function index(Request $request)
{
$this->authorize('view', License::class);
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier')->withCount('freeSeats'));
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats'));
if ($request->has('company_id')) {
@@ -62,6 +64,10 @@ class LicensesController extends Controller
$licenses->where('supplier_id','=',$request->input('supplier_id'));
}
if ($request->has('category_id')) {
$licenses->where('category_id','=',$request->input('category_id'));
}
if ($request->has('depreciation_id')) {
$licenses->where('depreciation_id','=',$request->input('depreciation_id'));
}
@@ -88,11 +94,14 @@ class LicensesController extends Controller
case 'supplier':
$licenses = $licenses->leftJoin('suppliers', 'licenses.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
break;
case 'category':
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
break;
case 'company':
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
break;
default:
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','license_name','license_email','free_seats_count','seats'];
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$licenses = $licenses->orderBy($sort, $order);
break;
@@ -121,6 +130,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 +149,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 +168,16 @@ class LicensesController extends Controller
public function update(Request $request, $id)
{
//
$this->authorize('update', License::class);
$license = License::findOrFail($id);
$license->fill($request->all());
if ($license->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.update.success')));
}
return Helper::formatStandardApiResponse('error', null, $license->getErrors());
}
/**
@@ -167,6 +191,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')));
}
/**
@@ -182,6 +223,8 @@ class LicensesController extends Controller
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
$offset = request('offset', 0);

View File

@@ -23,8 +23,8 @@ class LocationsController extends Controller
$this->authorize('view', Location::class);
$allowed_columns = [
'id','name','address','address2','city','state','country','zip','created_at',
'updated_at','parent_id', 'manager_id','image',
'assigned_assets_count','users_count','assets_count'];
'updated_at','manager_id','image',
'assigned_assets_count','users_count','assets_count','currency'];
$locations = Location::with('parent', 'manager', 'childLocations')->select([
'locations.id',
@@ -49,11 +49,25 @@ class LocationsController extends Controller
$locations = $locations->TextSearch($request->input('search'));
}
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$locations->orderBy($sort, $order);
switch ($request->input('sort')) {
case 'parent':
$locations->OrderParent($order);
break;
case 'manager':
$locations->OrderManager($order);
break;
default:
$locations->orderBy($sort, $order);
break;
}
$total = $locations->count();
$locations = $locations->skip($offset)->take($limit)->get();
@@ -108,7 +122,7 @@ class LocationsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Location::class);
$this->authorize('update', Location::class);
$location = Location::findOrFail($id);
$location->fill($request->all());

View File

@@ -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';
@@ -93,7 +99,7 @@ class ManufacturersController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Manufacturer::class);
$this->authorize('update', Manufacturer::class);
$manufacturer = Manufacturer::findOrFail($id);
$manufacturer->fill($request->all());

View File

@@ -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;
}
}

View File

@@ -18,7 +18,8 @@ class ReportsController extends Controller
*/
public function index(Request $request)
{
$this->authorize('reports.view');
$actionlogs = Actionlog::with('item', 'user', 'target','location');
if ($request->has('search')) {
@@ -28,7 +29,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,13 +40,21 @@ 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'
'created_at',
'target_id',
'user_id',
'action_type',
'note'
];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
$offset = request('offset', 0);
$limit = request('limit', 50);
$total = $actionlogs->count();

View File

@@ -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);

View File

@@ -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'];
$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;
@@ -101,7 +101,7 @@ class StatuslabelsController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Statuslabel::class);
$this->authorize('update', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$request->except('deployable', 'pending','archived');
@@ -160,6 +160,7 @@ class StatuslabelsController extends Controller
public function getAssetCountByStatuslabel()
{
$this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets')->get();
@@ -237,6 +238,9 @@ class StatuslabelsController extends Controller
*/
public function checkIfDeployable($id) {
$statuslabel = Statuslabel::findOrFail($id);
$this->authorize('view', $statuslabel);
if ($statuslabel->getStatuslabelType()=='deployable') {
return '1';
}

View File

@@ -25,8 +25,8 @@ 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')
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
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');
if ($request->has('search')) {
@@ -93,7 +93,7 @@ class SuppliersController extends Controller
*/
public function update(Request $request, $id)
{
$this->authorize('edit', Supplier::class);
$this->authorize('update', Supplier::class);
$supplier = Supplier::findOrFail($id);
$supplier->fill($request->all());

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'));
}
@@ -103,7 +105,7 @@ class UsersController extends Controller
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
'country', 'zip'
'country', 'zip', 'id'
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
@@ -140,7 +142,7 @@ class UsersController extends Controller
'users.avatar',
'users.email',
]
);
)->where('show_in_list', '=', '1');
$users = Company::scopeCompanyables($users);
@@ -189,10 +191,13 @@ class UsersController extends Controller
*/
public function store(SaveUserRequest $request)
{
$this->authorize('view', User::class);
$this->authorize('create', 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')));
@@ -226,7 +231,8 @@ class UsersController extends Controller
*/
public function update(SaveUserRequest $request, $id)
{
$this->authorize('edit', User::class);
$this->authorize('update', User::class);
$user = User::findOrFail($id);
$user->fill($request->all());
@@ -285,7 +291,49 @@ class UsersController extends Controller
public function assets($id)
{
$this->authorize('view', User::class);
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
$this->authorize('view', Asset::class);
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
/**
* Reset the user's two-factor status
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $userId
* @return string JSON
*/
public function postTwoFactorReset(Request $request)
{
$this->authorize('update', User::class);
if ($request->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);
}
/**
* Get info on the current user.
*
* @author [Juan Font] [<juanfontalonso@gmail.com>]
* @since [v4.4.2]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function getCurrentUserInfo(Request $request)
{
return response()->json($request->user());
}
}

View File

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

View File

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

View File

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

View File

@@ -63,85 +63,6 @@ class AssetMaintenancesController extends Controller
}
/**
* Generates the JSON response for asset maintenances listing view.
*
* @see AssetMaintenancesController::getIndex() method that generates view
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
* @return String JSON
*/
public function getDatatable(Request $request)
{
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company', 'admin');
if (Input::has('search')) {
$maintenances = $maintenances->TextSearch(e($request->input('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['id','title','asset_maintenance_time','asset_maintenance_type','cost','start_date','completion_date','notes','user_id'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
switch ($sort) {
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
default:
$maintenances = $maintenances->orderBy($sort, $order);
break;
}
$maintenancesCount = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
$rows = array();
$settings = Setting::getSettings();
foreach ($maintenances as $maintenance) {
$actions = '';
if (Gate::allows('update', Asset::class)) {
$actions .= Helper::generateDatatableButton('edit', route('maintenances.edit', $maintenance->id));
$actions .= Helper::generateDatatableButton(
'delete',
route('maintenances.destroy', $maintenance->id),
$enabled = true,
trans('admin/asset_maintenances/message.delete.confirm'),
$maintenance->title
);
}
if (($maintenance->cost) && (isset($maintenance->asset)) && ($maintenance->asset->location) && ($maintenance->asset->location->currency!='')) {
$maintenance_cost = $maintenance->asset->location->currency.$maintenance->cost;
} else {
$maintenance_cost = $settings->default_currency.$maintenance->cost;
}
$rows[] = array(
'id' => $maintenance->id,
'asset_name' => ($maintenance->asset) ? (string)link_to_route('maintenances.show', $maintenance->asset->present()->Name(), ['maintenance' => $maintenance->asset->id]) : 'Deleted Asset' ,
'title' => $maintenance->title,
'notes' => $maintenance->notes,
'supplier' => ($maintenance->supplier) ? (string)link_to_route('suppliers.show', $maintenance->supplier->name, ['maintenance'=>$maintenance->supplier->id]) : 'Deleted Supplier',
'cost' => $maintenance_cost,
'asset_maintenance_type' => e($maintenance->asset_maintenance_type),
'start_date' => $maintenance->start_date,
'asset_maintenance_time' => $maintenance->asset_maintenance_time,
'completion_date' => $maintenance->completion_date,
'user_id' => ($maintenance->admin) ? (string)link_to_route('users.show', $maintenance->admin->present()->fullName(), ['user'=>$maintenance->admin->id]) : '',
'actions' => $actions,
'company' => ($maintenance->asset->company) ? $maintenance->asset->company->name : ''
);
}
$data = array('total' => $maintenancesCount, 'rows' => $rows);
return $data;
}
/**
* Returns a form view to create a new asset maintenance.
@@ -192,7 +113,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
if (!Company::isCurrentUserHasAccess($asset)) {
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
return static::getInsufficientPermissionsRedirect();
}
@@ -265,9 +186,7 @@ class AssetMaintenancesController extends Controller
// Get Supplier List
// Render the view
return view('asset_maintenances/edit')
->with('asset_list', Helper::detailedAssetList())
->with('selectedAsset', null)
->with('supplier_list', Helper::suppliersList())
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', $assetMaintenance);

View File

@@ -110,52 +110,16 @@ 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'));
}
return redirect()->back()->withInput()->withErrors($model->getErrors());
}
/**
* Validates and stores new Asset Model data created from the
* modal form on the Asset Creation view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @param Request $request
* @return String JSON
*/
public function apiStore(Request $request)
{
//COPYPASTA!!!! FIXME
$this->authorize('create', AssetModel::class);
$model = new AssetModel;
$settings=Input::all();
$settings['eol']= null;
$model->name=$request->input('name');
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->model_number = $request->input('model_number');
$model->user_id = Auth::id();
$model->notes = $request->input('notes');
$model->eol= null;
if ($request->input('fieldset_id')=='') {
$model->fieldset_id = null;
} else {
$model->fieldset_id = e($request->input('fieldset_id'));
}
if ($model->save()) {
return JsonResponse::create($model);
} else {
return JsonResponse::create(["error" => "Failed validation: ".print_r($model->getErrors()->all('<li>:message</li>'), true)], 500);
}
}
/**
* Returns a view containing the asset model edit form.
*
@@ -166,7 +130,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 +154,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 +170,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;
@@ -362,9 +332,7 @@ class AssetModelsController extends Controller
// Show the page
$view = View::make('models/edit');
$view->with('category_list', Helper::categoryList('asset'));
$view->with('depreciation_list', Helper::depreciationList());
$view->with('manufacturer_list', Helper::manufacturerList());
$view->with('item', $model);
$view->with('clone_model', $model_to_clone);
return $view;
@@ -398,7 +366,7 @@ class AssetModelsController extends Controller
*/
public function postBulkEdit(Request $request)
{
$models_raw_array = Input::get('ids');
// Make sure some IDs have been selected
@@ -423,13 +391,8 @@ class AssetModelsController extends Controller
$nochange = ['NC' => 'No Change'];
$fieldset_list = $nochange + Helper::customFieldsetList();
$depreciation_list = $nochange + Helper::depreciationList();
$category_list = $nochange + Helper::categoryList('asset');
$manufacturer_list = $nochange + Helper::manufacturerList();
return view('models/bulk-edit', compact('models'))
->with('manufacturer_list', $manufacturer_list)
->with('category_list', $category_list)
->with('fieldset_list', $fieldset_list)
->with('depreciation_list', $depreciation_list);
}
@@ -531,4 +494,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();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -55,7 +55,10 @@ class ForgotPasswordController extends Controller
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$request->only('email')
array_merge(
$request->only('email'),
['activated' => '1']
)
);
if ($response === \Password::RESET_LINK_SENT) {

View File

@@ -50,17 +50,42 @@ 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.");
private function login_via_ldap(Request $request)
$pos = strpos($remote_user, '\\');
if ($pos > 0) {
$remote_user = substr($remote_user, $pos + 1);
};
try {
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
LOG::debug("Remote user auth lookup complete");
if(!is_null($user)) Auth::login($user, true);
} catch(Exception $e) {
LOG::error("There was an error authenticating the Remote user: " . $e->getMessage());
}
}
}
private function loginViaLdap(Request $request)
{
LOG::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
@@ -72,7 +97,7 @@ class LoginController extends Controller
}
// Check if the user already exists in the database and was imported via LDAP
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->first();
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->where('activated', '=', '1')->first();
LOG::debug("Local auth lookup complete");
// The user does not exist in the database. Try to get them from LDAP.
@@ -114,6 +139,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 +163,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
@@ -148,7 +177,7 @@ class LoginController extends Controller
if (!$user) {
LOG::debug("Authenticating user against database.");
// Try to log the user in
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
if (!Auth::attempt(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
if (!$lockedOut) {
$this->incrementLoginAttempts($request);
@@ -252,7 +281,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!');
}

View File

@@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use App\Models\User;
use Illuminate\Http\Request;
class ResetPasswordController extends Controller
{
@@ -36,4 +38,8 @@ class ResetPasswordController extends Controller
{
$this->middleware('guest');
}
}

View File

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

View File

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

View File

@@ -29,6 +29,8 @@ final class CompaniesController extends Controller
*/
public function index()
{
$this->authorize('view', Company::class);
return view('companies/index')->with('companies', Company::all());
}
@@ -41,6 +43,8 @@ final class CompaniesController extends Controller
*/
public function create()
{
$this->authorize('create', Company::class);
return view('companies/edit')->with('item', new Company);
}
@@ -54,6 +58,8 @@ final class CompaniesController extends Controller
*/
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Company::class);
$company = new Company;
$company->name = $request->input('name');
@@ -90,6 +96,9 @@ final class CompaniesController extends Controller
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('companies/edit')->with('item', $item);
}
@@ -108,6 +117,8 @@ final class CompaniesController extends Controller
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
}
$this->authorize('update', $company);
$company->name = $request->input('name');
$old_image = $company->image;
@@ -164,6 +175,9 @@ final class CompaniesController extends Controller
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
} else {
$this->authorize('delete', $company);
try {
$company->delete();
return redirect()->route('companies.index')

View File

@@ -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);
@@ -202,19 +202,6 @@ class ComponentsController extends Controller
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
}
public function postBulk($componentId = null)
{
//$this->authorize('checkout', $component)
echo 'Stubbed - not yet complete';
}
public function postBulkSave($componentId = null)
{
//$this->authorize('edit', Component::class);
echo 'Stubbed - not yet complete';
}
/**
* Return a view to display component information.
*

View File

@@ -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'));

View File

@@ -37,6 +37,7 @@ class CustomFieldsController extends Controller
*/
public function index()
{
$this->authorize('view', CustomField::class);
$fieldsets = CustomFieldset::with("fields", "models")->get();
$fields = CustomField::with("fieldset")->get();
@@ -57,6 +58,7 @@ class CustomFieldsController extends Controller
*/
public function create()
{
$this->authorize('create', CustomField::class);
return view("custom_fields.fields.edit")->with('field', new CustomField());
}
@@ -72,17 +74,20 @@ class CustomFieldsController extends Controller
*/
public function store(CustomFieldRequest $request)
{
$this->authorize('create', CustomField::class);
$field = new CustomField([
"name" => $request->get("name"),
"element" => $request->get("element"),
"help_text" => $request->get("help_text"),
"field_values" => $request->get("field_values"),
"field_encrypted" => $request->get("field_encrypted", 0),
"show_in_email" => $request->get("show_in_email", 0),
"user_id" => Auth::user()->id
]);
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
if ($request->has("custom_format")) {
$field->format = e($request->get("custom_format"));
} else {
$field->format = e($request->get("format"));
@@ -91,7 +96,6 @@ class CustomFieldsController extends Controller
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
} else {
// dd($field);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
}
@@ -109,6 +113,8 @@ class CustomFieldsController extends Controller
{
$field = CustomField::find($field_id);
$this->authorize('update', $field);
if ($field->fieldset()->detach($fieldset_id)) {
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
@@ -127,6 +133,8 @@ class CustomFieldsController extends Controller
{
$field = CustomField::find($field_id);
$this->authorize('delete', $field);
if ($field->fieldset->count()>0) {
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
} else {
@@ -148,6 +156,9 @@ class CustomFieldsController extends Controller
public function edit($id)
{
$field = CustomField::find($id);
$this->authorize('update', $field);
return view("custom_fields.fields.edit")->with('field', $field);
}
@@ -166,12 +177,14 @@ class CustomFieldsController extends Controller
{
$field = CustomField::find($id);
$this->authorize('update', $field);
$field->name = e($request->get("name"));
$field->element = e($request->get("element"));
$field->field_values = e($request->get("field_values"));
$field->field_encrypted = e($request->get("field_encrypted", 0));
$field->user_id = Auth::user()->id;
$field->help_text = $request->get("help_text");
$field->show_in_email = $request->get("show_in_email", 0);
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
$field->format = e($request->get("custom_format"));
@@ -179,7 +192,6 @@ class CustomFieldsController extends Controller
$field->format = e($request->get("format"));
}
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
}

View File

@@ -38,6 +38,8 @@ class CustomFieldsetsController extends Controller
{
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
$this->authorize('view', $cfset);
if ($cfset) {
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
@@ -68,6 +70,8 @@ class CustomFieldsetsController extends Controller
*/
public function create()
{
$this->authorize('create', CustomFieldset::class);
return view("custom_fields.fieldsets.edit");
}
@@ -81,6 +85,8 @@ class CustomFieldsetsController extends Controller
*/
public function store(Request $request)
{
$this->authorize('create', CustomFieldset::class);
$cfset = new CustomFieldset(
[
"name" => e($request->get("name")),
@@ -141,6 +147,8 @@ class CustomFieldsetsController extends Controller
{
$fieldset = CustomFieldset::find($id);
$this->authorize('delete', $fieldset);
if ($fieldset) {
$models = AssetModel::where("fieldset_id", "=", $id);
if ($models->count() == 0) {
@@ -169,6 +177,8 @@ class CustomFieldsetsController extends Controller
$set = CustomFieldset::find($id);
$this->authorize('update', $set);
foreach ($set->fields as $field) {
if ($field->id == Input::get('field_id')) {
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);

View File

@@ -83,6 +83,8 @@ class DepartmentsController extends Controller
{
$department = Department::find($id);
$this->authorize('view', $department);
if (isset($department->id)) {
return view('departments/view', compact('department'));
}
@@ -100,6 +102,8 @@ class DepartmentsController extends Controller
*/
public function create()
{
$this->authorize('create', Department::class);
return view('departments/edit')->with('item', new Department);
}
@@ -118,6 +122,8 @@ class DepartmentsController extends Controller
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.not_found'));
}
$this->authorize('delete', $department);
if ($department->users->count() > 0) {
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.assoc_users'));
}
@@ -141,16 +147,20 @@ class DepartmentsController extends Controller
if (is_null($item = Department::find($id))) {
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('departments/edit', compact('item'));
}
public function update(ImageUploadRequest $request, $id) {
$this->authorize('create', Department::class);
if (is_null($department = Department::find($id))) {
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
$this->authorize('update', $department);
$department->fill($request->all());
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);

View File

@@ -31,6 +31,8 @@ class DepreciationsController extends Controller
*/
public function index()
{
$this->authorize('view', Depreciation::class);
// Show the page
return view('depreciations/index', compact('depreciations'));
}
@@ -46,6 +48,8 @@ class DepreciationsController extends Controller
*/
public function create()
{
$this->authorize('create', Depreciation::class);
// Show the page
return view('depreciations/edit')->with('item', new Depreciation);
}
@@ -62,6 +66,8 @@ class DepreciationsController extends Controller
*/
public function store(Request $request)
{
$this->authorize('create', Depreciation::class);
// create a new instance
$depreciation = new Depreciation();
// Depreciation data
@@ -94,6 +100,8 @@ class DepreciationsController extends Controller
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('depreciations/edit', compact('item'));
}
@@ -116,6 +124,8 @@ class DepreciationsController extends Controller
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
}
$this->authorize('update', $depreciation);
// Depreciation data
$depreciation->name = $request->input('name');
$depreciation->months = $request->input('months');
@@ -145,6 +155,8 @@ class DepreciationsController extends Controller
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.not_found'));
}
$this->authorize('delete', $depreciation);
if ($depreciation->has_models() > 0) {
// Redirect to the asset management page
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.assoc_users'));
@@ -171,6 +183,8 @@ class DepreciationsController extends Controller
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
}
$this->authorize('view', $depreciation);
return view('depreciations/view', compact('depreciation'));
}

View File

@@ -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
{

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\AssetFileRequest;
use Assets;
use Illuminate\Support\Facades\Session;
use Input;
@@ -107,6 +108,7 @@ class LicensesController extends Controller
$license->seats = $request->input('seats');
$license->serial = $request->input('serial');
$license->supplier_id = $request->input('supplier_id');
$license->category_id = $request->input('category_id');
$license->termination_date = $request->input('termination_date');
$license->user_id = Auth::id();
@@ -182,6 +184,7 @@ class LicensesController extends Controller
$license->seats = e($request->input('seats'));
$license->manufacturer_id = $request->input('manufacturer_id');
$license->supplier_id = $request->input('supplier_id');
$license->category_id = $request->input('category_id');
if ($license->save()) {
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
@@ -262,31 +265,40 @@ class LicensesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param Request $request
* @param int $licenseId
* @param int $seatId
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckout(Request $request, $licenseId)
public function postCheckout(Request $request, $licenseId, $seatId = null)
{
// Check that the license is valid
if ($license = License::where('id',$licenseId)->first()) {
if ($license = License::where('id', $licenseId)->first()) {
// If the license is valid, check that there is an available seat
if ($license->getAvailSeatsCountAttribute() < 1) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
}
// Get the next available seat for this license
$next = $license->freeSeat();
if (!$next) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
if (!$seatId) {
// Get the next available seat for this license
$next = $license->freeSeat();
if (!$next) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
}
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
}
} else {
$licenseSeat = LicenseSeat::where('id', '=', $seatId)->first();
if (!$licenseSeat) {
return redirect()->route('licenses.index')->with('error', 'License seat is not available for checkout');
}
}
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
}
$this->authorize('checkout', $license);
@@ -419,6 +431,7 @@ class LicensesController extends Controller
if (!$return_to) {
$return_to = Asset::find($licenseSeat->asset_id);
}
// Update the asset data
$licenseSeat->assigned_to = null;
$licenseSeat->asset_id = null;
@@ -492,7 +505,7 @@ class LicensesController extends Controller
* @param int $licenseId
* @return \Illuminate\Http\RedirectResponse
*/
public function postUpload(Request $request, $licenseId = null)
public function postUpload(AssetFileRequest $request, $licenseId = null)
{
$license = License::find($licenseId);
// the license is valid
@@ -501,21 +514,11 @@ class LicensesController extends Controller
if (isset($license->id)) {
$this->authorize('update', $license);
if (Input::hasFile('licensefile')) {
if (Input::hasFile('file')) {
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'
);
$validator = Validator::make(array('licensefile'=> $file), $rules);
if ($validator->fails()) {
return redirect()->back()->with('error', trans('admin/licenses/message.upload.invalidfiles'));
}
foreach (Input::file('file') as $file) {
$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 +584,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 +594,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')));
}

View File

@@ -114,42 +114,6 @@ class LocationsController extends Controller
return redirect()->back()->withInput()->withErrors($location->getErrors());
}
/**
* Validates and stores a new location created via the Create Asset form modal.
*
* @todo Check if a Form Request would work better here.
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see AssetsController::getCreate() method that makes the form
* @since [v1.0]
* @return String JSON
*/
public function apiStore(Request $request)
{
$this->authorize('create', Location::class);
$new['currency']=Setting::first()->default_currency;
// create a new location instance
$location = new Location();
// Save the location data
$location->name = $request->input('name');
$location->currency = Setting::first()->default_currency; //e(Input::get('currency'));
$location->address = ''; //e(Input::get('address'));
// $location->address2 = e(Input::get('address2'));
$location->city = $request->input('city');
$location->state = '';//e(Input::get('state'));
$location->country = $request->input('country');
// $location->zip = e(Input::get('zip'));
$location->user_id = Auth::id();
// Was the location created?
if ($location->save()) {
return JsonResponse::create($location);
}
// failure
return JsonResponse::create(["error" => "Failed validation: ".print_r($location->getErrors(), true)], 500);
}
/**
* Makes a form view to edit location information.
@@ -162,7 +126,7 @@ class LocationsController extends Controller
*/
public function edit($locationId = null)
{
$this->authorize('edit', Location::class);
$this->authorize('update', Location::class);
// Check if the location exists
if (is_null($item = Location::find($locationId))) {
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
@@ -175,8 +139,7 @@ class LocationsController extends Controller
$location_options = array('' => 'Top Level') + $location_options;
return view('locations/edit', compact('item'))
->with('location_options', $location_options)
->with('manager_list', Helper::managerList());
->with('location_options', $location_options);
}
@@ -191,7 +154,7 @@ class LocationsController extends Controller
*/
public function update(ImageUploadRequest $request, $locationId = null)
{
$this->authorize('edit', Location::class);
$this->authorize('update', Location::class);
// Check if the location exists
if (is_null($location = Location::find($locationId))) {
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));

View File

@@ -67,7 +67,7 @@ class ManufacturersController extends Controller
public function store(ImageUploadRequest $request)
{
$this->authorize('edit', Manufacturer::class);
$this->authorize('create', Manufacturer::class);
$manufacturer = new Manufacturer;
$manufacturer->name = $request->input('name');
$manufacturer->user_id = Auth::user()->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'));
}

View File

@@ -13,9 +13,7 @@ class ModalController extends Controller
}
function model() {
return view('modals.model')
->with('manufacturer', Helper::manufacturerList())
->with('category', Helper::categoryList('asset'));
return view('modals.model');
}
function statuslabel() {

View File

@@ -45,15 +45,21 @@ class ProfileController extends Controller
{
$user = Auth::user();
$user->first_name = Input::get('first_name');
$user->last_name = Input::get('last_name');
$user->website = Input::get('website');
$user->location_id = Input::get('location_id');
$user->gravatar = Input::get('gravatar');
$user->locale = Input::get('locale');
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->website = $request->input('website');
$user->gravatar = $request->input('gravatar');
if (!config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en');
}
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled=='1') && (!config('app.lock_passwords')))) {
$user->two_factor_optin = Input::get('two_factor_optin', '0');
$user->two_factor_optin = $request->input('two_factor_optin', '0');
}
if (Gate::allows('self.edit_location') && (!config('app.lock_passwords'))) {
$user->location_id = $request->input('location_id');
}
if (Input::file('avatar')) {

View File

@@ -7,6 +7,7 @@ use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetMaintenance;
use App\Models\CustomField;
use App\Models\Depreciation;
use App\Models\License;
use App\Models\Setting;
use Carbon\Carbon;
@@ -25,6 +26,14 @@ use Illuminate\Http\Request;
*/
class ReportsController extends Controller
{
/**
* Checks for correct permissions
*/
public function __construct() {
parent::__construct();
$this->authorize('reports.view');
}
/**
* Returns a view that displays the accessories report.
@@ -81,136 +90,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.
*
@@ -221,11 +100,12 @@ class ReportsController extends Controller
public function getDeprecationReport()
{
$depreciations = Depreciation::get();
// Grab all the assets
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'assetlog', 'company', 'model.category', 'model.depreciation')
->orderBy('created_at', 'DESC')->get();
return view('reports/depreciation', compact('assets'));
return view('reports/depreciation', compact('assets'))->with('depreciations',$depreciations);
}
/**
@@ -492,6 +372,28 @@ 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');
@@ -506,6 +408,14 @@ class ReportsController extends Controller
$header[] = 'Employee No.';
}
if ($request->has('manager')) {
$header[] = trans('admin/users/table.manager');
}
if ($request->has('department')) {
$header[] = trans('general.department');
}
if ($request->has('status')) {
$header[] = trans('general.status');
}
@@ -565,6 +475,11 @@ class ReportsController extends Controller
$assets->where('assets.location_id', $request->input('by_location_id'));
}
if ($request->has('by_rtd_location_id')) {
\Log::debug('RTD location should match: '.$request->input('by_rtd_location_id'));
$assets->where('assets.rtd_location_id', $request->input('by_rtd_location_id'));
}
if ($request->has('by_supplier_id')) {
$assets->where('assets.supplier_id', $request->input('by_supplier_id'));
}
@@ -600,6 +515,10 @@ class ReportsController extends Controller
if (($request->has('created_start')) && ($request->has('created_end'))) {
$assets->whereBetween('assets.created_at', [$request->input('created_start'), $request->input('created_end')]);
}
if (($request->has('expected_checkin_start')) && ($request->has('expected_checkin_end'))) {
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
}
$assets->orderBy('assets.created_at', 'ASC')->chunk(500, function($assets) use($handle, $customfields, $request) {
@@ -624,7 +543,7 @@ class ReportsController extends Controller
}
if ($request->has('category')) {
$row[] = ($asset->model->category) ? $asset->model->category->name : '';
$row[] = (($asset->model) && ($asset->model->category)) ? $asset->model->category->name : '';
}
if ($request->has('manufacturer')) {
@@ -660,6 +579,29 @@ class ReportsController extends Controller
$row[] = ($asset->location) ? $asset->location->present()->name() : '';
}
if ($request->has('location_address')) {
$row[] = ($asset->location) ? $asset->location->address : '';
$row[] = ($asset->location) ? $asset->location->address2 : '';
$row[] = ($asset->location) ? $asset->location->city : '';
$row[] = ($asset->location) ? $asset->location->state : '';
$row[] = ($asset->location) ? $asset->location->country : '';
$row[] = ($asset->location) ? $asset->location->zip : '';
}
if ($request->has('rtd_location')) {
$row[] = ($asset->defaultLoc) ? $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());
@@ -683,6 +625,23 @@ class ReportsController extends Controller
}
}
if ($request->has('manager')) {
if ($asset->checkedOutToUser()) {
$row[] = (($asset->assignedto) && ($asset->assignedto->manager)) ? $asset->assignedto->manager->present()->fullName : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
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.')' : '';
}

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'));
});
});*/
}
@@ -314,8 +324,18 @@ class SettingsController extends Controller
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
$setting->modellist_displays = '';
if (($request->has('show_in_model_list')) && (count($request->input('show_in_model_list')) > 0))
{
$setting->modellist_displays = implode(',', $request->input('show_in_model_list'));
}
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
$setting->load_remote = $request->input('load_remote', '0');
$setting->unique_serial = $request->input('unique_serial', '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');
@@ -328,6 +348,7 @@ class SettingsController extends Controller
$setting->default_eula_text = $request->input('default_eula_text');
$setting->thumbnail_max_h = $request->input('thumbnail_max_h');
$setting->privacy_policy_link = $request->input('privacy_policy_link');
if (Input::get('per_page')!='') {
$setting->per_page = $request->input('per_page');
@@ -375,7 +396,13 @@ class SettingsController extends Controller
$setting->brand = $request->input('brand', '1');
$setting->header_color = $request->input('header_color');
$setting->support_footer = $request->input('support_footer');
$setting->version_footer = $request->input('version_footer');
$setting->footer_text = $request->input('footer_text');
$setting->skin = $request->input('skin');
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
// Only allow the site name and CSS to be changed if lock_passwords is false
@@ -455,6 +482,11 @@ class SettingsController extends Controller
$setting->two_factor_enabled = null;
} else {
$setting->two_factor_enabled = $request->input('two_factor_enabled');
# 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');
}
}
@@ -463,6 +495,7 @@ class SettingsController extends Controller
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
$setting->pwd_secure_complexity = '';
if ($request->has('pwd_secure_complexity')) {
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
}
@@ -506,7 +539,9 @@ class SettingsController extends Controller
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
$setting->locale = $request->input('locale', 'en');
if (!config('app.lock_passwords')) {
$setting->locale = $request->input('locale', 'en');
}
$setting->default_currency = $request->input('default_currency', '$');
$setting->date_display_format = $request->input('date_display_format');
$setting->time_display_format = $request->input('time_display_format');
@@ -550,8 +585,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');
@@ -757,22 +795,34 @@ class SettingsController extends Controller
if (Input::has('labels_display_name')) {
if ($request->has('labels_display_name')) {
$setting->labels_display_name = 1;
} else {
$setting->labels_display_name = 0;
}
if (Input::has('labels_display_serial')) {
if ($request->has('labels_display_serial')) {
$setting->labels_display_serial = 1;
} else {
$setting->labels_display_serial = 0;
}
if (Input::has('labels_display_tag')) {
if ($request->has('labels_display_tag')) {
$setting->labels_display_tag = 1;
} else {
$setting->labels_display_tag = 0;
}
if ($request->has('labels_display_tag')) {
$setting->labels_display_tag = 1;
} else {
$setting->labels_display_tag = 0;
}
if ($request->has('labels_display_model')) {
$setting->labels_display_model = 1;
} else {
$setting->labels_display_model = 0;
}
if ($setting->save()) {

View File

@@ -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'));
@@ -103,36 +103,6 @@ class StatuslabelsController extends Controller
return redirect()->back()->withInput()->withErrors($statusLabel->getErrors());
}
/**
* @param Request $request
* @return JsonResponse
*/
public function apiStore(Request $request)
{
$this->authorize('create', Statuslabel::class);
$statuslabel = new Statuslabel();
if (!$request->has('statuslabel_types')) {
return JsonResponse::create(["error" => trans('validation.statuslabel_type')], 500);
}
$statustype = Statuslabel::getStatuslabelTypesForDB(Input::get('statuslabel_types'));
$statuslabel->name = Input::get('name');
$statuslabel->user_id = Auth::id();
$statuslabel->notes = '';
$statuslabel->deployable = $statustype['deployable'];
$statuslabel->pending = $statustype['pending'];
$statuslabel->archived = $statustype['archived'];
if ($statuslabel->isValid()) {
$statuslabel->save();
// Redirect to the new Statuslabel page
return JsonResponse::create($statuslabel);
}
return JsonResponse::create(["error" => $statuslabel->getErrors()->first()], 500);
}
/**
* Statuslabel update.
*
@@ -185,6 +155,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?

View File

@@ -97,23 +97,6 @@ class SuppliersController extends Controller
return redirect()->back()->withInput()->withErrors($supplier->getErrors());
}
/**
* @param Request $request
* @return JsonResponse
*/
public function apiStore(Request $request)
{
$this->authorize('create', Supplier::class);
$supplier = new Supplier;
$supplier->name = $request->input('name');
$supplier->user_id = Auth::id();
if ($supplier->save()) {
return JsonResponse::create($supplier);
}
return JsonResponse::create(["error" => "Failed validation: ".print_r($supplier->getErrors(), true)], 500);
}
/**
* Supplier update.
*

View File

@@ -1,26 +1,30 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\AssetFileRequest;
use App\Helpers\Helper;
use App\Http\Requests\AssetFileRequest;
use App\Http\Requests\SaveUserRequest;
use App\Models\Accessory;
use App\Models\LicenseSeat;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Group;
use App\Models\Company;
use App\Models\Location;
use App\Models\License;
use App\Models\Setting;
use App\Http\Requests\SaveUserRequest;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Models\User;
use App\Models\Group;
use App\Models\Ldap;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Artisan;
use Auth;
use Config;
use Crypt;
use DB;
use Gate;
use HTML;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Input;
use Lang;
use League\Csv\Reader;
@@ -29,11 +33,9 @@ use Redirect;
use Response;
use Str;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use URL;
use View;
use Illuminate\Http\Request;
use Gate;
use Artisan;
/**
* This controller handles all actions related to Users for
@@ -84,8 +86,11 @@ class UsersController extends Controller
$userPermissions = Helper::selectedPermissionsArray($permissions, Input::old('permissions', array()));
$permissions = $this->filterDisplayable($permissions);
$user = new User;
$user->activated = 1;
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
->with('user', new User);
->with('user', $user);
}
/**
@@ -110,7 +115,7 @@ class UsersController extends Controller
$user->last_name = $request->input('last_name');
$user->locale = $request->input('locale');
$user->employee_num = $request->input('employee_num');
$user->activated = $request->input('activated', $user->activated);
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle');
$user->phone = $request->input('phone');
$user->location_id = $request->input('location_id', null);
@@ -146,78 +151,23 @@ 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'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
}
/**
* JSON handler for creating a user through a modal popup
*
* @todo Handle validation more graciously
* @author [B. Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
* @return string JSON
*/
public function apiStore(SaveUserRequest $request)
{
$this->authorize('create', User::class);
$user = new User;
$inputs = Input::except('csrf_token', 'password_confirm', 'groups', 'email_user');
$inputs['activated'] = true;
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->username = $request->input('username');
$user->email = $request->input('email');
$user->department_id = $request->input('department_id', null);
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
$user->activated = true;
// Was the user created?
if ($user->save()) {
if (Input::get('email_user') == 1) {
// Send the credentials through email
$data = array();
$data['email'] = $request->input('email');
$data['first_name'] = $request->input('first_name');
$data['last_name'] = $request->input('last_name');
$data['password'] = $request->input('password');
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);
}
return JsonResponse::create(["error" => "Failed validation: " . print_r($user->getErrors(), true)], 500);
}
/**
* Returns a view that displays the edit user form
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param $permissions
* @return View
* @internal param int $id
*/
private function filterDisplayable($permissions)
{
@@ -230,6 +180,15 @@ class UsersController extends Controller
return $output;
}
/**
* Returns a view that displays the edit user form
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param $permissions
* @return View
* @internal param int $id
*/
public function edit($id)
{
@@ -291,9 +250,10 @@ class UsersController extends Controller
}
}
} catch (UserNotFoundException $e) {
$error = trans('admin/users/message.user_not_found', compact('id'));
return redirect()->route('users.index')->with('error', $error);
} catch (ModelNotFoundException $e) {
return redirect()->route('users.index')
->with('error', trans('admin/users/message.user_not_found', compact('id')));
}
@@ -301,8 +261,6 @@ class UsersController extends Controller
if (Auth::user()->isSuperUser()) {
if ($request->has('groups')) {
$user->groups()->sync($request->input('groups'));
} else {
$user->groups()->sync(array());
}
}
@@ -319,7 +277,7 @@ class UsersController extends Controller
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
$user->locale = $request->input('locale');
$user->employee_num = $request->input('employee_num');
$user->activated = $request->input('activated', $user->activated);
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle', null);
$user->phone = $request->input('phone');
$user->location_id = $request->input('location_id', null);
@@ -331,6 +289,7 @@ class UsersController extends Controller
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->activated = $request->input('activated', 0);
$user->zip = $request->input('zip', null);
@@ -375,7 +334,7 @@ class UsersController extends Controller
{
try {
// Get user information
$user = User::find($id);
$user = User::findOrFail($id);
// Authorize takes care of many of our logic checks now.
$this->authorize('delete', User::class);
@@ -385,9 +344,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) {
@@ -413,7 +372,7 @@ class UsersController extends Controller
// Redirect to the user management page
return redirect()->route('users.index')->with('success', $success);
} catch (UserNotFoundException $e) {
} catch (ModelNotFoundException $e) {
// Prepare the error message
$error = trans('admin/users/message.user_not_found', compact('id'));
// Redirect to the user management page
@@ -431,23 +390,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 +416,13 @@ class UsersController extends Controller
public function postBulkEditSave(Request $request)
{
$this->authorize('update', User::class);
if ((!Input::has('ids')) || (count(Input::has('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 +438,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 +453,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 +465,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');
}
@@ -531,13 +487,13 @@ class UsersController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function postBulkSave()
public function postBulkSave(Request $request)
{
$this->authorize('update', User::class);
if ((!Input::has('ids')) || (count(Input::has('ids')) == 0)) {
if ((!$request->has('ids')) || (count($request->input('ids')) == 0)) {
return redirect()->back()->with('error', 'No users selected');
} elseif ((!Input::has('status_id')) || (count(Input::has('status_id')) == 0)) {
} elseif ((!$request->has('status_id')) || ($request->input('status_id')=='')) {
return redirect()->route('users.index')->with('error', 'No status selected');
} else {
@@ -547,7 +503,7 @@ class UsersController extends Controller
if (($key = array_search(Auth::user()->id, $user_raw_array)) !== false) {
unset($user_raw_array[$key]);
}
if (!config('app.lock_passwords')) {
@@ -758,124 +714,6 @@ class UsersController extends Controller
}
}
/**
* Return user import view
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function getImport()
{
$this->authorize('update', User::class);
// Selected groups
$selectedGroups = Input::old('groups', array());
// Get all the available permissions
$permissions = config('permissions');
$selectedPermissions = Input::old('permissions', array('superuser' => -1));
// Show the page
return view('users/import', compact('selectedGroups', 'permissions', 'selectedPermissions'));
}
/**
* Handle user import file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function postImport()
{
$this->authorize('update', User::class);
if (!ini_get("auto_detect_line_endings")) {
ini_set("auto_detect_line_endings", '1');
}
$csv = Reader::createFromPath(Input::file('user_import_csv'));
$csv->setNewline("\r\n");
if (Input::get('has_headers') == 1) {
$csv->setOffset(1);
}
$duplicates = '';
$nbInsert = $csv->each(function ($row) use ($duplicates) {
if (array_key_exists(2, $row)) {
if (Input::get('activate') == 1) {
$activated = '1';
} else {
$activated = '0';
}
$pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 15);
// Location
if (array_key_exists('4', $row)) {
$user_location_id = trim($row[4]);
if ($user_location_id=='') {
$user_location_id = null;
}
}
try {
// Check if this email already exists in the system
$user = User::where('username', $row[2])->first();
if ($user) {
$duplicates .= $row[2] . ', ';
} else {
$newuser = array(
'first_name' => trim(e($row[0])),
'last_name' => trim(e($row[1])),
'username' => trim(e($row[2])),
'email' => trim(e($row[3])),
'password' => bcrypt($pass),
'activated' => $activated,
'location_id' => trim(e($user_location_id)),
'phone' => trim(e($row[5])),
'jobtitle' => trim(e($row[6])),
'employee_num' => trim(e($row[7])),
'company_id' => Company::getIdForUser($row[8]),
'permissions' => '{"user":1}',
'notes' => 'Imported user'
);
DB::table('users')->insert($newuser);
if (((Input::get('email_user') == 1) && !config('app.lock_passwords'))) {
// Send the credentials through email
if ($row[3] != '') {
$data = array();
$data['username'] = trim(e($row[2]));
$data['first_name'] = trim(e($row[0]));
$data['password'] = $pass;
if ($newuser['email']) {
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']]));
});
}
}
}
}
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
return true;
}
});
return redirect()->route('users.index')->with('duplicates', $duplicates)->with('success', 'Success');
}
/**
* Return JSON response with a list of user details for the getIndex() view.
*
@@ -1002,8 +840,7 @@ class UsersController extends Controller
return redirect()->route('users.index')->with('error', $e->getMessage());
}
return view('users/ldap')
->with('location_list', Helper::locationsList());
return view('users/ldap');
}
@@ -1068,7 +905,7 @@ class UsersController extends Controller
// Open output stream
$handle = fopen('php://output', 'w');
User::with('assets', 'accessories', 'consumables', 'licenses', 'manager', 'groups', 'userloc', 'company','throttle')->orderBy('created_at', 'DESC')->chunk(500, function($users) use($handle) {
User::with('assets', 'accessories', 'consumables', 'department', 'licenses', 'manager', 'groups', 'userloc', 'company','throttle')->orderBy('created_at', 'DESC')->chunk(500, function($users) use($handle) {
$headers=[
// strtolower to prevent Excel from trying to open it as a SYLK file
strtolower(trans('general.id')),
@@ -1080,6 +917,7 @@ class UsersController extends Controller
trans('admin/users/table.email'),
trans('admin/users/table.manager'),
trans('admin/users/table.location'),
trans('general.department'),
trans('general.assets'),
trans('general.licenses'),
trans('general.accessories'),
@@ -1110,6 +948,7 @@ class UsersController extends Controller
$user->email,
($user->manager) ? $user->manager->present()->fullName() : '',
($user->userloc) ? $user->userloc->name : '',
($user->department) ? $user->department->name : '',
$user->assets->count(),
$user->licenses->count(),
$user->accessories->count(),
@@ -1136,23 +975,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.
*

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,47 @@ 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'));
$asset->decrement('requests_counter', 1);
$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();
$asset->increment('requests_counter', 1);
$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 +203,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 +224,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 +265,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 +317,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);

View File

@@ -22,8 +22,8 @@ class Kernel extends HttpKernel
\App\Http\Middleware\ReferrerPolicyHeader::class,
\App\Http\Middleware\ContentSecurityPolicyHeader::class,
\App\Http\Middleware\NosniffGuard::class,
\App\Http\Middleware\CheckForSetup::class,
\Fideloper\Proxy\TrustProxies::class,
\App\Http\Middleware\CheckForSetup::class,
\App\Http\Middleware\CheckForDebug::class,
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

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

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,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,lic|max:'.$max_file_size,
];
}

View File

@@ -46,13 +46,14 @@ class AssetRequest extends Request
$rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
$model = AssetModel::find($this->request->get('model_id'));
if($this->request->get('model_id') != '') {
$model = AssetModel::find($this->request->get('model_id'));
if (($model) && ($model->fieldset)) {
$rules += $model->fieldset->validation_rules();
if (($model) && ($model->fieldset)) {
$rules += $model->fieldset->validation_rules();
}
}
return $rules;
}

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',
];
}

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

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Traits;
trait UniqueSerialTrait
{
/**
* Prepare a unique_ids rule, adding a model identifier if required.
*
* @param array $parameters
* @param string $field
* @return string
*/
protected function prepareUniqueSerialRule($parameters, $field)
{
if ($settings = \App\Models\Setting::first()) {
if ($settings->unique_serial=='1') {
return 'unique_undeleted:'.$this->table.','. $this->getKey();
}
}
}
}

View File

@@ -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(),

View File

@@ -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()),
@@ -37,7 +47,7 @@ class ActionlogsTransformer
] : null,
'created_at' => Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($actionlog->updated_at, 'datetime'),
'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(), 'date'): null,
'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(null, $actionlog->item), 'date'): null,
'days_to_next_audit' => $actionlog->daysUntilNextAudit($settings->audit_interval, $actionlog->item),
'action_type' => $actionlog->present()->actionType(),
'admin' => ($actionlog->user) ? [
@@ -59,12 +69,11 @@ class ActionlogsTransformer
];
return $array;
}
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
{

View File

@@ -22,15 +22,25 @@ 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),
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'datetime'),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
'asset_maintenance_time' => $assetmaintenance->asset_maintenance_time,
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'datetime'),
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'date'),
'user_id' => ($assetmaintenance->admin) ? ['id' => $assetmaintenance->admin->id,'name'=> e($assetmaintenance->admin->getFullNameAttribute())] : null,
'created_at' => Helper::getFormattedDateObject($assetmaintenance->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->updated_at, 'datetime'),

View File

@@ -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)

View File

@@ -36,13 +36,13 @@ class AssetsTransformer
'id' => (int) $asset->assetstatus->id,
'name'=> e($asset->assetstatus->name),
'status_type'=> e($asset->assetstatus->getStatuslabelType()),
'status_meta' => e($asset->present()->statusMeta),
'status_meta' => e($asset->present()->statusMeta),
] : null,
'category' => ($asset->model->category) ? [
'category' => (($asset->model) && ($asset->model->category)) ? [
'id' => (int) $asset->model->category->id,
'name'=> e($asset->model->category->name)
] : null,
'manufacturer' => ($asset->model->manufacturer) ? [
'manufacturer' => (($asset->model) && ($asset->model->manufacturer)) ? [
'id' => (int) $asset->model->manufacturer->id,
'name'=> e($asset->model->manufacturer->name)
] : null,
@@ -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),
'checkin_counter' => (int) $asset->checkin_counter,
'checkout_counter' => (int) $asset->checkout_counter,
'requests_counter' => (int) $asset->requests_counter,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
];
if (($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;
}
}

View File

@@ -30,17 +30,18 @@ class CategoriesTransformer
'eula' => ($category->getEula()) ? true : false,
'checkin_email' => ($category->checkin_email =='1') ? true : false,
'require_acceptance' => ($category->require_acceptance =='1') ? true : false,
'assets_count' => $category->assets_count,
'accessories_count' => $category->accessories_count,
'consumables_count' => $category->consumables_count,
'components_count' => $category->components_count,
'assets_count' => (int) $category->assets_count,
'accessories_count' => (int) $category->accessories_count,
'consumables_count' => (int) $category->consumables_count,
'components_count' => (int) $category->components_count,
'licenses_count' => (int) $category->licenses_count,
'created_at' => Helper::getFormattedDateObject($category->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($category->updated_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', Category::class) ? true : false,
'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0)) ? true : false,
'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0) && ($category->licenses_count == 0)) ? true : false,
];
$array += $permissions_array;

View File

@@ -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),
];
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Transformers;
use App\Models\LicenseSeat;
use App\Models\License;
use Gate;
use Illuminate\Database\Eloquent\Collection;
use App\Helpers\Helper;

View File

@@ -38,6 +38,7 @@ class LicensesTransformer
'license_email' => e($license->license_email),
'maintained' => ($license->maintained == 1) ? true : false,
'supplier' => ($license->supplier) ? ['id' => (int) $license->supplier->id,'name'=> e($license->supplier->name)] : null,
'category' => ($license->category) ? ['id' => (int) $license->category->id,'name'=> e($license->category->name)] : null,
'created_at' => Helper::getFormattedDateObject($license->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($license->updated_at, 'datetime'),
'user_can_checkout' => (bool) ($license->free_seats_count > 0),

View File

@@ -34,15 +34,16 @@ class LocationsTransformer
'id' => (int) $location->id,
'name' => e($location->name),
'image' => ($location->image) ? app('locations_upload_url').e($location->image) : null,
'address' => e($location->address),
'city' => e($location->city),
'state' => e($location->state),
'country' => e($location->country),
'zip' => e($location->zip),
'address' => ($location->address) ? e($location->address) : null,
'address2' => ($location->address2) ? e($location->address2) : null,
'city' => ($location->city) ? e($location->city) : null,
'state' => ($location->state) ? e($location->state) : null,
'country' => ($location->country) ? e($location->country) : null,
'zip' => ($location->zip) ? e($location->zip) : null,
'assigned_assets_count' => (int) $location->assigned_assets_count,
'assets_count' => (int) $location->assets_count,
'users_count' => (int) $location->users_count,
'currency' => ($location->currency) ? e($location->currency) : null,
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
'parent' => ($location->parent) ? [

View File

@@ -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,
];

View File

@@ -32,12 +32,6 @@ class SelectlistTransformer
}
// This is weird and awful, but the only way I can find to allow the user to
// clear the selection - @snipe
if (count($items_array) > 0) {
array_unshift($items_array, ['id' =>'', 'text'=> trans('general.clear_selection')]);
}
$results = [
'items' => $items_array,
'pagination' =>

View File

@@ -9,13 +9,13 @@ use App\Helpers\Helper;
class StatuslabelsTransformer
{
public function transformStatuslabels (Collection $statuslabels)
public function transformStatuslabels (Collection $statuslabels, $total)
{
$array = array();
foreach ($statuslabels as $statuslabel) {
$array[] = self::transformStatuslabel($statuslabel);
}
return (new DatatablesTransformer)->transformDatatables($array);
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformStatuslabel (Statuslabel $statuslabel)
@@ -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'),

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;

View File

@@ -41,7 +41,9 @@ class AccessoryImporter extends ItemImporter
$this->log("No Matching Accessory, Creating a new one");
$accessory = new Accessory();
$accessory->fill($this->sanitizeItemForStoring($accessory));
$accessory->unsetEventDispatcher();
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
// $accessory->unsetEventDispatcher();
if ($accessory->save()) {
$accessory->logCreate('Imported using CSV Importer');
$this->log('Accessory ' . $this->item["name"] . ' was created');

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;
}
}
}
@@ -82,8 +81,8 @@ class AssetImporter extends ItemImporter
// We need to save the user if it exists so that we can checkout to user later.
// Sanitizing the item will remove it.
if(array_key_exists('user', $this->item)) {
$user = $this->item['user'];
if(array_key_exists('checkout_target', $this->item)) {
$target = $this->item['checkout_target'];
}
$item = $this->sanitizeItemForStoring($asset, $editingAsset);
// The location id fetched by the csv reader is actually the rtd_location_id.
@@ -106,14 +105,16 @@ class AssetImporter extends ItemImporter
$asset->{$custom_field} = $val;
}
}
$asset->unsetEventDispatcher();
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
// $asset->unsetEventDispatcher();
if ($asset->save()) {
$asset->logCreate('Imported using csv importer');
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
// If we have a user to checkout to, lets do so.
if(isset($user)) {
$asset->fresh()->checkOut($user);
// If we have a target to checkout to, lets do so.
if(isset($target)) {
$asset->fresh()->checkOut($target);
}
return;
}

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