Compare commits

...

1850 Commits

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

* Improved expiring assets notification

* Nicee low inventory notification

* Refactored stupid language strings

* Oops

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

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

* Remove dead code.

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

* Reverting assetcontroller

Not sure exactly what happened here…

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

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

* Allows search to handle multiple search terms better

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

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

* Remove apiStore method that is unusued since api controllers.

* Use proper model exception

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

* Refactor AssetsController methods.

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

Fix weird missing things.

* Fix Unit test failing after date changes.

* Pass valid string to be translated.

* Some method cleanup for codacy.

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

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

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

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

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

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

* Adds missing dates to $dates in models

* Removes duplicated „where“ conditions

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

* Removes the old text search methods

* Adds back additional conditions to the search

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

* Removes unnecessary check for the deleted_at attribute

* Fixes typo in comments

* suppresses errors from Codacy

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

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

See: https://select2.org/placeholders

* Removes empty option from multiple select

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

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

* Adds the option to clear select2 instances

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

See: https://select2.org/placeholders

* Removes the „Clear selection“ option from select lists

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

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

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

* Fixes checkout notification routing

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

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

* adds a check for EULAs

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

* adds permission checks to custom fieldsets

* adds separate permissions for custom fieldsets

* check for permissions in views

* Removes custom fieldsets from permissions config

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

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

* simplifies the authorization check for the custom fields overview

* removes special handling of custom fieldsets in base policy

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

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

* adds permission checks for depreciations

* adds permission check for all reports

* fixes permissions for departments

* fixes permission naming (edit -> update)

* fixes authorization checking wrong permission in API

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

* adds authorization checks for select2 lists

* adds missing authorization checks for api

* fixes user authorization check for creating users

* adds additional check viewing assets on showing a users assets

* Removes authorization checks for select2 lists

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

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

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

* Changes made per Snipe's direction

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

* Import location checkout. Frontend changes.

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

* Move userinfo method to UsersController

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

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

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

* disable redirection to asset maintenances view

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

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

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

* Update API to use de-normed fields

* Increment counters for checkin;/checkout

* Derp.

* Added request increment/decrementer

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

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

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

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

* fedora support. strict versioning.

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

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

* less spaces!

* rename functions

* move progress spinner variables into progress ()

* $hosts was used in one place

* missed this one

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

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

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

* Adds default value functionality

* Adds built assets

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

* Updates built assets

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

* Added department to custom asset export
Updates build assets

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

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

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

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

* Simplify Logic

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

* Removed old commented items

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

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

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

* Only show asset name in email if it has one

* Refactor requested method to only include non-canceled requests

* Refactored requestable assets to log request and cancelation

* Added softdeletes on checkout requests

* Differentiate between canceling and deleting requests

* Added asset request cancelation notification

* Added timestamps and corrected unique key on requests table

* Improved requests view

* Re-use blade for cancel/request email

* Refactored BS table formatter for requested assets

* Location name min reduced to 2

* Added PAT test as maintenance option

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

* Better slack message

* Added getImageUrl method for assets

* Include qty in request notifications

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

* Removed old asset request/cancel emails

* Added user profile asset request routes

* Added profile controller requested assets method

* Added blade link to requested assets for profile view

* Sort user history desc

* Added requested assets blade

* Added canceled at to checkoutRequest method

* Include qty in request

* Fixed comment, removed allowed_columns

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

* Fixed return type in method doc

* Fixed version number

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

* Seed the Setting object with default values.

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

* Seed the Setting object with default values.

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

* Added “show images in email” to settings

* Added nicer HTML emails

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

* Remove old notification for accessory checkout

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

* Add method for image url for accessories

* Added accessory checkout email blade

* Make accessory email notification on checkout screen consistent with assets

* Added native consumables notifications

* Fixes for asset notification

* Updated notification blades with correct-er fields

* Updated notifications

* License checkin notification - does not work yet

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

* Added alternate “cc” email for admins

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

* Fix tests

* Fixed consumable URL

* Removed unused notification

* Pass target type in params

* Show slack status

* Pass additional parameters

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

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

* Slack is broken :( Trying to fix

Will try a git bisect

* Put preview back into checkout

* Pulled old archaic mail

* Removed debugging

* Fixed wrong email title

* Fixed slack endpoint not firing

* Poobot, we hardly knew ye.

* Removed old, manual mail from API

* Typo :-/

* Code cleanup

* Use defined formatted date in JSON

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

* Removed debugging

* Use date formatter

* Fixed target_type

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

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

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

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

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

* Missing dependency for upgrade.php #4726

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

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

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

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

* Add show URL in Emails condition

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

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

* Update dark-green.css

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

* API tests for asset models and related cleanup/improvements

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

* API Category tests.

* Manufacturers API Test.

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

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

* Add Checkin/Checkout api tests.

* Accessories api test

* Add Companies API Test.

* Return ModelNotFound as a 404.

* Cleanups/simplficiations/updates.

* Locations api test.

* currency and image should be fillable on location.

* Update components api test.

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

* order_number should be fillable in component.

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

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

* Add test for assets method

* API status label test.

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

* Add serial to update.

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

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

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

* Fix functional tests

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

* Importer name/full name fixes.

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

Also update sample csvs and remove an outdated sample.

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

* Use sqlite/different db setup for unit tests.

* Fix assets api test.

* Fix Components API test.

* increase travis memory limit for functional tests.

* Use travis config for api tests as well.

* Fix memory limit file.

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

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

* Use maintenances API for table listings

* NIcer layout for allowed_columns in maintenances API

* Fixed #5014 - bootstrap cookie issues

* Fixed #5015 - bug when saving settings

* Refactored datatable code to use data attributes

* Updated dashboard with new table code

* Added - Order by group user count

* Updated groups to use new table attributes

* New license listing table code

* More bootstrap table implementations

* More BS table refactoring

* Improved bootstrap assigned assets

* New bootstrap for reports

* Misc BS fixes

* FIxed small issue with asset history display

* Removed multisort option

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

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

## What is CodeTriage?

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

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

## Why am I getting this PR?

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

## What does adding a badge accomplish?

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

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

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

## Have a question or comment?

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

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

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

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

* Actually disable the event dispatcher.

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

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

* Actually disable the event dispatcher.

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

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

* revert CustomFieldsetsController

* fieldset associate / disassociate api

* fix variable names and payload

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

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

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

* Add Locale Settings to Setup

* Add multiple company support to setup

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

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

* Cleaned up the if else statement

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

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

* Fix code error causing component importer to implode.

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

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

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

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

Fixes #4658

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

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

* Set the location_id when importing an item properly.

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

* Production assets.

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

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

* Fix for 4485.  Serial not serial_number

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

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

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

* Stupid stash

* Added radio button checkout selector javascript

* New compiled production assets

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

* Added checkout-to radio button selector

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

* Added new checkout-to selector to hardware edit

* Added new checkout-to selector to asset checkout form

* Refactored postCheckout to use radio button submission

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

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

* Add Company Name to Labels

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

* Add Company Name checkbox to Label settings

* Add Company Name Lang

* Add display company name to postLabels

* Revert Add Company Name Lang

* Fix display company name in postLabels

* Change tinyInt to boolean for display company name

* Simplify checking for company and getting the name

* Change to square brackets for array notation

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

* Remove min-height

Removed min-height from Assets by Status

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

* Clarified language of custom format, fixed regex example

* Fixed regex example in placeholder

* Added comments to custom fields

* Added regex validation string

* Added valid_regex validator in format requirements

* Removed useles comments

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

* Policy cleanup/fixes.

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

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

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

* Added manufacturers API support for image

* Added manufacturers API support for image

* Added image support for locations add/update

* Added manufacturer image upload support to controller

* General image string

* Added blade support for image uploads/delete image

* Added $request support (from Input::)

* Added image support in API transformers

* Added image to Manufacturers presenter for data table

* Migration to create image fields

* Ignore the contents of the new image directories

* Create new image upload directories

* Created components/consumables uploads directory

* Fixed missing textSearch scope from companies

* Added ignore for companies uploads directory

* Added blade support for image upload

* Fixed path to upload directory on edit

* Added company image upport to transformers, controllers

* Added image support for categories

* Added support for images in Departments

* Added support for image in Consumables

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

* API: Allow searching accessories by supplier id

* Adds suppliers and image upload to accessories

* Allow sorting by counts for suppliers

* Validate supplier image uploads

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

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

* FIX Recrypt Custom fields column names

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

* Revert css change

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

* Fixes #4056 - check for assets before deleting user

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

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

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

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

Changing Save button for Checkout button and adding Cancel button

* Small UI Change

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

* Further cleanup.

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

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

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

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

* Try to fix travis unit test things.

* Downgrade doctrine/inflector for php5

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

* Update sql dump for functional tests.

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

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

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

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

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

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

* remove unneeded else{} block

* Re-indented the code
2017-09-12 12:28:42 -07:00
snipe
81e358a01d Small maintenance fixes 2017-09-08 17:49:01 -07:00
KeenRivals
fd515654ff Fix broken user's manual link (#3902)
snipe-it-manual.readme.io goes to a 404 page
2017-08-29 13:50:57 -07:00
Robin Temme
5347b19910 Update Maintained Badge (#3877)
The maintenance badge was outdated and displayed "no!". As I guess this was not it's intended purpose, I changed it to the new path, and it now displays "yes".
2017-08-23 12:05:29 -07:00
snipe
29f1cf2b48 Increase depreciation max 2017-07-19 19:41:21 -07:00
Sorvani
f56862c684 change CentOS 7 to pull from git (#3734)
The package setup for CentOS 7 already installs git so use it for the install. This makes later updates easier for the end users. They can simply use git pull like the instructions say for updating.
2017-07-11 20:28:31 -07:00
snipe
1052be670d Increase size of state field 2017-06-15 21:04:10 -07:00
Daniel Nemanic
30a9704625 Update UsersController.php (#3640)
If a user is disabled in your Active Directory, it should be deactivated in the licensing too.

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

The codes comes from this site:
http://www.netvision.com/ad_useraccountcontrol.php
2017-06-12 15:29:55 -07:00
Lee Thoong Ching
9a9b6ae228 Update snipeit.sh (#3620)
To support Oracle Linux ( equivalent to redhat which check os version and type )
2017-06-01 20:54:23 -07:00
morph027
7c8dc9fe2d docker: use ubuntu:xenial with php7.0 (#3616)
Nice, thanks! I'll patch this over to develop as well.
2017-06-01 20:50:09 -07:00
snipe
b81dd18576 Fixed delete_at 2017-05-19 03:00:19 -07:00
snipe
dac4b58892 Show warning on deleted items 2017-05-19 02:58:55 -07:00
snipe
71fd430f8e Fixes issue with older, deleted asset tags caching QR codes 2017-05-19 02:51:35 -07:00
snipe
5265821bcc Bumped version 2017-05-18 22:24:03 -07:00
snipe
cb494a74ca Merge branch 'master' of github.com:snipe/snipe-it 2017-05-18 22:20:57 -07:00
snipe
c526ffbf68 Bumped version 2017-05-18 22:20:52 -07:00
snipe
6f5fe83a91 Fixed missing language string in asset display 2017-05-18 22:20:43 -07:00
Nate Felton
5a3816c907 Fixing a regression for RHEL 6 with snipe/snipe-it#2993 (#3572) 2017-05-12 17:19:41 -07:00
snipe
c8796cf045 Only prompt for checkin if there is an assigned user 2017-05-10 03:37:30 -07:00
snipe
f4095c6dd0 Bumped version 2017-05-09 15:32:34 -07:00
snipe
ee2c67a65f Added missing logging back in 2017-05-09 15:30:45 -07:00
snipe
5614578710 Don’t make the item clickable if it’s been deleted 2017-05-09 15:30:19 -07:00
snipe
08ef78356d Update Crowdin configuration file 2017-04-06 20:59:25 -07:00
Manasses Ferreira
47ac59abef We noted that the barcode folder was not being created. (what is done now in the entrypoint.sh) At the same time, a wrong directory was being created. (which explains the deleted line in Dockerfile) (#3456) 2017-03-31 13:48:50 -07:00
snipe
265a896211 Bumped version 2017-02-22 22:38:33 -08:00
snipe
f7e4fca70d Add model_number to accessory search 2017-02-22 22:34:56 -08:00
snipe
bb4c443cd9 Added notes to custom report 2017-02-03 02:20:56 -08:00
snipe
7b1d2ee050 Fixed typo 2017-01-17 11:57:24 -08:00
snipe
b3f70a046f Don’t require superadmin for checkin and delete user 2017-01-17 09:19:14 -08:00
morph027
734e87f85f Installer fine tuning (#2993)
* used shellcheck to lint snipeit.sh

* do not depend on lsb-release command

* add lsb codename

* really use perms()

* more fixes

* quiet apt

* silent logging using log()

* start mysql if not running

* added curl to ubuntu

* added logfile to log function

* update apt index after adding mariadb repo

* fixed typo

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

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

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

If we're using a debian base image for sizing reasons, we may as well go the full distance and use debian-slim.
2017-01-10 16:22:06 -08:00
snipe
9fd3a9a82d Removed logging 2017-01-06 03:12:49 -08:00
snipe
a6e6991a2d Removed second number_format 2017-01-06 03:11:27 -08:00
snipe
93ba0717d8 Removed number_format 2017-01-06 03:02:38 -08:00
snipe
a8839e0ef4 Fixes #3104, #2914 and #2918 - auto-increment incorrectly generating next ID 2017-01-06 02:52:10 -08:00
snipe
3e4152c966 Bumped version 2017-01-06 00:04:26 -08:00
snipe
1bf34d73f5 Use correct authorize rule in middleware for user delete 2017-01-06 00:02:46 -08:00
snipe
d1e360d64d Fix logic to use gate instead of asking if superuser 2017-01-06 00:02:19 -08:00
snipe
3b5b19848c Added oauth keys to gitignore 2017-01-06 00:01:55 -08:00
snipe
93ba90e837 Merge remote-tracking branch 'origin/develop' 2016-12-12 19:24:00 -08:00
snipe
fc70d79a17 Merge remote-tracking branch 'origin/develop' 2016-12-12 19:07:07 -08:00
snipe
65016a2383 Merge remote-tracking branch 'origin/develop' 2016-12-05 16:12:41 -08:00
snipe
dbcb2ccb46 Merge remote-tracking branch 'origin/develop' 2016-12-05 15:09:49 -08:00
snipe
2afcfcc87c Set DB_SSL to false for config 2016-12-03 17:32:44 -08:00
snipe
1afef9416a Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2016-12-03 17:15:02 -08:00
snipe
4049143ebf Bumped version 2016-12-01 06:11:03 -08:00
snipe
4df53bdf8d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
#	resources/views/account/view-assets.blade.php
2016-12-01 06:10:50 -08:00
snipe
d49a1ea304 More graceful error message if log doesn’t exist 2016-12-01 06:05:14 -08:00
snipe
521f4facd5 Bumped version 2016-12-01 05:56:24 -08:00
snipe
b4653dfc15 Ignore npm log 2016-12-01 05:55:29 -08:00
5595 changed files with 93353 additions and 497661 deletions

View File

@@ -737,6 +737,910 @@
"contributions": [
"code"
]
},
{
"login": "richardhofman6",
"name": "Richard Hofman",
"avatar_url": "https://avatars1.githubusercontent.com/u/6551003?v=4",
"profile": "https://github.com/richardhofman6",
"contributions": [
"code"
]
},
{
"login": "gizzmojr",
"name": "gizzmojr",
"avatar_url": "https://avatars0.githubusercontent.com/u/3697569?v=4",
"profile": "https://github.com/gizzmojr",
"contributions": [
"code"
]
},
{
"login": "imjennyli",
"name": "Jenny Li",
"avatar_url": "https://avatars3.githubusercontent.com/u/404729?v=4",
"profile": "https://github.com/imjennyli",
"contributions": [
"doc"
]
},
{
"login": "GeoffYoung",
"name": "Geoff Young",
"avatar_url": "https://avatars0.githubusercontent.com/u/869227?v=4",
"profile": "https://github.com/GeoffYoung",
"contributions": [
"code"
]
},
{
"login": "BlueHatbRit",
"name": "Elliot Blackburn",
"avatar_url": "https://avatars3.githubusercontent.com/u/1068477?v=4",
"profile": "http://www.elliotblackburn.com",
"contributions": [
"doc"
]
},
{
"login": "TonisOrmisson",
"name": "Tõnis Ormisson",
"avatar_url": "https://avatars1.githubusercontent.com/u/6357451?v=4",
"profile": "http://andmemasin.eu",
"contributions": [
"code"
]
},
{
"login": "thakilla",
"name": "Nicolai Essig",
"avatar_url": "https://avatars0.githubusercontent.com/u/449411?v=4",
"profile": "http://www.nicolai-essig.de",
"contributions": [
"code"
]
},
{
"login": "techincolor",
"name": "Danielle",
"avatar_url": "https://avatars1.githubusercontent.com/u/14809698?v=4",
"profile": "https://github.com/techincolor",
"contributions": [
"doc"
]
},
{
"login": "TheVakman",
"name": "Lawrence",
"avatar_url": "https://avatars1.githubusercontent.com/u/18545156?v=4",
"profile": "https://github.com/TheVakman",
"contributions": [
"test",
"bug"
]
},
{
"login": "uknzaeinozpas",
"name": "uknzaeinozpas",
"avatar_url": "https://avatars1.githubusercontent.com/u/22473767?v=4",
"profile": "https://github.com/uknzaeinozpas",
"contributions": [
"test",
"code"
]
},
{
"login": "Gelob",
"name": "Ryan",
"avatar_url": "https://avatars3.githubusercontent.com/u/422752?v=4",
"profile": "https://github.com/Gelob",
"contributions": [
"doc"
]
},
{
"login": "vcordes79",
"name": "vcordes79",
"avatar_url": "https://avatars1.githubusercontent.com/u/10672546?v=4",
"profile": "https://github.com/vcordes79",
"contributions": [
"code"
]
},
{
"login": "fordster78",
"name": "fordster78",
"avatar_url": "https://avatars3.githubusercontent.com/u/27958330?v=4",
"profile": "https://github.com/fordster78",
"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"
]
}
]
}

View File

@@ -7,18 +7,20 @@ APP_KEY=ChangeMe
APP_URL=null
APP_TIMEZONE='UTC'
APP_LOCALE=en
BACKUP_ENV=false
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=localhost
DB_HOST=127.0.0.1
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
@@ -29,7 +31,6 @@ DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
DB_SSL_CIPHER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
@@ -44,14 +45,12 @@ MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
@@ -62,6 +61,11 @@ COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
REFERRER_POLICY=same-origin
ENABLE_CSP=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
@@ -70,6 +74,12 @@ CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=null
REDIS_PASSWORD=null
REDIS_PORT-null
# --------------------------------------------
# OPTIONAL: AWS S3 SETTINGS
@@ -89,8 +99,11 @@ LOGIN_LOCKOUT_DURATION=60
# OPTIONAL: MISC
# --------------------------------------------
APP_LOG=single
APP_LOG_MAX_FILES=10
APP_LOCKED=false
FILESYSTEM_DISK=local
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
APP_CIPHER=AES-256-CBC
GOOGLE_MAPS_API=
BACKUP_ENV=true

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

View File

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

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

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

View File

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

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

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

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

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

8
.gitignore vendored
View File

@@ -27,8 +27,16 @@ public/uploads/logo.png
public/uploads/logo.svg
public/uploads/models/*
public/uploads/suppliers/*
public/uploads/accessories/*
public/uploads/locations/*
public/uploads/manufacturers/*
public/uploads/components/*
public/uploads/consumables/*
public/uploads/companies/*
public/uploads/categories/*
public/uploads/users/*
storage/app/private_uploads/users/*
public/uploads/departments/*
storage/debugbar/
storage/dumps/*
storage/laravel-backups

View File

@@ -1,4 +1,7 @@
addons:
code_climate:
repo_token:
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
hosts:
- localhost
sudo: false
@@ -13,9 +16,12 @@ services:
php:
- 5.6
- 7.0
- 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;'
@@ -43,11 +49,14 @@ before_script:
# use the $DB env variable to determine the phpunit.xml to use
# script: ./vendor/bin/codecept run --env testing-ci
script:
- ./vendor/bin/codecept run unit --env testing-ci
- ./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

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

View File

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

View File

@@ -18,6 +18,7 @@ patch \
curl \
vim \
git \
cron \
mysql-client \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -64,7 +65,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.png)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeyhead.svg?style=social)](https://twitter.com/snipeyhead) [![Zenhub](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.io) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-79-orange.svg?style=flat-square)](#contributors)
[![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-179-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:
@@ -67,7 +81,21 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars0.githubusercontent.com/u/8341172?v=3" width="110px;"/><br /><sub>Jay Richards</sub>](http://www.cordeos.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=technogenus "Code") | [<img src="https://avatars2.githubusercontent.com/u/7295127?v=3" width="110px;"/><br /><sub>Alexander Innes</sub>](https://necurity.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leostat "Code") | [<img src="https://avatars2.githubusercontent.com/u/334485?v=3" width="110px;"/><br /><sub>Danny Garcia</sub>](https://buzzedword.codes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=buzzedword "Code") | [<img src="https://avatars2.githubusercontent.com/u/366855?v=3" width="110px;"/><br /><sub>archpoint</sub>](https://github.com/archpoint)<br />[💻](https://github.com/snipe/snipe-it/commits?author=archpoint "Code") | [<img src="https://avatars1.githubusercontent.com/u/67991?v=3" width="110px;"/><br /><sub>Jake McGraw</sub>](http://www.jakemcgraw.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jakemcgraw "Code") | [<img src="https://avatars1.githubusercontent.com/u/1714374?v=3" width="110px;"/><br /><sub>FleischKarussel</sub>](https://github.com/FleischKarussel)<br />[📖](https://github.com/snipe/snipe-it/commits?author=FleischKarussel "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/319644?v=3" width="110px;"/><br /><sub>Dylan Yi</sub>](https://github.com/feeva)<br />[💻](https://github.com/snipe/snipe-it/commits?author=feeva "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/857740?v=3" width="110px;"/><br /><sub>Gil Rutkowski</sub>](http://FlashingCursor.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [<img src="https://avatars3.githubusercontent.com/u/129360?v=3" width="110px;"/><br /><sub>Desmond Morris</sub>](http://www.desmondmorris.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [<img src="https://avatars2.githubusercontent.com/u/52936?v=3" width="110px;"/><br /><sub>Nick Peelman</sub>](http://peelman.us)<br />[💻](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [<img src="https://avatars0.githubusercontent.com/u/53161?v=3" width="110px;"/><br /><sub>Abraham Vegh</sub>](https://abrahamvegh.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [<img src="https://avatars0.githubusercontent.com/u/2818680?v=3" width="110px;"/><br /><sub>Mohamed Rashid</sub>](https://github.com/rashivkp)<br />[📖](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1509456?v=3" width="110px;"/><br /><sub>Kasey</sub>](http://hinchk.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [<img src="https://avatars2.githubusercontent.com/u/10522541?v=3" width="110px;"/><br /><sub>Brett</sub>](https://github.com/BrettFagerlund)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") |
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
@@ -76,7 +104,7 @@ This project follows the [all-contributors](https://github.com/kentcdodds/all-co
### Contributing
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing).
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.

84
Vagrantfile vendored Normal file
View File

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

View File

@@ -16,7 +16,7 @@ class LdapSync extends Command
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--summary}';
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
/**
* The console command description.
@@ -55,31 +55,37 @@ class LdapSync extends Command
try {
$ldapconn = Ldap::connectToLdap();
} catch (\Exception $e) {
LOG::error($e);
}
try {
Ldap::bindAdminToLdap($ldapconn);
} 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 [];
}
$summary = array();
$results = Ldap::findLdapUsers();
$ldap_ou_locations = Location::whereNotNull('ldap_ou')->get();
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
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 [];
}
$results = Ldap::findLdapUsers();
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();
@@ -89,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") {
@@ -135,6 +162,14 @@ class LdapSync extends Command
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
$enabled_accounts = [
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224'
];
$item['activated'] = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
} else {
$item['activated'] = 0;
}
// User exists
$item["createorupdate"] = 'updated';
@@ -145,19 +180,23 @@ class LdapSync extends Command
}
// Create the user if they don't exist.
$user->first_name = e($item["firstname"]);
$user->last_name = e($item["lastname"]);
$user->username = e($item["username"]);
$user->email = e($item["email"]);
$user->employee_num = e($item["employee_number"]);
$user->activated = 1;
$user->activated = $item['activated'];
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';
@@ -184,17 +223,16 @@ class LdapSync extends Command
if ($this->option('summary')) {
for ($x = 0; $x < count($summary); $x++) {
if ($summary[$x]['status']=='error') {
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was not imported: '.$summary[$x]['note']);
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was '.strtoupper($summary[$x]['createorupdate']).'.');
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} else if ($this->option('json_summary')) {
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ];
$this->info(json_encode($json_summary));
} else {
return $summary;
}
}
}

View File

@@ -78,12 +78,7 @@ class ObjectImportCommand extends Command
$logFile = $this->option('logfile');
\Log::useFiles($logFile);
if ($this->option('testrun')) {
$this->comment('====== TEST ONLY Item Import for '.$filename.' ====');
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
} else {
$this->comment('======= Importing Items from '.$filename.' =========');
}
$this->comment('======= Importing Items from '.$filename.' =========');
$importer->import();
$this->bar = null;

View File

@@ -16,7 +16,8 @@ class RecryptFromMcrypt extends Command
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt';
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
@@ -48,6 +49,7 @@ class RecryptFromMcrypt extends Command
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
$errors = array();
if (!$legacy_key) {
@@ -60,6 +62,7 @@ class RecryptFromMcrypt extends Command
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1])==44)) {
$legacy_key = base64_decode($key_parts[1],true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
@@ -79,7 +82,9 @@ class RecryptFromMcrypt extends Command
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
if ($this->confirm("Are you SURE you wish to continue?")) {
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm("Are you SURE you wish to continue?"))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
@@ -91,13 +96,21 @@ class RecryptFromMcrypt extends Command
}
$mcrypter = new McryptEncrypter($legacy_key);
if ($legacy_cipher){
$mcrypter = new McryptEncrypter($legacy_key,$legacy_cipher);
}else{
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_password=='') {
if ($settings->ldap_pword=='') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = \Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted','=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
@@ -110,32 +123,22 @@ class RecryptFromMcrypt extends Command
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($custom_fields as $encrypted_field) {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($encrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
$bar->advance();
}
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$encrypted_field}!='') {
if ($asset->{$columnName}!='') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$encrypted_field});
$asset->{$encrypted_field} = \Crypt::encrypt($decrypted_field);
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = \Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();

View File

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

View File

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

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

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

View File

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

View File

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

View File

@@ -19,13 +19,17 @@ class Kernel extends ConsoleKernel
Commands\SendInventoryAlerts::class,
Commands\SendExpectedCheckinAlerts::class,
Commands\ObjectImportCommand::class,
Commands\Versioning::class,
Commands\Version::class,
Commands\SystemBackup::class,
Commands\DisableLDAP::class,
Commands\Purge::class,
Commands\LdapSync::class,
Commands\FixDoubleEscape::class,
Commands\RecryptFromMcrypt::class
Commands\RecryptFromMcrypt::class,
Commands\ResetDemoSettings::class,
Commands\SyncAssetLocations::class,
Commands\RegenerateAssetTags::class,
Commands\SyncAssetCounters::class,
];
/**
@@ -39,7 +43,7 @@ class Kernel extends ConsoleKernel
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkins')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
$schedule->command('snipeit:backup')->weekly();
$schedule->command('backup:clean')->daily();
}

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()
{
"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

@@ -7,6 +7,7 @@ use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use App\Helpers\Helper;
use Illuminate\Validation\ValidationException;
use Log;
class Handler extends ExceptionHandler
{
@@ -34,7 +35,10 @@ class Handler extends ExceptionHandler
*/
public function report(Exception $exception)
{
parent::report($exception);
if ($this->shouldReport($exception)) {
Log::error($exception);
return parent::report($exception);
}
}
/**
@@ -63,7 +67,7 @@ class Handler extends ExceptionHandler
}
if ($e instanceof \Illuminate\Validation\ValidationException) {
return response()->json(Helper::formatStandardApiResponse('error', $e->response['messages'], $e->getMessage(), 400));
return response()->json(Helper::formatStandardApiResponse('error', null, $e->response['messages'], 400));
}
if ($this->isHttpException($e)) {

View File

@@ -127,99 +127,16 @@ class Helper
$floatString = str_replace(",", "", $floatString);
$floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString);
// Strip Currency symbol
$floatString = str_replace($LocaleInfo['currency_symbol'], '', $floatString);
// If no currency symbol is set, default to $ because Murica
$currencySymbol = $LocaleInfo['currency_symbol'];
if (empty($currencySymbol)) {
$currencySymbol = '$';
}
$floatString = str_replace($currencySymbol, '', $floatString);
return floatval($floatString);
}
/**
* Get the list of models in an array to make a dropdown menu
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.5]
* @return Array
*/
public static function modelList()
{
$models = AssetModel::with('manufacturer')->get();
$model_array[''] = trans('general.select_model');
foreach ($models as $model) {
$model_array[$model->id] = $model->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
*
@@ -229,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
*
@@ -283,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
*
@@ -324,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
*
@@ -400,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;
}
/**
@@ -454,9 +281,9 @@ class Helper
*/
public static function checkLowInventory()
{
$consumables = Consumable::with('users')->whereNotNull('min_amt')->get();
$accessories = Accessory::with('users')->whereNotNull('min_amt')->get();
$components = Component::with('assets')->whereNotNull('min_amt')->get();
$consumables = Consumable::withCount('consumableAssignments')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('users')->whereNotNull('min_amt')->get();
$components = Component::withCount('assets')->whereNotNull('min_amt')->get();
$avail_consumables = 0;
$items_array = array();
@@ -466,7 +293,7 @@ class Helper
$avail = $consumable->numRemaining();
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($consumable->qty > 0) {
$percent = number_format((($consumable->numRemaining() / $consumable->qty) * 100), 0);
$percent = number_format((($avail / $consumable->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -475,7 +302,7 @@ class Helper
$items_array[$all_count]['name'] = $consumable->name;
$items_array[$all_count]['type'] = 'consumables';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$consumable->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$consumable->min_amt;
$all_count++;
}
@@ -484,11 +311,11 @@ class Helper
}
foreach ($accessories as $accessory) {
$avail = $accessory->numRemaining();
$avail = $accessory->qty - $accessory->users_count;
if ($avail < ($accessory->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($accessory->qty > 0) {
$percent = number_format((($accessory->numRemaining() / $accessory->qty) * 100), 0);
$percent = number_format((($avail / $accessory->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -497,7 +324,7 @@ class Helper
$items_array[$all_count]['name'] = $accessory->name;
$items_array[$all_count]['type'] = 'accessories';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$accessory->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$accessory->min_amt;
$all_count++;
}
@@ -505,10 +332,10 @@ class Helper
}
foreach ($components as $component) {
$avail = $component->numRemaining();
$avail = $component->qty - $component->assets_count;
if ($avail < ($component->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
if ($component->qty > 0) {
$percent = number_format((($component->numRemaining() / $component->qty) * 100), 0);
$percent = number_format((($avail / $component->qty) * 100), 0);
} else {
$percent = 100;
}
@@ -517,7 +344,7 @@ class Helper
$items_array[$all_count]['name'] = $component->name;
$items_array[$all_count]['type'] = 'components';
$items_array[$all_count]['percent'] = $percent;
$items_array[$all_count]['remaining']=$component->numRemaining();
$items_array[$all_count]['remaining'] = $avail;
$items_array[$all_count]['min_amt']=$component->min_amt;
$all_count++;
}
@@ -684,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;
@@ -725,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,12 +12,13 @@ use DB;
use Gate;
use Input;
use Lang;
use Mail;
use Redirect;
use Illuminate\Http\Request;
use Slack;
use Str;
use View;
use Image;
use App\Http\Requests\ImageUploadRequest;
/** This controller handles all actions related to Accessories for
* the Snipe-IT Asset Management application.
@@ -52,13 +53,9 @@ class AccessoriesController extends Controller
public function create(Request $request)
{
$this->authorize('create', Accessory::class);
// Show the page
return view('accessories/edit')
->with('item', new Accessory)
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
$category_type = 'accessory';
return view('accessories/edit')->with('category_type', $category_type)
->with('item', new Accessory);
}
@@ -68,7 +65,7 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return Redirect
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize(Accessory::class);
// create a new model instance
@@ -87,6 +84,28 @@ class AccessoriesController extends Controller
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost'));
$accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id');
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
$accessory->image = $file_name;
}
}
// Was the accessory created?
if ($accessory->save()) {
@@ -105,18 +124,15 @@ class AccessoriesController extends Controller
*/
public function edit(Request $request, $accessoryId = null)
{
// Check if the accessory exists
if (is_null($item = Accessory::find($accessoryId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
if ($item = Accessory::find($accessoryId)) {
$this->authorize($item);
$category_type = 'accessory';
return view('accessories/edit', compact('item'))->with('category_type', $category_type);
}
$this->authorize($item);
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
return view('accessories/edit', compact('item'))
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
}
@@ -127,7 +143,7 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @return Redirect
*/
public function update(Request $request, $accessoryId = null)
public function update(ImageUploadRequest $request, $accessoryId = null)
{
if (is_null($accessory = Accessory::find($accessoryId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
@@ -144,11 +160,36 @@ class AccessoriesController extends Controller
$accessory->manufacturer_id = request('manufacturer_id');
$accessory->order_number = request('order_number');
$accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id');
// Was the accessory updated?
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) {
unlink($path.'/'.$accessory->image);
}
$accessory->image = $file_name;
}
}
// Was the accessory updated?
if ($accessory->save()) {
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
}
@@ -197,11 +238,7 @@ class AccessoriesController extends Controller
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
}
// Prepare the error message
$error = trans('admin/accessories/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('accessories')->with('error', $error);
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', compact('id')));
}
/**
@@ -219,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'))->with('users_list', Helper::usersList());
}
@@ -247,7 +291,7 @@ class AccessoriesController extends Controller
$this->authorize('checkout', $accessory);
if (!$user = User::find(Input::get('assigned_to'))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
return redirect()->route('checkout/accessory', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
}
// Update the accessory data
@@ -273,15 +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())) {
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'));
@@ -328,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);
@@ -346,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')) {
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'));
}
@@ -369,143 +396,5 @@ class AccessoriesController extends Controller
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
}
/**
* Generates the JSON response for accessories listing view.
*
* Example:
* {
* "actions": "(links to available actions)",
* "category": "(link to category)",
* "company": "My Company",
* "location": "My Location",
* "min_amt": 2,
* "name": "(link to accessory),
* "numRemaining": 6,
* "order_number": null,
* "purchase_cost": "0.00",
* "purchase_date": null,
* "qty": 7
* },
*
* The names of the fields in the returns JSON correspond directly to the the
* names of the fields in the bootstrap-tables in the view.
*
* For debugging, see at /api/accessories/list
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @return string JSON containing accessories and their associated atrributes.
* @internal param int $accessoryId
*/
public function getDatatable(Request $request)
{
$this->authorize('index', Accessory::class);
$accessories = Company::scopeCompanyables(
Accessory::select('accessories.*')
->whereNull('accessories.deleted_at')
->with('category', 'company', 'manufacturer', 'users', 'location')
);
if (Input::has('search')) {
$accessories = $accessories->TextSearch(e(Input::get('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['name','min_amt','order_number','purchase_date','purchase_cost','company','category','model_number', 'manufacturer', 'location'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
switch ($sort) {
case 'category':
$accessories = $accessories->OrderCategory($order);
break;
case 'company':
$accessories = $accessories->OrderCompany($order);
break;
case 'location':
$accessories = $accessories->OrderLocation($order);
break;
case 'manufacturer':
$accessories = $accessories->OrderManufacturer($order);
break;
default:
$accessories = $accessories->orderBy($sort, $order);
break;
}
$accessCount = $accessories->count();
$accessories = $accessories->skip($offset)->take($limit)->get();
$rows = array();
foreach ($accessories as $accessory) {
$rows[] = $accessory->present()->forDataTable();
}
$data = array('total'=>$accessCount, 'rows'=>$rows);
return $data;
}
/**
* Generates the JSON response for accessory detail view.
*
* Example:
* <code>
* {
* "rows": [
* {
* "actions": "(link to available actions)",
* "name": "(link to user)"
* }
* ],
* "total": 1
* }
* </code>
*
* The names of the fields in the returns JSON correspond directly to the the
* names of the fields in the bootstrap-tables in the view.
*
* For debugging, see at /api/accessories/$accessoryID/view
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return string JSON containing accessories and their associated atrributes.
**/
public function getDataView(Request $request, $accessoryID)
{
$accessory = Accessory::find($accessoryID);
if (!Company::isCurrentUserHasAccess($accessory)) {
return ['total' => 0, 'rows' => []];
}
$accessory_users = $accessory->users;
$count = $accessory_users->count();
$rows = array();
foreach ($accessory_users as $user) {
$actions = '';
if (Gate::allows('checkin', $accessory)) {
$actions .= Helper::generateDatatableButton('checkin', route('checkin/accessory', $user->pivot->id));
}
if (Gate::allows('view', $user)) {
$name = (string) link_to_route('users.show', e($user->present()->fullName()), [$user->id]);
} else {
$name = e($user->present()->fullName());
}
$rows[] = array(
'name' => $name,
'actions' => $actions
);
}
$data = array('total'=>$count, 'rows'=>$rows);
return $data;
}
}

View File

@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Http\Transformers\AccessoriesTransformer;
use App\Models\Company;
class AccessoriesController extends Controller
@@ -23,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'));
@@ -33,10 +34,18 @@ class AccessoriesController extends Controller
$accessories->where('company_id','=',$request->input('company_id'));
}
if ($request->has('category_id')) {
$accessories->where('category_id','=',$request->input('category_id'));
}
if ($request->has('manufacturer_id')) {
$accessories->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
if ($request->has('supplier_id')) {
$accessories->where('supplier_id','=',$request->input('supplier_id'));
}
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@@ -95,8 +104,6 @@ class AccessoriesController extends Controller
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id);
$accessory_users = $accessory->users;
return (new AccessoriesTransformer)->transformAccessory($accessory);
}
@@ -128,10 +135,15 @@ class AccessoriesController extends Controller
public function checkedout($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::findOrFail($id)->with('users')->first();
$accessories_users = $accessory->users;
$total = $accessories_users->count();
return (new AccessoriesTransformer)->transformCheckedoutAccessories($accessories_users, $total);
$accessory = Accessory::findOrFail($id);
if (!Company::isCurrentUserHasAccess($accessory)) {
return ['total' => 0, 'rows' => []];
}
$accessory_users = $accessory->users;
$total = $accessory_users->count();
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_users, $total);
}

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,16 +67,20 @@ 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;
}
$total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $maintenances->count());
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
}

View File

@@ -8,6 +8,7 @@ use App\Helpers\Helper;
use Illuminate\Http\Request;
use App\Http\Transformers\AssetModelsTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\SelectlistTransformer;
/**
@@ -29,17 +30,36 @@ class AssetModelsController extends Controller
public function index(Request $request)
{
$this->authorize('view', AssetModel::class);
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer'];
$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'])
$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');
if ($request->has('status')) {
$assetmodels->onlyTrashed();
}
if ($request->has('search')) {
$assetmodels->TextSearch($request->input('search'));
}
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@@ -55,6 +75,7 @@ class AssetModelsController extends Controller
}
$total = $assetmodels->count();
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
@@ -124,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()));
@@ -154,8 +175,69 @@ class AssetModelsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.assoc_users')));
}
if ($assetmodel->image) {
try {
unlink(public_path().'/uploads/models/'.$assetmodel->image);
} catch (\Exception $e) {
\Log::error($e);
}
}
$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')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$assetmodels = AssetModel::select([
'models.id',
'models.name',
'models.image',
'models.model_number',
'models.manufacturer_id',
'models.category_id',
])->with('manufacturer','category');
$settings = \App\Models\Setting::getSettings();
if ($request->has('search')) {
$assetmodels = $assetmodels->SearchByManufacturerOrCat($request->input('search'));
}
$assetmodels = $assetmodels->OrderCategory('ASC')->OrderManufacturer('ASC')->orderby('models.name', 'asc')->orderby('models.model_number', 'asc')->paginate(50);
foreach ($assetmodels as $assetmodel) {
$assetmodel->use_text = '';
if ($settings->modellistCheckedValue('category')) {
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
}
if ($settings->modellistCheckedValue('manufacturer')) {
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
}
$assetmodel->use_text .= e($assetmodel->name);
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
}
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? url('/').'/uploads/models/'.$assetmodel->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($assetmodels);
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\AssetRequest;
use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer;
use App\Models\Asset;
use App\Models\AssetModel;
@@ -30,6 +31,7 @@ use Str;
use TCPDF;
use Validator;
use View;
use App\Http\Transformers\SelectlistTransformer;
/**
@@ -52,7 +54,9 @@ class AssetsController extends Controller
*/
public function index(Request $request)
{
$this->authorize('index', Asset::class);
$settings = Setting::getSettings();
$allowed_columns = [
'id',
@@ -69,36 +73,40 @@ class AssetsController extends Controller
'created_at',
'updated_at',
'purchase_date',
'purchase_cost'
'purchase_cost',
'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'));
}
if ($request->has('filter')) {
$filter = json_decode($request->input('filter'), true);
}
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[]=$field->db_column_name();
}
$assets = Company::scopeCompanyables(Asset::select('assets.*'))->with(
'assetloc', 'assetstatus', 'defaultLoc', 'assetlog', 'company',
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// If we should search on everything
if (($request->has('search')) && (count($filter) == 0)) {
$assets->TextSearch($request->input('search'));
// otherwise loop through the filters and search strictly on them
} else {
if (count($filter) > 0) {
$assets->ByFilter($filter);
}
// These are used by the API to query against specific ID numbers.
// They are also used by the individual searches on detail pages like
// locations, etc.
if ($request->has('status_id')) {
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
// These are used by the API to query against specific ID numbers
if ($request->has('status_id')) {
$assets->where('status_id', '=', $request->input('status_id'));
if ($request->input('requestable')=='true') {
$assets->where('assets.requestable', '=', '1');
}
if ($request->has('model_id')) {
@@ -110,13 +118,18 @@ class AssetsController extends Controller
}
if ($request->has('location_id')) {
$assets->ByLocationId($request->input('location_id'));
$assets->where('assets.location_id', '=', $request->input('location_id'));
}
if ($request->has('supplier_id')) {
$assets->where('assets.supplier_id', '=', $request->input('supplier_id'));
}
if (($request->has('assigned_to')) && ($request->has('assigned_type'))) {
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
->where('assets.assigned_type', '=', $request->input('assigned_type'));
}
if ($request->has('company_id')) {
$assets->where('assets.company_id', '=', $request->input('company_id'));
}
@@ -125,44 +138,104 @@ class AssetsController extends Controller
$assets->ByManufacturer($request->input('manufacturer_id'));
}
$request->has('order_number') ? $assets = $assets->where('order_number', '=', e($request->get('order_number'))) : '';
if ($request->has('depreciation_id')) {
$assets->ByDepreciationId($request->input('depreciation_id'));
}
$request->has('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
// This is used by the sidenav, mostly
// We switched from using query scopes here because of a Laravel bug
// related to fulltext searches on complex queries.
// I am sad. :(
switch ($request->input('status')) {
case 'Deleted':
$assets->withTrashed()->Deleted();
break;
case 'Pending':
$assets->Pending();
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',0)
->where('status_alias.pending','=',1)
->where('status_alias.archived', '=', 0);
});
break;
case 'RTD':
$assets->RTD();
$assets->whereNull('assets.assigned_to')
->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',1)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Undeployable':
$assets->Undeployable();
break;
case 'Archived':
$assets->Archived();
$assets->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',0)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 1);
});
break;
case 'Requestable':
$assets->RequestableAssets();
$assets->where('assets.requestable', '=', 1)
->join('status_labels AS status_alias',function ($join) {
$join->on('status_alias.id', "=", "assets.status_id")
->where('status_alias.deployable','=',1)
->where('status_alias.pending','=',0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Deployed':
$assets->Deployed();
// more sad, horrible workarounds for laravel bugs when doing full text searches
$assets->where('assets.assigned_to', '>', '0');
break;
default:
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 ((!is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter);
} elseif ($request->has('search')) {
$assets->TextSearch($request->input('search'));
}
// This handles all of the pivot sorting (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets.created_at';
switch ($request->input('sort')) {
// This is kinda gross, but we need to do this because the Bootstrap Tables
// API passes custom field ordering as custom_fields.fieldname, and we have to strip
// that out to let the default sorter below order them correctly on the assets table.
$sort_override = str_replace('custom_fields.','', $request->input('sort')) ;
// This handles all of the pivot sorting (versus the assets.* fields
// in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
@@ -180,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);
@@ -194,13 +269,52 @@ class AssetsController extends Controller
$assets->orderBy($column_sort, $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformAssets($assets, $total);
}
/**
* Returns JSON with information about an asset (by tag) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $tag
* @since [v4.2.1]
* @return JsonResponse
*/
public function showByTag($tag)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->where('asset_tag',$tag)->first()) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 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.
*
@@ -211,12 +325,69 @@ class AssetsController extends Controller
*/
public function show($id)
{
if ($asset = Asset::withTrashed()->find($id)) {
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->withCount('checkins', 'checkouts', 'userRequests')->findOrFail($id)) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$assets = Company::scopeCompanyables(Asset::select([
'assets.id',
'assets.name',
'assets.asset_tag',
'assets.model_id',
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id'
])->with('model', 'assetstatus', 'assignedTo')->NotArchived());
if ($request->has('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
}
if ($request->has('search')) {
$assets = $assets->AssignedSearch($request->input('search'));
}
$assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($assets as $asset) {
$asset->use_text = $asset->present()->fullName;
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
}
if ($asset->assetstatus->getStatuslabelType()=='pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
}
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
}
return (new SelectlistTransformer)->transformSelectlist($assets);
}
@@ -242,7 +413,7 @@ class AssetsController extends Controller
$asset->model_id = $request->get('model_id');
$asset->order_number = $request->get('order_number');
$asset->notes = $request->get('notes');
$asset->asset_tag = $request->get('asset_tag');
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
$asset->user_id = Auth::id();
$asset->archived = '0';
$asset->physical = '1';
@@ -261,12 +432,12 @@ class AssetsController extends Controller
$model = AssetModel::find($request->get('model_id'));
if ($model->fieldset) {
foreach ($model->fieldset->fields as $field) {
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug()));
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug(), null));
}
}
if ($asset->save()) {
$asset->logCreate();
if ($request->get('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif ($request->get('assigned_asset')) {
@@ -294,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')) ?
@@ -329,31 +500,34 @@ class AssetsController extends Controller
$asset->requestable = $request->get('requestable') : '';
($request->has('rtd_location_id')) ?
$asset->rtd_location_id = $request->get('rtd_location_id') : '';
($request->has('rtd_location_id')) ?
$asset->location_id = $request->get('rtd_location_id') : '';
($request->has('company_id')) ?
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
if ($request->has('model_id')) {
if (($model = AssetModel::find($request->get('model_id'))) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
if ($request->has($field->convertUnicodeDbSlug())) {
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug()));
}
// Update custom fields
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
if ($request->has($field->convertUnicodeDbSlug())) {
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug()));
}
}
}
if ($asset->save()) {
if ($request->get('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif ($request->get('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
} elseif ($request->get('assigned_location')) {
$target = Location::find(request('assigned_location'));
if (($request->has('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id;
} elseif (($request->has('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
$location = $target->location_id;
} elseif (($request->has('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
$location = $target->id;
}
if (isset($target)) {
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')));
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
@@ -402,7 +576,7 @@ class AssetsController extends Controller
* @since [v4.0]
* @return JsonResponse
*/
public function checkout(Request $request, $asset_id)
public function checkout(AssetCheckoutRequest $request, $asset_id)
{
$this->authorize('checkout', Asset::class);
$asset = Asset::findOrFail($asset_id);
@@ -413,25 +587,61 @@ class AssetsController extends Controller
$this->authorize('checkout', $asset);
if ($request->has('user_id')) {
$target = User::find($request->input('user_id'));
} elseif ($request->has('asset_id')) {
$target = Asset::find($request->input('asset_id'));
} elseif ($request->has('location_id')) {
$target = Location::find($request->input('location_id'));
$error_payload = [];
$error_payload['asset'] = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
// This item is checked out to a location
if (request('checkout_to_type')=='location') {
$target = Location::find(request('assigned_location'));
$asset->location_id = ($target) ? $target->id : '';
$error_payload['target_id'] = $request->input('assigned_location');
$error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type')=='asset') {
$target = Asset::where('id','!=',$asset_id)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id;
// Override with the asset's location_id if it has one
if ($target->location_id!='') {
$asset->location_id = ($target) ? $target->location_id : '';
}
$error_payload['target_id'] = $request->input('assigned_asset');
$error_payload['target_type'] = 'asset';
} elseif (request('checkout_to_type')=='user') {
// Fetch the target and set the asset's new location_id
$target = User::find(request('assigned_user'));
$asset->location_id = ($target) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_user');
$error_payload['target_type'] = 'user';
}
if (!isset($target)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], 'No valid checkout target specified for asset '.e($asset->asset_tag).'.'));
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
}
$checkout_at = request('checkout_at', date("Y-m-d H:i:s"));
$expected_checkin = request('expected_checkin', null);
$note = request('note', null);
$asset_name = request('name', null);
// Set the location ID to the RTD location id if there is one
if ($asset->rtd_location_id!='') {
$asset->location_id = $target->rtd_location_id;
}
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name)) {
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
}
@@ -447,7 +657,7 @@ class AssetsController extends Controller
* @since [v4.0]
* @return JsonResponse
*/
public function checkin($asset_id)
public function checkin(Request $request, $asset_id)
{
$this->authorize('checkin', Asset::class);
$asset = Asset::findOrFail($asset_id);
@@ -464,32 +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 (Input::has('status_id')) {
$asset->status_id = e(Input::get('status_id'));
if ($request->has('location_id')) {
$asset->location_id = $request->input('location_id');
}
if (Input::has('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;
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')));
}
@@ -524,7 +721,11 @@ class AssetsController extends Controller
if ($asset) {
// We don't want to log this as a normal update, so let's bypass that
$asset->unsetEventDispatcher();
$asset->next_audit_date = $request->input('next_audit_date');
$asset->last_audit_date = date('Y-m-d h:i:s');
if ($asset->save()) {
$log = $asset->logAudit(request('note'),request('location_id'));
return response()->json(Helper::formatStandardApiResponse('success', [
@@ -541,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

@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Category;
use App\Http\Transformers\CategoriesTransformer;
use App\Http\Transformers\SelectlistTransformer;
class CategoriesController extends Controller
{
@@ -20,10 +21,10 @@ class CategoriesController extends Controller
public function index(Request $request)
{
$this->authorize('view', Category::class);
$allowed_columns = ['id', 'name','category_type','use_default_eula','require_acceptance','checkin_email'];
$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','require_acceptance','checkin_email'])
->withCount('assets', 'accessories', 'consumables', 'components');
$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','licenses');
if ($request->has('search')) {
$categories = $categories->TextSearch($request->input('search'));
@@ -32,7 +33,7 @@ class CategoriesController extends Controller
$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';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
$categories->orderBy($sort, $order);
$total = $categories->count();
@@ -91,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());
@@ -128,4 +129,40 @@ class CategoriesController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request, $category_type = 'asset')
{
$categories = Category::select([
'id',
'name',
'image',
]);
if ($request->has('search')) {
$categories = $categories->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$categories = $categories->where('category_type', $category_type)->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($categories as $category) {
$category->use_image = ($category->image) ? url('/').'/uploads/categories/'.$category->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($categories);
}
}

View File

@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Company;
use App\Http\Transformers\SelectlistTransformer;
class CompaniesController extends Controller
{
@@ -21,12 +22,20 @@ class CompaniesController extends Controller
{
$this->authorize('view', Company::class);
$allowed_columns = ['id','name'];
$allowed_columns = [
'id',
'name',
'created_at',
'updated_at',
'users_count',
'assets_count',
'licenses_count',
'accessories_count',
'consumables_count',
'components_count',
];
$companies = Company::withCount('assets','licenses','accessories','consumables','components','users')
->withCount('users')->withCount('users')->withCount('assets')
->withCount('licenses')->withCount('accessories')
->withCount('consumables')->withCount('components');
$companies = Company::withCount('assets','licenses','accessories','consumables','components','users');
if ($request->has('search')) {
$companies->TextSearch($request->input('search'));
@@ -95,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());
@@ -141,4 +150,37 @@ class CompaniesController extends Controller
}
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$companies = Company::select([
'companies.id',
'companies.name',
'companies.image',
]);
if ($request->has('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}
$companies = $companies->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($companies as $company) {
$company->use_image = ($company->image) ? url('/').'/uploads/companies/'.$company->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($companies);
}
}

View File

@@ -24,17 +24,29 @@ 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')) {
$components = $components->TextSearch($request->input('search'));
}
if ($request->has('company_id')) {
$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);
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location'];
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location','image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
@@ -89,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')));
}
@@ -110,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());
@@ -149,7 +159,7 @@ class ComponentsController extends Controller
*/
public function getAssets(Request $request, $id)
{
$this->authorize('index', Asset::class);
$this->authorize('view', \App\Models\Asset::class);
$component = Component::findOrFail($id);
$assets = $component->assets();
@@ -158,6 +168,6 @@ class ComponentsController extends Controller
$limit = $request->input('limit', 50);
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
return (new ComponentsAssetsTransformer)->transformAssets($assets, $total);
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
}
}

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')
);
@@ -43,7 +42,7 @@ class ConsumablesController extends Controller
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty'];
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty','image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
@@ -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());
@@ -160,7 +159,6 @@ class ConsumablesController extends Controller
*/
public function getDataView($consumableId)
{
//$consumable = Consumable::find($consumableID);
$consumable = Consumable::with(array('consumableAssignments'=>
function ($query) {
$query->orderBy('created_at', 'DESC');
@@ -171,18 +169,16 @@ class ConsumablesController extends Controller
},
))->find($consumableId);
// $consumable->load('consumableAssignments.admin','consumableAssignments.user');
if (!Company::isCurrentUserHasAccess($consumable)) {
return ['total' => 0, 'rows' => []];
}
$this->authorize('view', Component::class);
$this->authorize('view', Consumable::class);
$rows = array();
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'name' => $consumable_assignment->user->present()->nameUrl(),
'created_at' => ($consumable_assignment->created_at->format('Y-m-d H:i:s')=='-0001-11-30 00:00:00') ? '' : $consumable_assignment->created_at->format('Y-m-d H:i:s'),
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
];
}

View File

@@ -2,11 +2,14 @@
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\CustomFieldsTransformer;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Http\Request;
use Validator;
use Illuminate\Validation\Rule;
class CustomFieldsController extends Controller
{
@@ -23,13 +26,95 @@ 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());
}
/**
* Shows the given field
* @author [V. Cordes] [<volker@fdatek.de>]
* @param int $id
* @since [v4.1.10]
* @return View
*/
public function show($id)
{
$this->authorize('show', CustomField::class);
if ($field = CustomField::find($id)) {
return (new CustomFieldsTransformer)->transformCustomField($field);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/custom_fields/message.field.invalid')), 200);
}
/**
* Update the specified field
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->authorize('update', CustomField::class);
$field = CustomField::findOrFail($id);
/**
* Updated values for the field,
* without the "field_encrypted" flag, preventing the change of encryption status
* @var array
*/
$data = $request->except(['field_encrypted']);
$validator = Validator::make($data, $field->validationRules());
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
$field->fill($data);
if ($field->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
}
/**
* Store a newly created field.
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @since [v4.1.10]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->authorize('create', CustomField::class);
$field = new CustomField;
$data = $request->all();
$validator = Validator::make($data, $field->validationRules());
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
$field->fill($data);
if ($field->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
}
public function postReorder(Request $request, $id)
{
$fieldset = CustomFieldset::find($id);
$this->authorize('update', $fieldset);
$fields = array();
$order_array = array();
@@ -47,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.
@@ -57,15 +176,17 @@ class CustomFieldsController extends Controller
*/
public function destroy($field_id)
{
$field = CustomField::find($field_id);
$field = CustomField::findOrFail($field_id);
$this->authorize('delete', $field);
if ($field->fieldset->count() >0) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Field is in use.'));
} else {
$field->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
}
$field->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
}
}

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

@@ -8,6 +8,7 @@ use App\Models\Department;
use App\Http\Transformers\DepartmentsTransformer;
use App\Helpers\Helper;
use Auth;
use App\Http\Transformers\SelectlistTransformer;
class DepartmentsController extends Controller
{
@@ -21,16 +22,17 @@ class DepartmentsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Department::class);
$allowed_columns = ['id','name'];
$allowed_columns = ['id','name','image','users_count'];
$departments = Department::select([
'id',
'name',
'location_id',
'company_id',
'manager_id',
'created_at',
'updated_at'
'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')) {
@@ -41,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();
@@ -101,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')));
}
@@ -110,4 +125,38 @@ class DepartmentsController extends Controller
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$departments = Department::select([
'id',
'name',
'image',
]);
if ($request->has('search')) {
$departments = $departments->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$departments = $departments->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($departments as $department) {
$department->use_image = ($department->image) ? url('/').'/uploads/departments/'.$department->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($departments);
}
}

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');
@@ -73,7 +73,7 @@ class GroupsController extends Controller
{
$this->authorize('view', Group::class);
$group = Group::findOrFail($id);
return $group;
return (new GroupsTransformer)->transformGroup($group);
}
@@ -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

@@ -13,6 +13,8 @@ use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Session;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Artisan;
use App\Models\Asset;
class ImportController extends Controller
{
@@ -56,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 {
@@ -70,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;
}
@@ -94,6 +120,8 @@ class ImportController extends Controller
public function process(ItemImportRequest $request, $import_id)
{
$this->authorize('create', Asset::class);
// Run a backup immediately before processing
Artisan::call('backup:run');
$errors = $request->import(Import::find($import_id));
$redirectTo = "hardware.index";
switch ($request->get('import-type')) {

View File

@@ -2,11 +2,15 @@
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Models\License;
use App\Models\Company;
use App\Models\License;
use App\Models\LicenseSeat;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class LicensesController extends Controller
{
@@ -21,11 +25,8 @@ class LicensesController extends Controller
public function index(Request $request)
{
$this->authorize('view', License::class);
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer', 'supplier'));
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats'));
if ($request->has('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
}
if ($request->has('company_id')) {
$licenses->where('company_id','=',$request->input('company_id'));
@@ -63,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'));
}
@@ -71,30 +76,41 @@ class LicensesController extends Controller
$licenses->where('supplier_id','=',$request->input('supplier_id'));
}
if ($request->has('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
switch ($request->input('sort')) {
case 'manufacturer':
$licenses = $licenses->OrderManufacturer($order);
case 'manufacturer':
$licenses = $licenses->leftJoin('manufacturers', 'licenses.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
break;
case 'supplier':
$licenses = $licenses->OrderSupplier($order);
$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->OrderCompany($order);
$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'];
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$licenses = $licenses->orderBy($sort, $order);
break;
}
$total = $licenses->count();
$licenses = $licenses->skip($offset)->take($limit)->get();
return (new LicensesTransformer)->transformLicenses($licenses, $total);
@@ -114,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()));
}
/**
@@ -125,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);
}
@@ -147,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());
}
/**
@@ -160,5 +191,58 @@ 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')));
}
/**
* Get license seat listing
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $licenseId
* @return \Illuminate\Contracts\View\View
*/
public function seats(Request $request, $licenseId)
{
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
$offset = request('offset', 0);
$limit = request('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$total = $seats->count();
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
}

View File

@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Location;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
class LocationsController extends Controller
{
@@ -20,10 +21,12 @@ class LocationsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Location::class);
$allowed_columns = ['id','name','address','address2','city','state','country','zip','created_at',
'updated_at','parent_id', 'manager_id'];
$allowed_columns = [
'id','name','address','address2','city','state','country','zip','created_at',
'updated_at','manager_id','image',
'assigned_assets_count','users_count','assets_count','currency'];
$locations = Location::select([
$locations = Location::with('parent', 'manager', 'childLocations')->select([
'locations.id',
'locations.name',
'locations.address',
@@ -36,23 +39,39 @@ class LocationsController extends Controller
'locations.manager_id',
'locations.created_at',
'locations.updated_at',
'locations.image',
'locations.currency'
])->withCount('assets')->withCount('users');
])->withCount('assignedAssets')
->withCount('assets')
->withCount('users');
if ($request->has('search')) {
$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();
return (new LocationsTransformer)->transformLocations($locations, $total);
}
@@ -74,7 +93,6 @@ class LocationsController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
/**
@@ -104,12 +122,18 @@ 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());
if ($location->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.update.success')));
return response()->json(
Helper::formatStandardApiResponse(
'success',
(new LocationsTransformer)->transformLocation($location),
trans('admin/locations/message.update.success')
)
);
}
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
@@ -129,7 +153,42 @@ class LocationsController extends Controller
$location = Location::findOrFail($id);
$this->authorize('delete', $location);
$location->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$locations = Location::select([
'locations.id',
'locations.name',
'locations.image',
]);
if ($request->has('search')) {
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->get('search').'%');
}
$locations = $locations->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($locations as $location) {
$location->use_text = $location->name;
$location->use_image = ($location->image) ? url('/').'/uploads/locations/'.$location->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($locations);
}
}

View File

@@ -8,6 +8,7 @@ use App\Helpers\Helper;
use App\Models\Manufacturer;
use App\Http\Transformers\DatatablesTransformer;
use App\Http\Transformers\ManufacturersTransformer;
use App\Http\Transformers\SelectlistTransformer;
class ManufacturersController extends Controller
{
@@ -21,17 +22,23 @@ class ManufacturersController extends Controller
public function index(Request $request)
{
$this->authorize('view', Manufacturer::class);
$allowed_columns = ['id','name','url','support_url','support_email','support_phone','created_at','updated_at'];
$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')
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';
@@ -92,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());
@@ -120,4 +127,39 @@ class ManufacturersController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$manufacturers = Manufacturer::select([
'id',
'name',
'image',
]);
if ($request->has('search')) {
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->get('search').'%');
}
$manufacturers = $manufacturers->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($manufacturers as $manufacturer) {
$manufacturer->use_text = $manufacturer->name;
$manufacturer->use_image = ($manufacturer->image) ? url('/').'/uploads/manufacturers/'.$manufacturer->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($manufacturers);
}
}

View File

@@ -0,0 +1,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,21 +40,27 @@ 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();
$actionlogs = $actionlogs->orderBy($sort, $order);
$actionlogs = $actionlogs->skip($offset)->take($limit)->get();
return (new ActionlogsTransformer)->transformActionlogs($actionlogs, $total);
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
}
}

View File

@@ -5,78 +5,25 @@ namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Ldap;
use Validator;
use App\Models\Setting;
use Mail;
use App\Notifications\SlackTest;
use Notification;
use App\Notifications\MailTest;
class SettingsController extends Controller
{
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function index()
public function ldaptest()
{
//
}
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
/**
* Store a newly created resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
public function getLdapTest()
{
\Log::debug('Preparing to test LDAP connection');
try {
@@ -98,4 +45,104 @@ class SettingsController extends Controller
}
public function ldaptestlogin(Request $request)
{
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled. Cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
$rules = array(
'ldaptest_user' => 'required',
'ldaptest_password' => 'required'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
\Log::debug('LDAP Validation test failed.');
$validation_errors = implode(' ',$validator->errors()->all());
return response()->json(['message' => $validator->errors()->all()], 400);
}
\Log::debug('Preparing to test LDAP login');
try {
$connection = Ldap::connectToLdap();
try {
Ldap::bindAdminToLdap($connection);
\Log::debug('Attempting to bind to LDAP for LDAP test');
try {
$ldap_user = Ldap::findAndBindUserLdap($request->input('ldaptest_user'), $request->input('ldaptest_password'));
if ($ldap_user) {
\Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
}
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
} catch (\Exception $e) {
\Log::debug('LDAP login failed');
return response()->json(['message' => $e->getMessage()], 400);
}
} catch (\Exception $e) {
\Log::debug('Bind failed');
return response()->json(['message' => $e->getMessage()], 400);
//return response()->json(['message' => $e->getMessage()], 500);
}
} catch (\Exception $e) {
\Log::debug('Connection failed');
return response()->json(['message' => $e->getMessage()], 500);
}
}
public function slacktest()
{
if ($settings = Setting::getSettings()->slack_channel=='') {
\Log::debug('Slack is not enabled. Cannot test.');
return response()->json(['message' => 'Slack is not enabled, cannot test.'], 400);
}
\Log::debug('Preparing to test slack connection');
try {
Notification::send($settings = Setting::getSettings(), new SlackTest());
return response()->json(['message' => 'Success'], 200);
} catch (\Exception $e) {
\Log::debug('Slack connection failed');
return response()->json(['message' => $e->getMessage()], 400);
}
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
if (!config('app.lock_passwords')) {
try {
Notification::send(Setting::first(), new MailTest());
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
return response()->json(['message' => 'Mail would have been sent, but this application is in demo mode! '], 200);
}
}

View File

@@ -22,7 +22,7 @@ class StatuslabelsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Statuslabel::class);
$allowed_columns = ['id','name','created_at'];
$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');
@@ -137,8 +137,14 @@ class StatuslabelsController extends Controller
$this->authorize('delete', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$this->authorize('delete', $statuslabel);
$statuslabel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
// Check that there are no assets associated
if ($statuslabel->assets()->count() == 0) {
$statuslabel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets')));
}
@@ -154,23 +160,25 @@ class StatuslabelsController extends Controller
public function getAssetCountByStatuslabel()
{
$this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets')->get();
$statusLabels = Statuslabel::with('assets')->get();
$labels=[];
$points=[];
$colors=[];
foreach ($statusLabels as $statusLabel) {
if ($statusLabel->assets()->count() > 0) {
$labels[]=$statusLabel->name;
$points[]=$statusLabel->assets()->whereNull('assigned_to')->count();
if ($statusLabel->color!='') {
$colors[]=$statusLabel->color;
foreach ($statuslabels as $statuslabel) {
if ($statuslabel->assets_count > 0) {
$labels[]=$statuslabel->name. ' ('.number_format($statuslabel->assets_count).')';
$points[]=$statuslabel->assets_count;
if ($statuslabel->color!='') {
$colors[]=$statuslabel->color;
}
}
}
$labels[]='Deployed';
$points[]=Asset::whereNotNull('assigned_to')->count();
$colors_array = array_merge($colors, Helper::chartColors());
$result= [
@@ -230,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

@@ -7,6 +7,8 @@ use App\Http\Controllers\Controller;
use App\Helpers\Helper;
use App\Models\Supplier;
use App\Http\Transformers\SuppliersTransformer;
use App\Http\Transformers\SelectlistTransformer;
class SuppliersController extends Controller
{
@@ -20,11 +22,11 @@ class SuppliersController extends Controller
public function index(Request $request)
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['id','name','address','phone','contact','fax','email'];
$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')
)->withCount('assets')->withCount('licenses')->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')) {
@@ -91,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());
@@ -113,10 +115,60 @@ class SuppliersController extends Controller
public function destroy($id)
{
$this->authorize('delete', Supplier::class);
$supplier = Supplier::findOrFail($id);
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets', 'licenses')->findOrFail($id);
$this->authorize('delete', $supplier);
if ($supplier->assets_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
}
if ($supplier->asset_maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count])));
}
if ($supplier->licenses_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
}
$supplier->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$suppliers = Supplier::select([
'id',
'name',
'image',
]);
if ($request->has('search')) {
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->get('search').'%');
}
$suppliers = $suppliers->orderBy('name', 'ASC')->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($suppliers as $supplier) {
$supplier->use_text = $supplier->name;
$supplier->use_image = ($supplier->image) ? url('/').'/uploads/suppliers/'.$supplier->image : null;
}
return (new SelectlistTransformer)->transformSelectlist($suppliers);
}
}

View File

@@ -10,6 +10,8 @@ use App\Models\User;
use App\Helpers\Helper;
use App\Http\Requests\SaveUserRequest;
use App\Models\Asset;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\SelectlistTransformer;
class UsersController extends Controller
{
@@ -26,53 +28,65 @@ 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.username',
'users.location_id',
'users.manager_id',
'users.first_name',
'users.last_name',
'users.created_at',
'users.notes',
'users.activated',
'users.address',
'users.avatar',
'users.city',
'users.company_id',
'users.last_login',
'users.country',
'users.created_at',
'users.deleted_at',
'users.department_id',
'users.activated'
])->with('manager', 'groups', 'userloc', 'company', 'department','throttle','assets','licenses','accessories','consumables')
'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');
$users = Company::scopeCompanyables($users);
if ($request->has('search')) {
$users = $users->TextSearch($request->input('search'));
}
if (($request->has('deleted')) && ($request->input('deleted')=='true')) {
$users = $users->GetDeleted();
}
if ($request->has('company_id')) {
$users = $users->where('company_id', '=', $request->input('company_id'));
$users = $users->where('users.company_id', '=', $request->input('company_id'));
}
if ($request->has('location_id')) {
$users = $users->where('location_id', '=', $request->input('location_id'));
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
if ($request->has('group_id')) {
$users = $users->ByGroup($request->get('group_id'));
}
if ($request->has('department_id')) {
$users = $users->where('department_id','=',$request->input('department_id'));
$users = $users->where('users.department_id','=',$request->input('department_id'));
}
if ($request->has('search')) {
$users = $users->TextSearch($request->input('search'));
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$offset = request('offset', 0);
$limit = request('limit', 50);
$limit = request('limit', 20);
switch ($request->input('sort')) {
case 'manager':
@@ -89,19 +103,84 @@ class UsersController extends Controller
[
'last_name','first_name','email','jobtitle','username','employee_num',
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
'two_factor_enrolled','two_factor_optin','last_login'
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
'country', 'zip'
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
$users = $users->orderBy($sort, $order);
break;
}
$total = $users->count();
$users = $users->skip($offset)->take($limit)->get();
return (new UsersTransformer)->transformUsers($users, $total);
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*
*/
public function selectlist(Request $request)
{
$users = User::select(
[
'users.id',
'users.username',
'users.employee_num',
'users.first_name',
'users.last_name',
'users.gravatar',
'users.avatar',
'users.email',
]
)->where('show_in_list', '=', '1');
$users = Company::scopeCompanyables($users);
if ($request->has('search')) {
$users = $users->where('first_name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('last_name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
}
$users = $users->orderBy('last_name', 'asc')->orderBy('first_name', 'asc');
$users = $users->paginate(50);
foreach ($users as $user) {
$name_str = '';
if ($user->last_name!='') {
$name_str .= e($user->last_name).', ';
}
$name_str .= e($user->first_name);
if ($user->username!='') {
$name_str .= ' ('.e($user->username).')';
}
if ($user->employee_num!='') {
$name_str .= ' - #'.e($user->employee_num);
}
$user->use_text = $name_str;
$user->use_image = ($user->present()->gravatar) ? $user->present()->gravatar : null;
}
return (new SelectlistTransformer)->transformSelectlist($users);
}
/**
* Store a newly created resource in storage.
*
@@ -112,13 +191,16 @@ 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.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
}
@@ -149,14 +231,22 @@ 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());
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
if ($user->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
@@ -201,7 +291,49 @@ class UsersController extends Controller
public function assets($id)
{
$this->authorize('view', User::class);
$this->authorize('view', Asset::class);
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
return response()->json($assets);
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->assetloc) && ($maintenance->asset->assetloc->currency!='')) {
$maintenance_cost = $maintenance->asset->assetloc->currency.$maintenance->cost;
} else {
$maintenance_cost = $settings->default_currency.$maintenance->cost;
}
$rows[] = array(
'id' => $maintenance->id,
'asset_name' => ($maintenance->asset) ? (string)link_to_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.
@@ -154,16 +75,21 @@ class AssetMaintenancesController extends Controller
*/
public function create()
{
$asset = null;
if ($asset = Asset::find(request('asset_id'))) {
// We have to set this so that the correct property is set in the select2 ajax dropdown
$asset->asset_id = $asset->id;
}
// Prepare Asset Maintenance Type List
$assetMaintenanceType = [
'' => 'Select an asset maintenance type',
] + AssetMaintenance::getImprovementOptions();
// Mark the selected asset, if it came in
// Render the view
return view('asset_maintenances/edit')
->with('asset_list', Helper::detailedAssetList())
->with('selectedAsset', request('asset_id'))
->with('supplier_list', Helper::suppliersList())
->with('asset', $asset)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', new AssetMaintenance);
}
@@ -187,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();
}
@@ -260,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

@@ -17,6 +17,7 @@ use App\Models\Company;
use Config;
use App\Helpers\Helper;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -34,12 +35,12 @@ class AssetModelsController extends Controller
* the content for the accessories listing, which is generated in getDatatable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see AssetModelsController::getDatatable() method that generates the JSON response
* @since [v1.0]
* @return View
*/
public function index()
{
$this->authorize('index', AssetModel::class);
return view('models/index');
}
@@ -52,11 +53,10 @@ class AssetModelsController extends Controller
*/
public function create()
{
// Show the page
return view('models/edit')
->with('category_list', Helper::categoryList('asset'))
$this->authorize('create', AssetModel::class);
$category_type = 'asset';
return view('models/edit')->with('category_type',$category_type)
->with('depreciation_list', Helper::depreciationList())
->with('manufacturer_list', Helper::manufacturerList())
->with('item', new AssetModel);
}
@@ -68,9 +68,10 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @return Redirect
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize('create', AssetModel::class);
// Create a new asset model
$model = new AssetModel;
@@ -82,7 +83,7 @@ class AssetModelsController extends Controller
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->notes = $request->input('notes');
$model->user_id = Auth::guard('api')->user();
$model->user_id = Auth::id();
$model->requestable = Input::has('requestable');
if ($request->input('custom_fieldset')!='') {
@@ -90,63 +91,35 @@ class AssetModelsController extends Controller
}
if (Input::file('image')) {
$image = Input::file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/models/'.$file_name);
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$file_name = str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
$path = app('models_upload_path');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
$model->image = $file_name;
}
// 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
$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.
*
@@ -157,17 +130,16 @@ class AssetModelsController extends Controller
*/
public function edit($modelId = null)
{
// Check if the model exists
if (is_null($item = AssetModel::find($modelId))) {
// Redirect to the model management page
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
$this->authorize('update', AssetModel::class);
if ($item = AssetModel::find($modelId)) {
$category_type = 'asset';
$view = View::make('models/edit', compact('item','category_type'));
$view->with('depreciation_list', Helper::depreciationList());
return $view;
}
$view = View::make('models/edit', compact('item'));
$view->with('category_list', Helper::categoryList('asset'));
$view->with('depreciation_list', Helper::depreciationList());
$view->with('manufacturer_list', Helper::manufacturerList());
return $view;
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
@@ -180,45 +152,68 @@ class AssetModelsController extends Controller
* @param int $modelId
* @return Redirect
*/
public function update(Request $request, $modelId = null)
public function update(ImageUploadRequest $request, $modelId = null)
{
$this->authorize('update', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
// Redirect to the models management page
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
$model->depreciation_id = $request->input('depreciation_id');
$model->eol = $request->input('eol');
$model->depreciation_id = $request->input('depreciation_id');
$model->eol = $request->input('eol');
$model->name = $request->input('name');
$model->model_number = $request->input('model_number');
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->notes = $request->input('notes');
$model->requestable = $request->input('requestable', '0');
$model->requestable = Input::has('requestable');
$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'));
}
}
if (Input::file('image')) {
$image = Input::file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/models/'.$file_name);
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$model->image = $file_name;
}
$old_image = $model->image;
if ($request->input('image_delete') == 1 && Input::file('image') == "") {
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$model->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $model->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('models_upload_path').$file_name);
} else {
$image->move(app('models_upload_path'), $file_name);
}
$model->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('models_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($model->save()) {
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
}
@@ -236,6 +231,7 @@ class AssetModelsController extends Controller
*/
public function destroy($modelId)
{
$this->authorize('delete', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found'));
@@ -245,6 +241,15 @@ class AssetModelsController extends Controller
// Throw an error that this model is associated with assets
return redirect()->route('models.index')->with('error', trans('admin/models/message.assoc_users'));
}
if ($model->image) {
try {
unlink(public_path().'/uploads/models/'.$model->image);
} catch (\Exception $e) {
\Log::error($e);
}
}
// Delete the model
$model->delete();
@@ -263,7 +268,7 @@ class AssetModelsController extends Controller
*/
public function getRestore($modelId = null)
{
$this->authorize('create', AssetModel::class);
// Get user information
$model = AssetModel::withTrashed()->find($modelId);
@@ -294,6 +299,7 @@ class AssetModelsController extends Controller
*/
public function show($modelId = null)
{
$this->authorize('view', AssetModel::class);
$model = AssetModel::withTrashed()->find($modelId);
if (isset($model->id)) {
@@ -326,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;
@@ -352,49 +356,6 @@ class AssetModelsController extends Controller
/**
* Get the asset information to present to the model view detail page
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @param Request $request
* @param $modelID
* @return String JSON
* @internal param int $modelId
*/
public function getDataView(Request $request, $modelID)
{
$assets = Asset::where('model_id', '=', $modelID)->with('company', 'assetstatus');
if (Input::has('search')) {
$assets = $assets->TextSearch(e($request->input('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['name', 'serial','asset_tag'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$assets = $assets->orderBy($sort, $order);
$assetsCount = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
$rows = array();
$all_custom_fields = CustomField::all();
foreach ($assets as $asset) {
$rows[] = $asset->present()->forDataTable($all_custom_fields);
}
$data = array('total' => $assetsCount, 'rows' => $rows);
return $data;
}
/**
* Returns a view that allows the user to bulk edit model attrbutes
@@ -405,20 +366,41 @@ class AssetModelsController extends Controller
*/
public function postBulkEdit(Request $request)
{
$models_raw_array = Input::get('ids');
$models = AssetModel::whereIn('id', $models_raw_array)->get();
$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);
$models_raw_array = Input::get('ids');
// Make sure some IDs have been selected
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets')->orderBy('assets_count', 'ASC')->get();
// If deleting....
if ($request->input('bulk_actions')=='delete') {
$valid_count = 0;
foreach ($models as $model) {
if ($model->assets_count == 0) {
$valid_count++;
}
}
return view('models/bulk-delete', compact('models'))->with('valid_count', $valid_count);
// Otherwise display the bulk edit screen
} else {
$nochange = ['NC' => 'No Change'];
$fieldset_list = $nochange + Helper::customFieldsetList();
$depreciation_list = $nochange + Helper::depreciationList();
return view('models/bulk-edit', compact('models'))
->with('fieldset_list', $fieldset_list)
->with('depreciation_list', $depreciation_list);
}
}
return redirect()->route('models.index')
->with('error', 'You must select at least one model to edit.');
}
@@ -437,6 +419,7 @@ class AssetModelsController extends Controller
$models_raw_array = Input::get('ids');
$update_array = array();
if (($request->has('manufacturer_id') && ($request->input('manufacturer_id')!='NC'))) {
$update_array['manufacturer_id'] = $request->input('manufacturer_id');
}
@@ -463,4 +446,91 @@ class AssetModelsController extends Controller
}
/**
* Validate and delete the given Asset Models. An Asset Model
* cannot be deleted if there are associated assets.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $modelId
* @return Redirect
*/
public function postBulkDelete(Request $request)
{
$models_raw_array = Input::get('ids');
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets')->get();
$del_error_count = 0;
$del_count = 0;
foreach ($models as $model) {
\Log::debug($model->id);
if ($model->assets_count > 0) {
$del_error_count++;
} else {
$model->delete();
$del_count++;
}
}
\Log::debug($del_count);
\Log::debug($del_error_count);
if ($del_error_count == 0) {
return redirect()->route('models.index')
->with('success', trans('admin/models/message.bulkdelete.success',['success_count'=> $del_count] ));
}
return redirect()->route('models.index')
->with('warning', trans('admin/models/message.bulkdelete.success_partial', ['fail_count'=>$del_error_count, 'success_count'=> $del_count]));
}
return redirect()->route('models.index')
->with('error', trans('admin/models/message.bulkdelete.error'));
}
/**
* 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

@@ -47,19 +47,45 @@ class LoginController extends Controller
public function __construct()
{
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
\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'));
@@ -71,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.
@@ -113,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()) {
@@ -133,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
@@ -147,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);
@@ -251,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!');
}
@@ -320,4 +358,9 @@ class LoginController extends Controller
return redirect()->route('login');
}
public function redirectTo()
{
return Session::get('backUrl') ? Session::get('backUrl') : $this->redirectTo;
}
}

View File

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

View File

@@ -25,7 +25,7 @@ class ResetPasswordController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.

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

@@ -15,6 +15,8 @@ use Lang;
use Redirect;
use Str;
use View;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This class controls all actions related to Categories for
@@ -38,6 +40,7 @@ class CategoriesController extends Controller
public function index()
{
// Show the page
$this->authorize('view', Category::class);
return view('categories/index');
}
@@ -53,6 +56,7 @@ class CategoriesController extends Controller
public function create()
{
// Show the page
$this->authorize('create', Category::class);
$category_types= Helper::categoryTypeList();
return view('categories/edit')->with('item', new Category)
->with('category_types', $category_types);
@@ -67,11 +71,10 @@ class CategoriesController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
// create a new model instance
$this->authorize('create', Category::class);
$category = new Category();
// Update the category data
$category->name = $request->input('name');
$category->category_type = $request->input('category_type');
$category->eula_text = $request->input('eula_text');
@@ -80,6 +83,18 @@ class CategoriesController extends Controller
$category->checkin_email = $request->input('checkin_email', '0');
$category->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/categories/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$category->image = $file_name;
}
if ($category->save()) {
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.create.success'));
}
@@ -98,10 +113,9 @@ class CategoriesController extends Controller
*/
public function edit($categoryId = null)
{
// Check if the category exists
$this->authorize('edit', Category::class);
if (is_null($item = Category::find($categoryId))) {
// Redirect to the blogs management page
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.does_not_exist'));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
$category_types= Helper::categoryTypeList();
@@ -120,9 +134,9 @@ class CategoriesController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @since [v1.0]
*/
public function update(Request $request, $categoryId = null)
public function update(ImageUploadRequest $request, $categoryId = null)
{
// Check if the blog post exists
$this->authorize('edit', Category::class);
if (is_null($category = Category::find($categoryId))) {
// Redirect to the categories management page
return redirect()->to('admin/categories')->with('error', trans('admin/categories/message.does_not_exist'));
@@ -138,6 +152,38 @@ class CategoriesController extends Controller
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$old_image = $category->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$category->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $category->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('categories_upload_path').$file_name);
} else {
$image->move(app('categories_upload_path'), $file_name);
}
$category->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('categories_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($category->save()) {
// Redirect to the new category page
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.update.success'));
@@ -156,24 +202,25 @@ class CategoriesController extends Controller
*/
public function destroy($categoryId)
{
$this->authorize('delete', Category::class);
// Check if the category exists
if (is_null($category = Category::find($categoryId))) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.not_found'));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
}
if ($category->has_models() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
} elseif ($category->accessories()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
} elseif ($category->consumables()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
} elseif ($category->components()->count() > 0) {
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
}
$category->delete();
// Redirect to the locations management page
return redirect()->to(route('categories.index'))->with('success', trans('admin/categories/message.delete.success'));
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
}
@@ -189,6 +236,7 @@ class CategoriesController extends Controller
*/
public function show($id)
{
$this->authorize('view', Category::class);
if ($category = Category::find($id)) {
if ($category->category_type=='asset') {

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

@@ -7,6 +7,8 @@ use Lang;
use Redirect;
use View;
use Illuminate\Http\Request;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to Companies for
@@ -27,6 +29,8 @@ final class CompaniesController extends Controller
*/
public function index()
{
$this->authorize('view', Company::class);
return view('companies/index')->with('companies', Company::all());
}
@@ -39,6 +43,8 @@ final class CompaniesController extends Controller
*/
public function create()
{
$this->authorize('create', Company::class);
return view('companies/edit')->with('item', new Company);
}
@@ -50,11 +56,24 @@ final class CompaniesController extends Controller
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Company::class);
$company = new Company;
$company->name = $request->input('name');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/companies/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$company->image = $file_name;
}
if ($company->save()) {
return redirect()->route('companies.index')
->with('success', trans('admin/companies/message.create.success'));
@@ -77,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);
}
@@ -89,14 +111,48 @@ final class CompaniesController extends Controller
* @param int $companyId
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $companyId)
public function update(ImageUploadRequest $request, $companyId)
{
if (is_null($company = Company::find($companyId))) {
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;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$company->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $company->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('companies_upload_path').$file_name);
} else {
$image->move(app('companies_upload_path'), $file_name);
}
$company->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('companies_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($company->save()) {
return redirect()->route('companies.index')
->with('success', trans('admin/companies/message.update.success'));
@@ -119,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

@@ -2,6 +2,8 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Component;
use App\Models\CustomField;
@@ -21,6 +23,7 @@ use View;
use Validator;
use Illuminate\Http\Request;
use Gate;
use Image;
/**
* This class controls all actions related to Components for
@@ -57,12 +60,9 @@ class ComponentsController extends Controller
public function create()
{
$this->authorize('create', Component::class);
// Show the page
return view('components/edit')
->with('item', new Component)
->with('category_list', Helper::categoryList('component'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList());
$category_type = 'component';
return view('components/edit')->with('category_type',$category_type)
->with('item', new Component);
}
@@ -74,22 +74,34 @@ class ComponentsController extends Controller
* @since [v3.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function store()
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Component::class);
$component = new Component();
$component->name = Input::get('name');
$component->category_id = Input::get('category_id');
$component->location_id = Input::get('location_id');
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$component->order_number = Input::get('order_number');
$component->min_amt = Input::get('min_amt');
$component->serial = Input::get('serial');
$component->purchase_date = Input::get('purchase_date');
$component->purchase_cost = request('purchase_cost');
$component->qty = Input::get('qty');
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number', null);
$component->min_amt = $request->input('min_amt', null);
$component->serial = $request->input('serial', null);
$component->purchase_date = $request->input('purchase_date', null);
$component->purchase_cost = $request->input('purchase_cost', null);
$component->qty = $request->input('qty');
$component->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/components/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$component->image = $file_name;
}
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
}
@@ -107,16 +119,18 @@ class ComponentsController extends Controller
*/
public function edit($componentId = null)
{
if (is_null($item = Component::find($componentId))) {
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
if ($item = Component::find($componentId)) {
$this->authorize('update', $item);
$category_type = 'component';
return view('components/edit', compact('item'))->with('category_type', $category_type);
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
$this->authorize('update', $item);
return view('components/edit', compact('item'))
->with('category_list', Helper::categoryList('component'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList());
}
@@ -129,7 +143,7 @@ class ComponentsController extends Controller
* @since [v3.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function update($componentId = null)
public function update(ImageUploadRequest $request, $componentId = null)
{
if (is_null($component = Component::find($componentId))) {
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
@@ -150,6 +164,19 @@ class ComponentsController extends Controller
$component->purchase_cost = request('purchase_cost');
$component->qty = Input::get('qty');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/components/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$component->image = $file_name;
} elseif ($request->input('image_delete')=='1') {
$component->image = null;
}
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
}
@@ -167,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);
@@ -175,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.
*
@@ -228,7 +242,7 @@ class ComponentsController extends Controller
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
$this->authorize('checkout', $component);
return view('components/checkout', compact('component'))->with('assets_list', Helper::detailedAssetList());
return view('components/checkout', compact('component'));
}
/**
@@ -287,36 +301,97 @@ class ComponentsController extends Controller
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
}
/**
* Returns a view that allows the checkin of a component from an asset.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::postCheckout() method that stores the data.
* @since [v4.1.4]
* @param int $componentId
* @return \Illuminate\Contracts\View\View
*/
public function getCheckin($component_asset_id)
{
// This could probably be done more cleanly but I am very tired. - @snipe
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
}
if (is_null($asset = Asset::find($component_assets->asset_id))) {
return redirect()->route('components.index')->with('error',
trans('admin/components/message.not_found'));
}
$this->authorize('checkin', $component);
return view('components/checkin', compact('component_assets','component','asset'));
}
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
}
/**
* Return JSON data to populate the components view,
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getView() method that returns the view.
* @since [v3.0]
* @param int $componentId
* @return string JSON
*/
public function getDataView($componentId)
* Validate and store checkin data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ComponentsController::getCheckout() method that returns the form.
* @since [v4.1.4]
* @param Request $request
* @param int $componentId
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckin(Request $request, $component_asset_id)
{
if (is_null($component = Component::with('assets')->find($componentId))) {
// Redirect to the component management page with error
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return redirect()->route('components.index')->with('error',
trans('admin/components/message.not_found'));
}
if (!Company::isCurrentUserHasAccess($component)) {
return ['total' => 0, 'rows' => []];
}
$this->authorize('view', $component);
$rows = array();
$all_custom_fields = CustomField::all(); // Cached for table;
foreach ($component->assets as $component_assignment) {
$rows[] = $component_assignment->present()->forDataTable($all_custom_fields);
}
$this->authorize('checkin', $component);
$componentCount = $component->assets->count();
$data = array('total' => $componentCount, 'rows' => $rows);
return $data;
$max_to_checkin = $component_assets->assigned_qty;
$validator = Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Validation passed, so let's figure out what we have to do here.
$qty_remaining_in_checkout = ($component_assets->assigned_qty - (int)$request->input('checkin_qty'));
// We have to modify the record to reflect the new qty that's
// actually checked out.
$component_assets->assigned_qty = $qty_remaining_in_checkout;
DB::table('components_assets')->where('id',
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
$log = new Actionlog();
$log->user_id = Auth::user()->id;
$log->action_type = 'checkin from';
$log->target_type = Asset::class;
$log->target_id = $component_assets->asset_id;
$log->item_id = $component_assets->component_id;
$log->item_type = Component::class;
$log->note = $request->input('note');
$log->save();
// If the checked-in qty is exactly the same as the assigned_qty,
// we can simply delete the associated components_assets record
if ($qty_remaining_in_checkout == 0) {
DB::table('components_assets')->where('id', '=', $component_asset_id)->delete();
}
return redirect()->route('components.index')->with('success',
trans('admin/components/message.checkout.success'));
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
}

View File

@@ -7,18 +7,18 @@ 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;
use View;
use Gate;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to Consumables for
@@ -54,13 +54,9 @@ class ConsumablesController extends Controller
public function create()
{
$this->authorize('create', Consumable::class);
// Show the page
return view('consumables/edit')
->with('item', new Consumable)
->with('category_list', Helper::categoryList('consumable'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
$category_type = 'consumable';
return view('consumables/edit')->with('category_type', $category_type)
->with('item', new Consumable);
}
@@ -72,24 +68,36 @@ class ConsumablesController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function store()
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Consumable::class);
$consumable = new Consumable();
$consumable->name = Input::get('name');
$consumable->category_id = Input::get('category_id');
$consumable->location_id = Input::get('location_id');
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$consumable->order_number = Input::get('order_number');
$consumable->min_amt = Input::get('min_amt');
$consumable->manufacturer_id = Input::get('manufacturer_id');
$consumable->model_number = Input::get('model_number');
$consumable->item_no = Input::get('item_no');
$consumable->purchase_date = Input::get('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
$consumable->qty = Input::get('qty');
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
$consumable->min_amt = $request->input('min_amt');
$consumable->manufacturer_id = $request->input('manufacturer_id');
$consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat($request->input('purchase_cost'));
$consumable->qty = $request->input('qty');
$consumable->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/consumables/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$consumable->image = $file_name;
}
if ($consumable->save()) {
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
}
@@ -109,17 +117,14 @@ class ConsumablesController extends Controller
*/
public function edit($consumableId = null)
{
if (is_null($item = Consumable::find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
if ($item = Consumable::find($consumableId)) {
$this->authorize($item);
$category_type = 'consumable';
return view('consumables/edit', compact('item'))->with('category_type', $category_type);
}
$this->authorize($item);
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
return view('consumables/edit', compact('item'))
->with('category_list', Helper::categoryList('consumable'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
}
@@ -132,7 +137,7 @@ class ConsumablesController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function update($consumableId = null)
public function update(ImageUploadRequest $request, $consumableId = null)
{
if (is_null($consumable = Consumable::find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
@@ -140,19 +145,32 @@ class ConsumablesController extends Controller
$this->authorize($consumable);
$consumable->name = Input::get('name');
$consumable->category_id = Input::get('category_id');
$consumable->location_id = Input::get('location_id');
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$consumable->order_number = Input::get('order_number');
$consumable->min_amt = Input::get('min_amt');
$consumable->manufacturer_id = Input::get('manufacturer_id');
$consumable->model_number = Input::get('model_number');
$consumable->item_no = Input::get('item_no');
$consumable->purchase_date = Input::get('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
$consumable->min_amt = $request->input('min_amt');
$consumable->manufacturer_id = $request->input('manufacturer_id');
$consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
$consumable->qty = Helper::ParseFloat(Input::get('qty'));
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/consumables/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$consumable->image = $file_name;
} elseif ($request->input('image_delete')=='1') {
$consumable->image = null;
}
if ($consumable->save()) {
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
}
@@ -194,7 +212,7 @@ class ConsumablesController extends Controller
if (isset($consumable->id)) {
return view('consumables/view', compact('consumable'));
}
return redirect()->route('consumables')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
}
/**
@@ -209,10 +227,10 @@ class ConsumablesController extends Controller
public function getCheckout($consumableId)
{
if (is_null($consumable = Consumable::find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
$this->authorize('checkout', $consumable);
return view('consumables/checkout', compact('consumable'))->with('users_list', Helper::usersList());
return view('consumables/checkout', compact('consumable'));
}
/**
@@ -238,7 +256,7 @@ class ConsumablesController extends Controller
// Check if the user exists
if (is_null($user = User::find($assigned_to))) {
// Redirect to the consumable management page with error
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.user_does_not_exist'));
return redirect()->route('checkout/consumable', $consumable)->with('error', trans('admin/consumables/message.checkout.user_does_not_exist'));
}
// Update the consumable data
@@ -259,14 +277,6 @@ class ConsumablesController extends Controller
$data['note'] = $logaction->note;
$data['require_acceptance'] = $consumable->requireAcceptance();
if (($consumable->requireAcceptance()=='1') || ($consumable->getEula())) {
Mail::send('emails.accept-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_consumable_delivery'));
});
}
// Redirect to the new consumable page
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));

View File

@@ -1,6 +1,7 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\CustomFieldRequest;
use View;
use App\Models\CustomFieldset;
use App\Models\CustomField;
@@ -36,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();
@@ -56,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());
}
@@ -69,39 +72,33 @@ class CustomFieldsController extends Controller
* @since [v1.8]
* @return Redirect
*/
public function store(Request $request)
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"));
}
$validator = Validator::make(Input::all(), $field->rules);
if ($validator->passes()) {
$results = $field->save();
if ($results) {
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'));
}
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
} else {
return redirect()->back()->withInput()->withErrors($validator);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
}
}
@@ -116,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'));
}
@@ -134,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 {
@@ -155,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);
}
@@ -169,16 +173,18 @@ class CustomFieldsController extends Controller
* @since [v4.0]
* @return Redirect
*/
public function update(Request $request, $id)
public function update(CustomFieldRequest $request, $id)
{
$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"));
@@ -186,13 +192,11 @@ class CustomFieldsController extends Controller
$field->format = e($request->get("format"));
}
$validator = Validator::make(Input::all(), $field->rules);
if ($field->save()) {
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
}
return redirect()->back()->withInput()->withErrors($validator);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
}

View File

@@ -37,19 +37,27 @@ class CustomFieldsetsController extends Controller
public function show($id)
{
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
$maxid = 0;
foreach ($cfset->fields() as $field) {
if ($field->pivot->order > $maxid) {
$maxid=$field->pivot->order;
}
if (isset($custom_fields_list[$field->id])) {
unset($custom_fields_list[$field->id]);
$this->authorize('view', $cfset);
if ($cfset) {
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
$maxid = 0;
foreach ($cfset->fields() as $field) {
if ($field->pivot->order > $maxid) {
$maxid=$field->pivot->order;
}
if (isset($custom_fields_list[$field->id])) {
unset($custom_fields_list[$field->id]);
}
}
return view("custom_fields.fieldsets.view")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
}
return view("custom_fields.fieldsets.view")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
}
@@ -62,6 +70,8 @@ class CustomFieldsetsController extends Controller
*/
public function create()
{
$this->authorize('create', CustomFieldset::class);
return view("custom_fields.fieldsets.edit");
}
@@ -75,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")),
@@ -133,16 +145,23 @@ class CustomFieldsetsController extends Controller
*/
public function destroy($id)
{
//
$fieldset = CustomFieldset::find($id);
$models = AssetModel::where("fieldset_id", "=", $id);
if ($models->count() == 0) {
$fieldset->delete();
return redirect()->route("fields.show")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
} else {
return redirect()->route("fields.show")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
$this->authorize('delete', $fieldset);
if ($fieldset) {
$models = AssetModel::where("fieldset_id", "=", $id);
if ($models->count() == 0) {
$fieldset->delete();
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
} else {
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
}
}
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
}
@@ -158,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

@@ -39,8 +39,8 @@ class DashboardController extends Controller
$counts['grand_total'] = $counts['asset'] + $counts['accessory'] + $counts['license'] + $counts['consumable'];
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
\Artisan::call('passport:install');
\Artisan::call('migrate', ['--force' => true]);
\Artisan::call('passport:install');
}
return view('dashboard')->with('asset_stats', $asset_stats)->with('counts', $counts);

View File

@@ -6,6 +6,8 @@ use Illuminate\Http\Request;
use App\Models\Department;
use App\Helpers\Helper;
use Auth;
use Image;
use App\Http\Requests\ImageUploadRequest;
class DepartmentsController extends Controller
{
@@ -27,13 +29,11 @@ class DepartmentsController extends Controller
public function index(Request $request)
{
$this->authorize('index', Department::class);
$company = null;
if ($request->has('company_id')) {
$company = Company::find($request->input('company_id'));
} else {
$company = null;
}
return view('departments/index')->with('company',$company);
return view('departments/index')->with('company', $company);
}
@@ -45,7 +45,7 @@ class DepartmentsController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Department::class);
$department = new Department;
@@ -53,12 +53,21 @@ class DepartmentsController extends Controller
$department->user_id = Auth::user()->id;
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/departments/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$department->image = $file_name;
}
if ($department->save()) {
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($department->getErrors());
}
/**
@@ -74,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'));
}
@@ -91,10 +102,9 @@ class DepartmentsController extends Controller
*/
public function create()
{
return view('departments/edit')->with('item', new Department)
->with('manager_list', Helper::managerList())
->with('location_list', Helper::locationsList())
->with('company_list', Helper::companyList());
$this->authorize('create', Department::class);
return view('departments/edit')->with('item', new Department);
}
@@ -112,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'));
}
@@ -135,30 +147,57 @@ class DepartmentsController extends Controller
if (is_null($item = Department::find($id))) {
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
}
return view('departments/edit', compact('item'))
->with('manager_list', Helper::managerList())
->with('location_list', Helper::locationsList())
->with('company_list', Helper::companyList());
}
public function update(Request $request, $id) {
$this->authorize('create', Department::class);
$this->authorize('update', $item);
return view('departments/edit', compact('item'));
}
public function update(ImageUploadRequest $request, $id) {
if (is_null($department = Department::find($id))) {
return redirect()->to('admin/settings/departments')->with('error', trans('admin/departments/message.does_not_exist'));
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);
$old_image = $department->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$department->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $department->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('departments_upload_path').$file_name);
} else {
$image->move(app('departments_upload_path'), $file_name);
}
$department->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('departments_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($department->save()) {
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($department->getErrors());
}
}

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'));
@@ -155,5 +167,26 @@ class DepreciationsController extends Controller
return redirect()->route('depreciations.index')->with('success', trans('admin/depreciations/message.delete.success'));
}
/**
* Returns a view that displays a form to display depreciation listing
*
* @author [A. Gianotto] [<snipe@snipe.net]
* @see DepreciationsController::postEdit()
* @param int $depreciationId
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function show($id)
{
if (is_null($depreciation = Depreciation::find($id))) {
// Redirect to the blogs management page
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

@@ -47,7 +47,7 @@ class GroupsController extends Controller
$group = new Group;
// Get all the available permissions
$permissions = config('permissions');
$groupPermissions = array();
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
$selectedPermissions = Input::old('permissions', $groupPermissions);
// Show the page
@@ -84,13 +84,18 @@ class GroupsController extends Controller
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function edit($id = null)
public function edit($id)
{
$group = Group::find($id);
$permissions = config('permissions');
$groupPermissions = $group->decodePermissions();
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
if ($group) {
$permissions = config('permissions');
$groupPermissions = $group->decodePermissions();
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
}
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found'));
}
/**
@@ -142,4 +147,24 @@ class GroupsController extends Controller
return redirect()->route('groups.index')->with('error', trans('general.feature_disabled'));
}
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the group detail page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $locationId
* @since [v4.0.11]
* @return \Illuminate\Contracts\View\View
*/
public function show($id)
{
$group = Group::find($id);
if ($group) {
return view('groups/view', compact('group'));
}
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
}
}

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;
@@ -67,10 +68,7 @@ class LicensesController extends Controller
return view('licenses/edit')
//->with('license_options',$license_options)
->with('depreciation_list', Helper::depreciationList())
->with('supplier_list', Helper::suppliersList())
->with('maintained_list', $maintained_list)
->with('company_list', Helper::companyList())
->with('manufacturer_list', Helper::manufacturerList())
->with('item', new License);
}
@@ -110,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();
@@ -144,10 +143,7 @@ class LicensesController extends Controller
return view('licenses/edit', compact('item'))
->with('depreciation_list', Helper::depreciationList())
->with('supplier_list', Helper::suppliersList())
->with('company_list', Helper::companyList())
->with('maintained_list', $maintained_list)
->with('manufacturer_list', Helper::manufacturerList());
->with('maintained_list', $maintained_list);
}
@@ -187,6 +183,8 @@ class LicensesController extends Controller
$license->termination_date = $request->input('termination_date');
$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'));
@@ -244,18 +242,19 @@ class LicensesController extends Controller
* @param int $seatId
* @return \Illuminate\Contracts\View\View
*/
public function getCheckout($seatId)
public function getCheckout($licenceId)
{
// Check if the license seat exists
if (is_null($licenseSeat = LicenseSeat::find($seatId))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
// Check that the license is valid
if ($license = License::where('id',$licenceId)->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');
}
}
$this->authorize('checkout', $licenseSeat);
return view('licenses/checkout', compact('licenseSeat'))
->with('users_list', Helper::usersList())
->with('asset_list', Helper::detailedAssetList());
$this->authorize('checkout', $license);
return view('licenses/checkout', compact('license'));
}
@@ -269,80 +268,84 @@ class LicensesController extends Controller
* @param int $seatId
* @return \Illuminate\Http\RedirectResponse
*/
public function postCheckout(Request $request, $seatId)
public function postCheckout(Request $request, $licenseId)
{
$licenseSeat = LicenseSeat::find($seatId);
$assigned_to = e($request->input('assigned_to'));
$asset_id = e($request->input('asset_id'));
$this->authorize('checkout', $licenseSeat);
// Check that the license is valid
if ($license = License::where('id',$licenseId)->first()) {
// Declare the rules for the form validation
$rules = [
'note' => 'string',
'asset_id' => 'required_without:assigned_to',
];
// Create a new validator instance from our validation rules
$validator = Validator::make(Input::all(), $rules);
// If validation fails, we'll exit the operation now.
if ($validator->fails()) {
// Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($validator);
}
$target = null;
if ($assigned_to!='') {
// Check if the user exists
if (is_null($target = User::find($assigned_to))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
}
}
if ($asset_id!='') {
if (is_null($target = Asset::find($asset_id))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
// If 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');
}
if (($target->assigned_to!='') && (($target->assigned_to!=$assigned_to)) && ($target!='')) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.owner_doesnt_match_asset'));
// 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');
}
$this->authorize('checkout', $license);
// Declare the rules for the form validation
$rules = [
'note' => 'string|nullable',
'asset_id' => 'required_without:assigned_to',
];
// Create a new validator instance from our validation rules
$validator = Validator::make(Input::all(), $rules);
// If validation fails, we'll exit the operation now.
if ($validator->fails()) {
// Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($validator);
}
$target = null;
// This item is checked out to a an asset
if (request('checkout_to_type')=='asset') {
if (is_null($target = Asset::find(request('asset_id')))) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
}
$licenseSeat->asset_id = $request->input('asset_id');
// Override asset's assigned user if available
if ($target->checkedOutToUser()) {
$licenseSeat->assigned_to = $target->assigned_to;
}
} else {
// Fetch the target and set the license user
if (is_null($target = User::find(request('assigned_to')))) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
}
$licenseSeat->assigned_to = request('assigned_to');
}
$licenseSeat->user_id = Auth::user()->id;
if ($licenseSeat->save()) {
$licenseSeat->logCheckout($request->input('note'), $target);
return redirect()->route("licenses.index")->with('success', trans('admin/licenses/message.checkout.success'));
}
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
// Check if the asset exists
if (is_null($licenseSeat)) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
if ($request->input('asset_id') == '') {
$licenseSeat->asset_id = null;
} else {
$licenseSeat->asset_id = $request->input('asset_id');
}
// Update the asset data
if ($request->input('assigned_to') == '') {
$licenseSeat->assigned_to = null;
} else {
$licenseSeat->assigned_to = $request->input('assigned_to');
}
// Was the asset updated?
if ($licenseSeat->save()) {
$licenseSeat->logCheckout($request->input('note'), $target);
$data['license_id'] =$licenseSeat->license_id;
$data['note'] = $request->input('note');
// Redirect to the new asset page
return redirect()->route("licenses.index")->with('success', trans('admin/licenses/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->to("admin/licenses/{$asset_id}/checkout")->with('error', trans('admin/licenses/message.create.error'))->with('license', new License);
return redirect()->route("licenses.index")->with('error', trans('admin/licenses/message.checkout.error'));
}
@@ -358,12 +361,19 @@ class LicensesController extends Controller
public function getCheckin($seatId = null, $backTo = null)
{
// Check if the asset exists
if (is_null($licenseseat = LicenseSeat::find($seatId))) {
if (is_null($licenseSeat = LicenseSeat::find($seatId))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
$this->authorize('checkin', $licenseseat);
return view('licenses/checkin', compact('licenseseat'))->with('backto', $backTo);
if (is_null($license = License::find($licenseSeat->license_id))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
$this->authorize('checkout', $license);
return view('licenses/checkin', compact('licenseSeat'))->with('backto', $backTo);
}
@@ -386,8 +396,7 @@ class LicensesController extends Controller
}
$license = License::find($licenseSeat->license_id);
$this->authorize('checkin', $licenseSeat);
$this->authorize('checkout', $license);
if (!$license->reassignable) {
// Not allowed to checkin
@@ -413,6 +422,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;
@@ -440,15 +450,16 @@ class LicensesController extends Controller
*/
public function show($licenseId = null)
{
$license = License::find($licenseId);
if (isset($license->id)) {
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
$license = License::with('assignedusers', 'licenseSeats.user', 'licenseSeats.asset')->find($licenseId);
if ($license) {
$this->authorize('view', $license);
return view('licenses/view', compact('license'));
}
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
return redirect()->route('licenses.index')->with('error', $error);
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
}
public function getClone($licenseId = null)
{
@@ -471,11 +482,8 @@ class LicensesController extends Controller
// Show the page
return view('licenses/edit')
->with('depreciation_list', Helper::depreciationList())
->with('supplier_list', Helper::suppliersList())
->with('item', $license)
->with('maintained_list', $maintained_list)
->with('company_list', Helper::companyList())
->with('manufacturer_list', Helper::manufacturerList());
->with('maintained_list', $maintained_list);
}
@@ -488,7 +496,7 @@ class LicensesController extends Controller
* @param int $licenseId
* @return \Illuminate\Http\RedirectResponse
*/
public function postUpload($licenseId = null)
public function postUpload(AssetFileRequest $request, $licenseId = null)
{
$license = License::find($licenseId);
// the license is valid
@@ -497,33 +505,26 @@ 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
$license->logUpload($filename, e($request->input('notes')));
}
// This being called from a modal seems to confuse redirect()->back()
// It thinks we should go to the dashboard. As this is only used
// from the modal at present, hardcode the redirect. Longterm
// maybe we evaluate something else.
if ($upload_success) {
return redirect()->back()->with('success', trans('admin/licenses/message.upload.success'));
return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/licenses/message.upload.error'));
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.error'));
}
return redirect()->back()->with('error', trans('admin/licenses/message.upload.nofiles'));
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
}
// Prepare the error message
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
@@ -574,7 +575,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);
@@ -584,63 +585,35 @@ 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);
}
// Prepare the error message
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
// Redirect to the licence management page
return redirect()->route('licenses.index')->with('error', $error);
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
}
/**
* Generates a JSON response to populate the licence index datatables.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see LicensesController::getIndex() method that provides the view
* @since [v1.0]
* @return String JSON
*/
public function getDatatable(Request $request)
{
$this->authorize('view', License::class);
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer'));
if (Input::has('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','manufacturer','company'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
switch ($sort) {
case 'manufacturer':
$licenses = $licenses->OrderManufacturer($order);
break;
case 'company':
$licenses = $licenses->OrderCompany($order);
break;
default:
$licenses = $licenses->orderBy($sort, $order);
break;
}
$licenseCount = $licenses->count();
$licenses = $licenses->skip($offset)->take($limit)->get();
$rows = array();
foreach ($licenses as $license) {
$rows[] = $license->present()->forDataTable();
}
$data = array('total' => $licenseCount, 'rows' => $rows);
return $data;
}
/**
* Generates the next free seat ID for checkout.

View File

@@ -16,6 +16,8 @@ use Validator;
use View;
use Auth;
use Symfony\Component\HttpFoundation\JsonResponse;
use Image;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to Locations for
@@ -38,6 +40,7 @@ class LocationsController extends Controller
public function index()
{
// Grab all the locations
$this->authorize('view', Location::class);
$locations = Location::orderBy('created_at', 'DESC')->with('parent', 'assets', 'assignedassets')->get();
// Show the page
@@ -55,6 +58,7 @@ class LocationsController extends Controller
*/
public function create()
{
$this->authorize('create', Location::class);
$locations = Location::orderBy('name', 'ASC')->get();
$location_options_array = Location::getLocationHierarchy($locations);
@@ -63,8 +67,7 @@ class LocationsController extends Controller
return view('locations/edit')
->with('location_options', $location_options)
->with('item', new Location)
->with('manager_list', Helper::managerList());
->with('item', new Location);
}
@@ -77,62 +80,40 @@ class LocationsController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function store()
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Location::class);
$location = new Location();
$location->name = Input::get('name');
$location->parent_id = Input::get('parent_id', null);
$location->currency = Input::get('currency', '$');
$location->address = Input::get('address');
$location->address2 = Input::get('address2');
$location->city = Input::get('city');
$location->state = Input::get('state');
$location->country = Input::get('country');
$location->zip = Input::get('zip');
$location->manager_id = Input::get('manager_id');
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
$location->currency = $request->input('currency', '$');
$location->address = $request->input('address');
$location->address2 = $request->input('address2');
$location->city = $request->input('city');
$location->state = $request->input('state');
$location->country = $request->input('country');
$location->zip = $request->input('zip');
$location->ldap_ou = $request->input('ldap_ou');
$location->manager_id = $request->input('manager_id');
$location->user_id = Auth::id();
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/locations/'.$file_name);
Image::make($image->getRealPath())->resize(600, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$location->image = $file_name;
}
if ($location->save()) {
return redirect()->route("locations.index")->with('success', trans('admin/locations/message.create.success'));
}
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()
{
$new['currency']=Setting::first()->default_currency;
// create a new location instance
$location = new Location();
// Save the location data
$location->name = Input::get('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 = Input::get('city');
$location->state = '';//e(Input::get('state'));
$location->country = Input::get('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.
@@ -145,9 +126,10 @@ class LocationsController extends Controller
*/
public function edit($locationId = null)
{
$this->authorize('update', Location::class);
// Check if the location exists
if (is_null($item = Location::find($locationId))) {
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.does_not_exist'));
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
// Show the page
@@ -156,10 +138,8 @@ class LocationsController extends Controller
$location_options = Location::flattenLocationsArray($location_options_array);
$location_options = array('' => 'Top Level') + $location_options;
return view('locations/edit', compact('item'))
->with('location_options', $location_options)
->with('manager_list', Helper::managerList());
->with('location_options', $location_options);
}
@@ -172,32 +152,62 @@ class LocationsController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function update($locationId = null)
public function update(ImageUploadRequest $request, $locationId = null)
{
$this->authorize('update', Location::class);
// Check if the location exists
if (is_null($location = Location::find($locationId))) {
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.does_not_exist'));
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
// Update the location data
$location->name = Input::get('name');
$location->parent_id = Input::get('parent_id', null);
$location->currency = Input::get('currency', '$');
$location->address = Input::get('address');
$location->address2 = Input::get('address2');
$location->city = Input::get('city');
$location->state = Input::get('state');
$location->country = Input::get('country');
$location->zip = Input::get('zip');
$location->ldap_ou = Input::get('ldap_ou');
$location->manager_id = Input::get('manager_id');
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
$location->currency = $request->input('currency', '$');
$location->address = $request->input('address');
$location->address2 = $request->input('address2');
$location->city = $request->input('city');
$location->state = $request->input('state');
$location->country = $request->input('country');
$location->zip = $request->input('zip');
$location->ldap_ou = $request->input('ldap_ou');
$location->manager_id = $request->input('manager_id');
$old_image = $location->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$location->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $location->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(600, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('locations_upload_path').$file_name);
} else {
$image->move(app('locations_upload_path'), $file_name);
}
$location->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('locations_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
// Was the location updated?
if ($location->save()) {
// Redirect to the saved location page
return redirect()->route("locations.index")->with('success', trans('admin/locations/message.update.success'));
}
// Redirect to the location management page
return redirect()->back()->withInput()->withInput()->withErrors($location->getErrors());
}
@@ -211,21 +221,23 @@ class LocationsController extends Controller
*/
public function destroy($locationId)
{
// Check if the location exists
$this->authorize('delete', Location::class);
if (is_null($location = Location::find($locationId))) {
// Redirect to the blogs management page
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found'));
}
if ($location->users->count() > 0) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_users'));
} elseif ($location->childLocations->count() > 0) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_child_loc'));
} elseif ($location->assets->count() > 0) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
} elseif ($location->assignedassets->count() > 0) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
} else {
$location->delete();
return redirect()->to(route('locations.index'))->with('success', trans('admin/locations/message.delete.success'));
@@ -249,11 +261,8 @@ class LocationsController extends Controller
if (isset($location->id)) {
return view('locations/view', compact('location'));
}
// Prepare the error message
$error = trans('admin/locations/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('locations.index')->with('error', $error);
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\CustomField;
use App\Models\Manufacturer;
use Auth;
@@ -13,6 +14,7 @@ use Redirect;
use Str;
use View;
use Illuminate\Http\Request;
use Image;
/**
* This controller handles all actions related to Manufacturers for
@@ -33,6 +35,7 @@ class ManufacturersController extends Controller
*/
public function index()
{
$this->authorize('index', Manufacturer::class);
return view('manufacturers/index', compact('manufacturers'));
}
@@ -47,6 +50,7 @@ class ManufacturersController extends Controller
*/
public function create()
{
$this->authorize('create', Manufacturer::class);
return view('manufacturers/edit')->with('item', new Manufacturer);
}
@@ -60,9 +64,10 @@ class ManufacturersController extends Controller
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Manufacturer::class);
$manufacturer = new Manufacturer;
$manufacturer->name = $request->input('name');
$manufacturer->user_id = Auth::user()->id;
@@ -72,6 +77,18 @@ class ManufacturersController extends Controller
$manufacturer->support_email = $request->input('support_email');
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_slug($image->getClientOriginalName()).".".$image->getClientOriginalExtension();
$path = public_path('uploads/manufacturers/'.$file_name);
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$manufacturer->image = $file_name;
}
if ($manufacturer->save()) {
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.create.success'));
@@ -90,6 +107,7 @@ class ManufacturersController extends Controller
*/
public function edit($id = null)
{
$this->authorize('edit', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($item = Manufacturer::find($id))) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
@@ -109,8 +127,9 @@ class ManufacturersController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @since [v1.0]
*/
public function update(Request $request, $manufacturerId = null)
public function update(ImageUploadRequest $request, $manufacturerId = null)
{
$this->authorize('edit', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
// Redirect to the manufacturer page
@@ -124,6 +143,38 @@ class ManufacturersController extends Controller
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
$old_image = $manufacturer->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$manufacturer->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $manufacturer->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('manufacturers_upload_path').$file_name);
} else {
$image->move(app('manufacturers_upload_path'), $file_name);
}
$manufacturer->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('manufacturers_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($manufacturer->save()) {
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.update.success'));
}
@@ -140,6 +191,7 @@ class ManufacturersController extends Controller
*/
public function destroy($manufacturerId)
{
$this->authorize('delete', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
// Redirect to the manufacturers page
@@ -150,6 +202,16 @@ class ManufacturersController extends Controller
// Redirect to the asset management page
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.assoc_users'));
}
if ($manufacturer->image) {
try {
unlink(public_path().'/uploads/manufacturers/'.$manufacturer->image);
} catch (\Exception $e) {
\Log::error($e);
}
}
// Delete the manufacturer
$manufacturer->delete();
// Redirect to the manufacturers management page
@@ -158,170 +220,55 @@ class ManufacturersController extends Controller
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the manufacturers detail listing, which is generated in getDatatable.
* the content for the manufacturers detail listing, which is generated via API.
* This data contains a listing of all assets that belong to that manufacturer.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ManufacturersController::getDataView()
* @param int $manufacturerId
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function show($manufacturerId = null)
{
$this->authorize('view', Manufacturer::class);
$manufacturer = Manufacturer::find($manufacturerId);
if (isset($manufacturer->id)) {
return view('manufacturers/view', compact('manufacturer'));
}
// Prepare the error message
$error = trans('admin/manufacturers/message.does_not_exist', compact('id'));
$error = trans('admin/manufacturers/message.does_not_exist');
// Redirect to the user management page
return redirect()->route('manufacturers')->with('error', $error);
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'));
}
/**
* Generates the JSON used to display the manufacturer detail.
* This JSON returns data on all of the assets with the specified
* manufacturer ID number.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see ManufacturersController::getView()
* @param int $manufacturerId
* @param string $itemType
* @param Request $request
* @return String JSON* @since [v1.0]
*/
public function getDataView($manufacturerId, $itemType = null, Request $request)
{
$manufacturer = Manufacturer::find($manufacturerId);
switch ($itemType) {
case "assets":
return $this->getDataAssetsView($manufacturer, $request);
case "licenses":
return $this->getDataLicensesView($manufacturer, $request);
case "accessories":
return $this->getDataAccessoriesView($manufacturer, $request);
case "consumables":
return $this->getDataConsumablesView($manufacturer, $request);
}
return "We shouldn't be here";
}
protected function getDataAssetsView(Manufacturer $manufacturer, Request $request)
{
$manufacturer = $manufacturer->load('assets.model', 'assets.assignedTo', 'assets.assetstatus', 'assets.company');
$manufacturer_assets = $manufacturer->assets();
if ($request->has('search')) {
$manufacturer_assets = $manufacturer_assets->TextSearch(e($request->input('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$allowed_columns = ['id','name','serial','asset_tag'];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$count = $manufacturer_assets->count();
$manufacturer_assets = $manufacturer_assets->skip($offset)->take($limit)->get();
$rows = array();
$all_custom_fields = CustomField::all(); // cached;
foreach ($manufacturer_assets as $asset) {
$rows[] = $asset->present()->forDataTable($all_custom_fields);
}
$data = array('total' => $count, 'rows' => $rows);
return $data;
}
protected function getDataLicensesView(Manufacturer $manufacturer, Request $request)
{
$manufacturer = $manufacturer->load('licenses.company', 'licenses.manufacturer', 'licenses.licenseSeatsRelation');
$licenses = $manufacturer->licenses;
if ($request->has('search')) {
$licenses = $licenses->TextSearch($request->input('search'));
}
$licenseCount = $licenses->count();
$rows = array();
foreach ($licenses as $license) {
$rows[] = $license->present()->forDataTable();
}
$data = array('total' => $licenseCount, 'rows' => $rows);
return $data;
}
public function getDataAccessoriesView(Manufacturer $manufacturer, Request $request)
{
$manufacturer = $manufacturer->load(
'accessories.location',
'accessories.company',
'accessories.category',
'accessories.manufacturer',
'accessories.users'
);
$accessories = $manufacturer->accessories();
if ($request->has('search')) {
$accessories = $accessories->TextSearch(e($request->input('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$accessCount = $accessories->count();
$accessories = $accessories->skip($offset)->take($limit)->get();
$rows = array();
foreach ($accessories as $accessory) {
$rows[] = $accessory->present()->forDataTable();
}
$data = array('total'=>$accessCount, 'rows'=>$rows);
return $data;
}
public function getDataConsumablesView($manufacturer, Request $request)
{
$manufacturer = $manufacturer->load(
'consumables.location',
'consumables.company',
'consumables.category',
'consumables.manufacturer',
'consumables.users'
);
$consumables = $manufacturer->consumables();
if ($request->has('search')) {
$consumables = $consumables->TextSearch(e($request->input('search')));
}
$offset = request('offset', 0);
$limit = request('limit', 50);
$consumCount = $consumables->count();
$consumables = $consumables->skip($offset)->take($limit)->get();
$rows = array();
foreach ($consumables as $consumable) {
$rows[] = $consumable->present()->forDataTable();
}
$data = array('total' => $consumCount, 'rows' => $rows);
return $data;
}
}

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() {
@@ -29,4 +27,13 @@ class ModalController extends Controller
function user() {
return view('modals.user');
}
function category() {
return view('modals.category');
}
function manufacturer() {
return view('modals.manufacturer');
}
}

View File

@@ -11,6 +11,7 @@ use App\Models\Setting;
use Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Http\Requests\ImageUploadRequest;
/**
* This controller handles all actions related to User Profiles for
@@ -30,8 +31,7 @@ class ProfileController extends Controller
public function getIndex()
{
$user = Auth::user();
$location_list = Helper::locationsList();
return view('account/profile', compact('user'))->with('location_list', $location_list);
return view('account/profile', compact('user'));
}
/**
@@ -41,7 +41,7 @@ class ProfileController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\RedirectResponse
*/
public function postIndex()
public function postIndex(ImageUploadRequest $request)
{
$user = Auth::user();
@@ -109,12 +109,12 @@ class ProfileController extends Controller
{
if (config('app.lock_passwords')) {
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/table.lock_passwords'));
return redirect()->route('account.password.index')->with('error', trans('admin/users/table.lock_passwords'));
}
$user = Auth::user();
if ($user->ldap_import=='1') {
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/message.error.password_ldap'));
return redirect()->route('account.password.index')->with('error', trans('admin/users/message.error.password_ldap'));
}
$rules = array(
@@ -143,5 +143,25 @@ class ProfileController extends Controller
}
/**
* Save the menu state of open/closed when the user clicks on the hamburger
* menu.
*
* This URL is triggered via jquery in
* resources/views/layouts/default.blade.php
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return View
*/
public function getMenuState(Request $request) {
if ($request->input('state')=='open') {
$request->session()->put('menu_state', 'open');
} else {
$request->session()->put('menu_state', 'closed');
}
}
}

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;
@@ -15,6 +16,7 @@ use Illuminate\Support\Facades\View;
use Input;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Illuminate\Http\Request;
/**
* This controller handles all actions related to Reports for
@@ -24,6 +26,14 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
*/
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.
@@ -80,101 +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()
{
\Debugbar::disable();
$customfields = CustomField::get();
$response = new StreamedResponse(function () use ($customfields) {
// Open output stream
$handle = fopen('php://output', 'w');
Asset::with('assignedTo', 'assetLoc','defaultLoc','assignedTo','model','supplier','assetstatus','model.manufacturer')->orderBy('created_at', 'DESC')->chunk(500, function($assets) use($handle, $customfields) {
$headers=[
trans('general.company'),
trans('admin/hardware/table.asset_tag'),
trans('admin/hardware/form.manufacturer'),
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('admin/hardware/form.supplier'),
trans('admin/hardware/table.checkoutto'),
trans('admin/hardware/table.checkout_date'),
trans('admin/hardware/table.location'),
trans('general.notes'),
];
foreach ($customfields as $field) {
$headers[]=$field->name;
}
fputcsv($handle, $headers);
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) ? $asset->model->name : '',
($asset->model->model_number) ? $asset->model->model_number : '',
($asset->name) ? $asset->name : '',
($asset->serial) ? $asset->serial : '',
($asset->assetstatus) ? e($asset->assetstatus->name) : '',
($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->assignedTo) ? e($asset->assignedTo->present()->name()) : '',
($asset->last_checkout!='') ? e($asset->last_checkout) : '',
e($asset->assetLoc->present()->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="assets-'.date('Y-m-d-his').'.csv"',
]);
return $response;
}
/**
* Show depreciation report for assets.
*
@@ -185,11 +100,12 @@ class ReportsController extends Controller
public function getDeprecationReport()
{
$depreciations = Depreciation::get();
// Grab all the assets
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'assetloc', 'assetlog', 'company', 'model.category', 'model.depreciation')
$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);
}
/**
@@ -241,7 +157,7 @@ class ReportsController extends Controller
$row[] = ''; // Empty string if unassigned
}
if (( $asset->assigned_to > 0 ) && ( $location = $asset->assetLoc )) {
if (( $asset->assigned_to > 0 ) && ( $location = $asset->location )) {
if ($location->city) {
$row[] = e($location->city) . ', ' . e($location->state);
} elseif ($location->name) {
@@ -253,8 +169,8 @@ class ReportsController extends Controller
$row[] = ''; // Empty string if location is not set
}
if ($asset->assetloc) {
$currency = e($asset->assetloc->currency);
if ($asset->location) {
$currency = e($asset->location->currency);
} else {
$currency = e(Setting::first()->default_currency);
}
@@ -388,251 +304,411 @@ class ReportsController extends Controller
* @since [v1.0]
* @return \Illuminate\Http\Response
*/
public function postCustom()
public function postCustom(Request $request)
{
$assets = Asset::orderBy('created_at', 'DESC')->with('company', 'assignedTo', 'assetloc', 'defaultLoc', 'model', 'supplier', 'assetstatus', 'model.manufacturer')->get();
\Debugbar::disable();
$customfields = CustomField::get();
$response = new StreamedResponse(function () use ($customfields, $request) {
$rows = [ ];
$header = [ ];
if (e(Input::get('company')) == '1') {
$header[] = 'Company Name';
}
if (e(Input::get('asset_name')) == '1') {
$header[] = 'Asset Name';
}
if (e(Input::get('asset_tag')) == '1') {
$header[] = 'Asset Tag';
}
if (e(Input::get('manufacturer')) == '1') {
$header[] = 'Manufacturer';
}
if (e(Input::get('model')) == '1') {
$header[] = 'Model';
$header[] = 'Model Number';
}
if (e(Input::get('category')) == '1') {
$header[] = 'Category';
}
if (e(Input::get('serial')) == '1') {
$header[] = 'Serial';
}
if (e(Input::get('purchase_date')) == '1') {
$header[] = 'Purchase Date';
}
if (( e(Input::get('purchase_cost')) == '1' ) && ( e(Input::get('depreciation')) != '1' )) {
$header[] = 'Purchase Cost';
}
if (e(Input::get('eol')) == '1') {
$header[] = 'EOL';
}
if (e(Input::get('order')) == '1') {
$header[] = 'Order Number';
}
if (e(Input::get('supplier')) == '1') {
$header[] = 'Supplier';
}
if (e(Input::get('location')) == '1') {
$header[] = 'Location';
}
if (e(Input::get('assigned_to')) == '1') {
$header[] = 'Assigned To';
}
if (e(Input::get('username')) == '1') {
$header[] = 'Username';
}
if (e(Input::get('employee_num')) == '1') {
$header[] = 'Employee No.';
}
if (e(Input::get('status')) == '1') {
$header[] = 'Status';
}
if (e(Input::get('warranty')) == '1') {
$header[] = 'Warranty';
$header[] = 'Warranty Expires';
}
if (e(Input::get('depreciation')) == '1') {
$header[] = 'Purchase Cost';
$header[] = 'Value';
$header[] = 'Diff';
}
if (e(Input::get('expected_checkin')) == '1') {
$header[] = trans('admin/hardware/form.expected_checkin');
}
if (e(Input::get('notes')) == '1') {
$header[] = 'Notes';
}
foreach ($customfields as $customfield) {
if (e(Input::get($customfield->db_column_name())) == '1') {
$header[] = $customfield->name;
}
}
$header = array_map('trim', $header);
$rows[] = implode($header, ',');
foreach ($assets as $asset) {
$row = [ ];
if (e(Input::get('company')) == '1') {
$row[] = is_null($asset->company) ? '' : '"'.$asset->company->name.'"';
// Open output stream
$handle = fopen('php://output', 'w');
if ($request->has('use_bom')) {
fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
}
if (e(Input::get('asset_name')) == '1') {
$row[] = '"' .e($asset->name) . '"';
}
if (e(Input::get('asset_tag')) == '1') {
$row[] = e($asset->asset_tag);
}
if (e(Input::get('manufacturer')) == '1') {
if ($asset->model->manufacturer) {
$row[] = '"' .e($asset->model->manufacturer->name) . '"';
} else {
$row[] = '';
}
}
if (e(Input::get('model')) == '1') {
$row[] = '"' . e($asset->model->name) . '"';
$row[] = '"' . e($asset->model->model_number) . '"';
}
if (e(Input::get('category')) == '1') {
$row[] = '"' .e($asset->model->category->name) . '"';
$header = [];
if ($request->has('company')) {
$header[] = trans('general.company');
}
if (e(Input::get('serial')) == '1') {
$row[] = e($asset->serial);
}
if (e(Input::get('purchase_date')) == '1') {
$row[] = e($asset->purchase_date);
}
if (e(Input::get('purchase_cost')) == '1' && ( e(Input::get('depreciation')) != '1' )) {
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
}
if (e(Input::get('eol')) == '1') {
$row[] = '"' .($asset->present()->eol_date()) ? $asset->present()->eol_date() : ''. '"';
}
if (e(Input::get('order')) == '1') {
if ($asset->order_number) {
$row[] = e($asset->order_number);
} else {
$row[] = '';
}
}
if (e(Input::get('supplier')) == '1') {
if ($asset->supplier) {
$row[] = '"' .e($asset->supplier->name) . '"';
} else {
$row[] = '';
}
if ($request->has('asset_name')) {
$header[] = trans('admin/hardware/form.name');
}
if (e(Input::get('location')) == '1') {
if($asset->assetLoc) {
$show_loc = $asset->assetLoc->present()->name();
} else {
$show_loc = 'Default location '.$asset->rtd_location_id.' is invalid';
}
$row[] = $show_loc;
if ($request->has('asset_tag')) {
$header[] = trans('admin/hardware/table.asset_tag');
}
if ($request->has('model')) {
$header[] = trans('admin/hardware/form.model');
$header[] = trans('general.model_no');
}
if ($request->has('category')) {
$header[] = trans('general.category');
}
if ($request->has('manufacturer')) {
$header[] = trans('admin/hardware/form.manufacturer');
}
if ($request->has('serial')) {
$header[] = trans('admin/hardware/table.serial');
}
if ($request->has('purchase_date')) {
$header[] = trans('admin/hardware/table.purchase_date');
}
if (($request->has('purchase_cost')) || ($request->has('depreciation'))) {
$header[] = trans('admin/hardware/table.purchase_cost');
}
if ($request->has('eol')) {
$header[] = trans('admin/hardware/table.eol');
}
if ($request->has('order')) {
$header[] = trans('admin/hardware/form.order');
}
if ($request->has('supplier')) {
$header[] = trans('general.supplier');
}
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 (e(Input::get('assigned_to')) == '1') {
if ($asset->assignedTo) {
$row[] = '"' .e($asset->assignedTo->present()->name()). '"';
} else {
$row[] = ''; // Empty string if unassigned
}
if ($request->has('assigned_to')) {
$header[] = trans('admin/hardware/table.checkoutto');
$header[] = trans('general.type');
}
if (e(Input::get('username')) == '1') {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
$row[] = '"' .e($asset->assignedTo->username). '"';
} else {
$row[] = ''; // Empty string if unassigned
}
if ($request->has('username')) {
$header[] = 'Username';
}
if (e(Input::get('employee_num')) == '1') {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
$row[] = '"' .e($asset->assignedTo->employee_num). '"';
} else {
$row[] = ''; // Empty string if unassigned
}
if ($request->has('employee_num')) {
$header[] = 'Employee No.';
}
if (e(Input::get('status')) == '1') {
if (( $asset->status_id == '0' ) && ( $asset->assigned_to == '0' )) {
$row[] = trans('general.ready_to_deploy');
} elseif (( $asset->status_id == '' ) && ( $asset->assigned_to == '0' )) {
$row[] = trans('general.pending');
} elseif ($asset->assetstatus) {
$row[] = '"' .e($asset->assetstatus->name). '"';
} else {
$row[] = '';
}
}
if (e(Input::get('warranty')) == '1') {
if ($asset->warranty_months) {
$row[] = $asset->warranty_months;
$row[] = $asset->present()->warrantee_expires();
} else {
$row[] = '';
$row[] = '';
}
}
if (e(Input::get('depreciation')) == '1') {
$depreciation = $asset->getDepreciatedValue();
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
$row[] = '"' . Helper::formatCurrencyOutput($depreciation) . '"';
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
}
if (e(Input::get('expected_checkin')) == '1') {
if ($asset->expected_checkin) {
$row[] = '"' .e($asset->expected_checkin). '"';
} else {
$row[] = ''; // Empty string if blankd
}
if ($request->has('manager')) {
$header[] = trans('admin/users/table.manager');
}
if (e(Input::get('notes')) == '1') {
if ($asset->notes) {
$row[] = '"' .$asset->notes. '"';
} else {
$row[] = ''; // Empty string if unassigned
}
if ($request->has('department')) {
$header[] = trans('general.department');
}
if ($request->has('status')) {
$header[] = trans('general.status');
}
if ($request->has('warranty')) {
$header[] = 'Warranty';
$header[] = 'Warranty Expires';
}
if ($request->has('depreciation')) {
$header[] = 'Value';
$header[] = 'Diff';
}
if ($request->has('checkout_date')) {
$header[] = trans('admin/hardware/table.checkout_date');
}
if ($request->has('expected_checkin')) {
$header[] = trans('admin/hardware/form.expected_checkin');
}
if ($request->has('created_at')) {
$header[] = trans('general.created_at');
}
if ($request->has('updated_at')) {
$header[] = trans('general.updated_at');
}
if ($request->has('last_audit_date')) {
$header[] = trans('general.last_audit');
}
if ($request->has('next_audit_date')) {
$header[] = trans('general.next_audit_date');
}
if ($request->has('notes')) {
$header[] = trans('general.notes');
}
foreach ($customfields as $customfield) {
$column_name = $customfield->db_column_name();
if (e(Input::get($customfield->db_column_name())) == '1') {
$row[] = str_replace(",", "\,", $asset->$column_name);
$header[] = $customfield->name;
}
}
$rows[] = implode($row, ',');
}
fputcsv($handle, $header);
$assets = \App\Models\Company::scopeCompanyables(Asset::select('assets.*'))->with(
'location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer','supplier');
if ($request->has('by_location_id')) {
$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'));
}
if ($request->has('by_company_id')) {
$assets->where('assets.company_id', $request->input('by_company_id'));
}
if ($request->has('by_model_id')) {
$assets->where('assets.model_id', $request->input('by_model_id'));
}
if ($request->has('by_category_id')) {
$assets->InCategory($request->input('by_category_id'));
}
if ($request->has('by_manufacturer_id')) {
$assets->ByManufacturer($request->input('by_manufacturer_id'));
}
if ($request->has('by_order_number')) {
$assets->where('assets.order_number', $request->input('by_order_number'));
}
if ($request->has('by_status_id')) {
$assets->where('assets.status_id', $request->input('by_status_id'));
}
if (($request->has('purchase_start')) && ($request->has('purchase_end'))) {
$assets->whereBetween('assets.purchase_date', [$request->input('purchase_start'), $request->input('purchase_end')]);
}
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) {
foreach ($assets as $asset) {
$row = [];
if ($request->has('company')) {
$row[] = ($asset->company) ? $asset->company->name : '';
}
if ($request->has('asset_name')) {
$row[] = ($asset->name) ? $asset->name : '';
}
if ($request->has('asset_tag')) {
$row[] = ($asset->asset_tag) ? $asset->asset_tag : '';
}
if ($request->has('model')) {
$row[] = ($asset->model) ? $asset->model->name : '';
$row[] = ($asset->model) ? $asset->model->model_number : '';
}
if ($request->has('category')) {
$row[] = (($asset->model) && ($asset->model->category)) ? $asset->model->category->name : '';
}
if ($request->has('manufacturer')) {
$row[] = ($asset->model && $asset->model->manufacturer) ? $asset->model->manufacturer->name : '';
}
if ($request->has('serial')) {
$row[] = ($asset->serial) ? $asset->serial : '';
}
if ($request->has('purchase_date')) {
$row[] = ($asset->purchase_date) ? $asset->purchase_date : '';
}
if ($request->has('purchase_cost')) {
$row[] = ($asset->purchase_cost) ? Helper::formatCurrencyOutput($asset->purchase_cost) : '';
}
if ($request->has('eol')) {
$row[] = ($asset->purchase_date!='') ? $asset->present()->eol_date() : '';
}
if ($request->has('order')) {
$row[] = ($asset->order_number) ? $asset->order_number : '';
}
if ($request->has('supplier')) {
$row[] = ($asset->supplier) ? $asset->supplier->name : '';
}
if ($request->has('location')) {
$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());
}
if ($request->has('username')) {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
$row[] = ($asset->assignedto) ? $asset->assignedto->username : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
if ($request->has('employee_num')) {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
$row[] = ($asset->assignedto) ? $asset->assignedto->employee_num : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
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.')' : '';
}
if ($request->has('warranty')) {
$row[] = ($asset->warranty_months) ? $asset->warranty_months : '';
$row[] = $asset->present()->warrantee_expires();
}
if ($request->has('depreciation')) {
$depreciation = $asset->getDepreciatedValue();
$diff = ($asset->purchase_cost - $depreciation);
$row[] = Helper::formatCurrencyOutput($depreciation);
$row[] = Helper::formatCurrencyOutput($diff);
}
if ($request->has('checkout_date')) {
$row[] = ($asset->last_checkout) ? $asset->last_checkout : '';
}
if ($request->has('expected_checkin')) {
$row[] = ($asset->expected_checkin) ? $asset->expected_checkin : '';
}
if ($request->has('created_at')) {
$row[] = ($asset->created_at) ? $asset->created_at : '';
}
if ($request->has('updated_at')) {
$row[] = ($asset->updated_at) ? $asset->updated_at : '';
}
if ($request->has('last_audit_date')) {
$row[] = ($asset->last_audit_date) ? $asset->last_audit_date : '';
}
if ($request->has('next_audit_date')) {
$row[] = ($asset->next_audit_date) ? $asset->next_audit_date : '';
}
if ($request->has('notes')) {
$row[] = ($asset->notes) ? $asset->notes : '';
}
foreach ($customfields as $customfield) {
$column_name = $customfield->db_column_name();
if ($request->has($customfield->db_column_name())) {
$row[] = $asset->$column_name;
}
}
fputcsv($handle, $row);
}
});
// Close the output stream
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition'
=> 'attachment; filename="custom-assets-report-'.date('Y-m-d-his').'.csv"',
]);
return $response;
// spit out a csv
if (array_filter($rows)) {
$csv = implode($rows, "\n");
$response = Response::make($csv, 200);
$response->header('Content-Type', 'text/csv');
$response->header('Content-disposition', 'attachment;filename='.date('Y-m-d-His').'-custom-asset-report.csv');
return $response;
} else {
return redirect()->to("reports/custom")
->with('error', trans('admin/reports/message.error'));
}
}

View File

@@ -21,6 +21,8 @@ use App\Models\User;
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
@@ -56,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'];
@@ -66,7 +69,7 @@ class SettingsController extends Controller
$start_settings['url_config'] = url('/');
$start_settings['real_url'] = $pageURL;
// Curl the .env file to make sure it's not accessible via a browser
$ch = curl_init($protocol . $host.'/.env');
curl_setopt($ch, CURLOPT_HEADER, true); // we want headers
@@ -136,28 +139,6 @@ class SettingsController extends Controller
->with('section', 'Pre-Flight Check');
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.from.address'), config('mail.from.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
return 'success';
} catch (Exception $e) {
return 'error';
}
}
/**
* Save the first admin user from Setup.
@@ -171,28 +152,31 @@ class SettingsController extends Controller
$user = new User;
$user->first_name = $data['first_name']= e(Input::get('first_name'));
$user->last_name = e(Input::get('last_name'));
$user->email = $data['email'] = e(Input::get('email'));
$user->first_name = $data['first_name']= $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->email = $data['email'] = $request->input('email');
$user->activated = 1;
$permissions = array('superuser' => 1);
$user->permissions = json_encode($permissions);
$user->username = $data['username'] = e(Input::get('username'));
$user->password = bcrypt(Input::get('password'));
$data['password'] = Input::get('password');
$user->username = $data['username'] = $request->input('username');
$user->password = bcrypt($request->input('password'));
$data['password'] = $request->input('password');
$settings = new Setting;
$settings->site_name = e(Input::get('site_name'));
$settings->alert_email = e(Input::get('email'));
$settings->full_multiple_companies_support = $request->input('full_multiple_companies_support', 0);
$settings->site_name = $request->input('site_name');
$settings->alert_email = $request->input('email');
$settings->alerts_enabled = 1;
$settings->pwd_secure_min = 10;
$settings->brand = 1;
$settings->locale = 'en';
$settings->default_currency = 'USD';
$settings->locale = $request->input('locale', 'en');
$settings->default_currency = $request->input('default_currency', "USD");
$settings->user_id = 1;
$settings->email_domain = e(Input::get('email_domain'));
$settings->email_format = e(Input::get('email_format'));
$settings->email_domain = $request->input('email_domain');
$settings->email_format = $request->input('email_format');
$settings->next_auto_tag_base = 1;
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
if ((!$user->isValid()) || (!$settings->isValid())) {
@@ -203,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'));
});
});*/
}
@@ -263,8 +255,8 @@ class SettingsController extends Controller
$output = Artisan::output();
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
Artisan::call('passport:install');
Artisan::call('migrate', ['--force' => true]);
Artisan::call('passport:install');
}
@@ -332,15 +324,31 @@ 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');
$setting->email_format = $request->input('email_format');
$setting->username_format = $request->input('username_format');
$setting->require_accept_signature = $request->input('require_accept_signature');
$setting->login_note = $request->input('login_note');
if (!config('app.lock_passwords')) {
$setting->login_note = $request->input('login_note');
}
$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');
@@ -388,6 +396,11 @@ 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');
// Only allow the site name and CSS to be changed if lock_passwords is false
@@ -411,7 +424,7 @@ class SettingsController extends Controller
$file_name = "logo.".$image->getClientOriginalExtension();
$path = public_path('uploads');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 40, function ($constraint) {
Image::make($image->getRealPath())->resize(null, 150, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
@@ -467,6 +480,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');
}
}
@@ -475,6 +493,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'));
}
@@ -562,13 +581,17 @@ 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');
$setting->audit_interval = $request->input('audit_interval');
$setting->audit_warning_days = $request->input('audit_warning_days');
$setting->show_alerts_in_menu = $request->input('show_alerts_in_menu', '0');
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -764,6 +787,7 @@ class SettingsController extends Controller
$setting->labels_fontsize = $request->input('labels_fontsize');
$setting->labels_pagewidth = $request->input('labels_pagewidth');
$setting->labels_pageheight = $request->input('labels_pageheight');
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
@@ -783,7 +807,13 @@ class SettingsController extends Controller
$setting->labels_display_tag = 1;
} else {
$setting->labels_display_tag = 0;
}
}
if (Input::has('labels_display_tag')) {
$setting->labels_display_tag = 1;
} else {
$setting->labels_display_tag = 0;
}
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -843,6 +873,7 @@ class SettingsController extends Controller
$setting->is_ad = $request->input('is_ad', '0');
$setting->ldap_tls = $request->input('ldap_tls', '0');
$setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0');
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -904,12 +935,30 @@ class SettingsController extends Controller
public function postBackups()
{
if (!config('app.lock_passwords')) {
Artisan::call('backup:run');
return redirect()->route('settings.backups.index')->with('success', trans('admin/settings/message.backup.generated'));
} else {
return redirect()->to("settings.backups.index")->with('error', trans('general.feature_disabled'));
}
$output = Artisan::output();
// Backup completed
if (!preg_match('/failed/', $output)) {
return redirect()->route('settings.backups.index')
->with('success', trans('admin/settings/message.backup.generated'));
}
$formatted_output = str_replace('Backup completed!', '', $output);
$output_split = explode('...', $formatted_output);
if (array_key_exists(2, $output_split)) {
return redirect()->route("settings.backups.index")->with('error', $output_split[2]);
}
return redirect()->route("settings.backups.index")->with('error', $formatted_output);
}
return redirect()->route("settings.backups.index")->with('error', trans('general.feature_disabled'));
}
@@ -992,6 +1041,8 @@ class SettingsController extends Controller
{
if (!config('app.lock_passwords')) {
if (Input::get('confirm_purge')=='DELETE') {
// Run a backup immediately before processing
Artisan::call('backup:run');
Artisan::call('snipeit:purge', ['--force'=>'true','--no-interaction'=>true]);
$output = Artisan::output();
return view('settings/purge')
@@ -1018,4 +1069,28 @@ class SettingsController extends Controller
public function api() {
return view('settings.api');
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.from.address'), config('mail.from.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
return response()->json(Helper::formatStandardApiResponse('success', null, 'Maiol sent!'));
} catch (Exception $e) {
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
}
}
}

View File

@@ -32,18 +32,18 @@ class StatuslabelsController extends Controller
public function index()
{
return view('statuslabels/index', compact('statuslabels'));
$this->authorize('view', Statuslabel::class);
return view('statuslabels.index', compact('statuslabels'));
}
public function show($id)
{
$statuslabel = Statuslabel::find($id);
if (isset($statuslabel->id)) {
return view('statuslabels/view', compact('statuslabel'));
$this->authorize('view', Statuslabel::class);
if ($statuslabel = Statuslabel::find($id)) {
return view('statuslabels.view')->with('statuslabel', $statuslabel);
}
return redirect()->route('statuslabels.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist', compact('id')));
}
@@ -56,6 +56,7 @@ class StatuslabelsController extends Controller
public function create()
{
// Show the page
$this->authorize('create', Statuslabel::class);
$item = new Statuslabel;
$use_statuslabel_type = $item->getStatuslabelType();
$statuslabel_types = Helper::statusTypeList();
@@ -73,6 +74,7 @@ class StatuslabelsController extends Controller
public function store(Request $request)
{
$this->authorize('create', Statuslabel::class);
// create a new model instance
$statusLabel = new Statuslabel();
@@ -91,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'));
@@ -101,35 +103,6 @@ class StatuslabelsController extends Controller
return redirect()->back()->withInput()->withErrors($statusLabel->getErrors());
}
/**
* @param Request $request
* @return JsonResponse
*/
public function apiStore(Request $request)
{
$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.
*
@@ -138,6 +111,7 @@ class StatuslabelsController extends Controller
*/
public function edit($statuslabelId = null)
{
$this->authorize('update', Statuslabel::class);
// Check if the Statuslabel exists
if (is_null($item = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
@@ -160,6 +134,7 @@ class StatuslabelsController extends Controller
*/
public function update(Request $request, $statuslabelId = null)
{
$this->authorize('update', Statuslabel::class);
// Check if the Statuslabel exists
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
@@ -180,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?
@@ -198,19 +174,18 @@ class StatuslabelsController extends Controller
*/
public function destroy($statuslabelId)
{
$this->authorize('delete', Statuslabel::class);
// Check if the Statuslabel exists
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.not_found'));
}
if ($statuslabel->has_assets() == 0) {
// Check that there are no assets associated
if ($statuslabel->assets()->count() == 0) {
$statuslabel->delete();
// Redirect to the statuslabels management page
return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.delete.success'));
}
// Redirect to the asset management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.assoc_assets'));
}

View File

@@ -13,6 +13,7 @@ use Str;
use View;
use Auth;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -32,6 +33,7 @@ class SuppliersController extends Controller
public function index()
{
// Grab all the suppliers
$this->authorize('view', Supplier::class);
$suppliers = Supplier::orderBy('created_at', 'DESC')->get();
// Show the page
@@ -46,6 +48,7 @@ class SuppliersController extends Controller
*/
public function create()
{
$this->authorize('create', Supplier::class);
return view('suppliers/edit')->with('item', new Supplier);
}
@@ -56,8 +59,9 @@ class SuppliersController extends Controller
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize('create', Supplier::class);
// Create a new supplier
$supplier = new Supplier;
// Save the location data
@@ -76,41 +80,23 @@ class SuppliersController extends Controller
$supplier->url = $supplier->addhttp(request('url'));
$supplier->user_id = Auth::id();
if (Input::file('image')) {
if ($request->file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/suppliers/'.$file_name);
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$supplier->image = $file_name;
$supplier->image = $file_name;
}
// Was it created?
if ($supplier->save()) {
// Redirect to the new supplier page
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($supplier->getErrors());
}
/**
* @param Request $request
* @return JsonResponse
*/
public function apiStore(Request $request)
{
$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.
*
@@ -119,6 +105,7 @@ class SuppliersController extends Controller
*/
public function edit($supplierId = null)
{
$this->authorize('edit', Supplier::class);
// Check if the supplier exists
if (is_null($item = Supplier::find($supplierId))) {
// Redirect to the supplier page
@@ -136,8 +123,9 @@ class SuppliersController extends Controller
* @param int $supplierId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($supplierId = null, Request $request)
public function update($supplierId = null, ImageUploadRequest $request)
{
$this->authorize('edit', Supplier::class);
// Check if the supplier exists
if (is_null($supplier = Supplier::find($supplierId))) {
// Redirect to the supplier page
@@ -159,21 +147,39 @@ class SuppliersController extends Controller
$supplier->url = $supplier->addhttp(request('url'));
$supplier->notes = request('notes');
if (Input::file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/suppliers/'.$file_name);
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$supplier->image = $file_name;
}
if (request('image_delete') == 1 && $request->file('image') == "") {
$old_image = $supplier->image;
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
$supplier->image = null;
}
if ($request->file('image')) {
$image = $request->file('image');
$file_name = $supplier->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(app('suppliers_upload_path').$file_name);
} else {
$image->move(app('suppliers_upload_path'), $file_name);
}
$supplier->image = $file_name;
}
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
try {
unlink(app('suppliers_upload_path').$old_image);
} catch (\Exception $e) {
\Log::error($e);
}
}
if ($supplier->save()) {
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.update.success'));
}
@@ -190,23 +196,30 @@ class SuppliersController extends Controller
*/
public function destroy($supplierId)
{
// Check if the supplier exists
if (is_null($supplier = Supplier::find($supplierId))) {
// Redirect to the suppliers page
$this->authorize('delete', Supplier::class);
if (is_null($supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets','licenses')->find($supplierId))) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
}
if ($supplier->num_assets() == 0) {
// Delete the supplier
$supplier->delete();
// Redirect to the suppliers management page
return redirect()->route('suppliers.index')->with(
'success',
trans('admin/suppliers/message.delete.success')
);
if ($supplier->assets_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
}
// Redirect to the asset management page
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.assoc_users'));
if ($supplier->asset_maintenances_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count]));
}
if ($supplier->licenses_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count]));
}
$supplier->delete();
return redirect()->route('suppliers.index')->with('success',
trans('admin/suppliers/message.delete.success')
);
}
@@ -228,7 +241,7 @@ class SuppliersController extends Controller
$error = trans('admin/suppliers/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('suppliers')->with('error', $error);
return redirect()->route('suppliers.index')->with('error', $error);
}
}

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,10 +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;
/**
* This controller handles all actions related to Users for
@@ -84,10 +87,6 @@ class UsersController extends Controller
$permissions = $this->filterDisplayable($permissions);
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
->with('location_list', Helper::locationsList())
->with('manager_list', Helper::managerList())
->with('company_list', Helper::companyList())
->with('department_list', Helper::departmentList())
->with('user', new User);
}
@@ -109,7 +108,6 @@ class UsersController extends Controller
$user->password = bcrypt($request->input('password'));
$data['password'] = $request->input('password');
}
// Update the user
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->locale = $request->input('locale');
@@ -122,6 +120,11 @@ class UsersController extends Controller
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
$user->notes = $request->input('notes');
$user->address = $request->input('address', null);
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
// Strip out the superuser permission if the user isn't a superadmin
$permissions_array = $request->input('permission');
@@ -145,78 +148,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)
{
@@ -229,11 +177,20 @@ 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)
{
try {
$user = User::find($id);
if ($user = User::find($id)) {
$this->authorize('update', $user);
$permissions = config('permissions');
@@ -243,19 +200,14 @@ class UsersController extends Controller
$user->permissions = $user->decodePermissions();
$userPermissions = Helper::selectedPermissionsArray($permissions, $user->permissions);
$permissions = $this->filterDisplayable($permissions);
} catch (UserNotFoundException $e) {
$error = trans('admin/users/message.user_not_found', compact('id'));
return redirect()->route('users.index')->with('error', $error);
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))->with('item', $user);
}
// Show the page
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))
->with('location_list', Helper::locationsList())
->with('department_list', Helper::departmentList())
->with('company_list', Helper::companyList())
->with('manager_list', Helper::managerList());
$error = trans('admin/users/message.user_not_found', compact('id'));
return redirect()->route('users.index')->with('error', $error);
}
/**
@@ -281,6 +233,10 @@ class UsersController extends Controller
try {
$user = User::find($id);
if ($user->id == $request->input('manager_id')) {
return redirect()->back()->withInput()->with('error', 'You cannot be your own manager.');
}
$this->authorize('update', $user);
// Figure out of this user was an admin before this edit
$orig_permissions_array = $user->decodePermissions();
@@ -319,16 +275,26 @@ 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->jobtitle = $request->input('jobtitle');
$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);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
$user->notes = $request->input('notes');
$user->department_id = $request->input('department_id', null);
$user->address = $request->input('address', null);
$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);
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
// Do we want to update the user password?
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
@@ -366,18 +332,24 @@ 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);
if ($user->assets()->count() > 0) {
// Check if we are not trying to delete ourselves
if ($user->id === Auth::user()->id) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
}
if ($user->assets->count() > 0) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . count($user->assets->count()) . ' assets associated with them.');
}
if ($user->licenses()->count() > 0) {
// Redirect to the user management page
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->licenses()->count() . ' licenses associated with them.');
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
}
if ($user->accessories()->count() > 0) {
@@ -398,7 +370,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
@@ -416,28 +388,19 @@ class UsersController extends Controller
public function postBulkEdit(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 {
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('location_list', Helper::locationsList())
->with('company_list', Helper::companyList())
->with('manager_list', Helper::managerList())
->with('manager_list', Helper::managerList())
->with('department_list', Helper::departmentList())
->with('groups', Group::pluck('name', 'id'));
}
return view('users/confirm-bulk-delete', compact('users', 'statuslabel_list'));
}
return redirect()->back()->with('error', 'No users selected');
}
@@ -451,15 +414,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');
@@ -470,6 +431,10 @@ class UsersController extends Controller
if ($request->has('company_id')) {
$update_array['company_id'] = $request->input('company_id');
}
if ($request->has('locale')) {
$update_array['locale'] = $request->input('locale');
}
if ($request->has('manager_id')) {
@@ -477,7 +442,6 @@ class UsersController extends Controller
// edited.
if (!array_key_exists($request->input('manager_id'), $user_raw_array)) {
$update_array['manager_id'] = $request->input('manager_id');
} else {
$manager_conflict = true;
}
@@ -487,8 +451,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
@@ -498,13 +463,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');
}
@@ -516,13 +485,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 {
@@ -533,9 +502,6 @@ class UsersController extends Controller
unset($user_raw_array[$key]);
}
if (!Auth::user()->isSuperUser()) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.insufficient_permissions'));
}
if (!config('app.lock_passwords')) {
@@ -721,7 +687,7 @@ class UsersController extends Controller
$user->first_name = '';
$user->last_name = '';
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
;
$user->id = null;
// Get this user groups
@@ -734,10 +700,6 @@ class UsersController extends Controller
// Show the page
return view('users/edit', compact('permissions', 'userPermissions'))
->with('location_list', Helper::locationsList())
->with('company_list', Helper::companyList())
->with('manager_list', Helper::managerList())
->with('department_list', Helper::departmentList())
->with('user', $user)
->with('groups', Group::pluck('name', 'id'))
->with('userGroups', $userGroups)
@@ -750,125 +712,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'
);
//dd($newuser);
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.
*
@@ -928,7 +771,6 @@ class UsersController extends Controller
$user = User::find($userId);
$destinationPath = config('app.private_uploads').'/users';
// the license is valid
if (isset($user->id)) {
$this->authorize('update', $user);
$log = Actionlog::find($fileId);
@@ -996,8 +838,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');
}
@@ -1028,128 +869,20 @@ class UsersController extends Controller
*/
public function postLDAP(Request $request)
{
$this->authorize('update', User::class);
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
// Call Artisan LDAP import command.
$location_id = $request->input('location_id');
Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]);
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
// Collect and parse JSON summary.
$ldap_results_json = Artisan::output();
$ldap_results = json_decode($ldap_results_json, true);
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_email = Setting::getSettings()->ldap_email;
try {
$ldapconn = Ldap::connectToLdap();
} catch (\Exception $e) {
return redirect()->back()->withInput()->with('error', $e->getMessage());
// Direct user to appropriate status page.
if ($ldap_results['error']) {
return redirect()->back()->withInput()->with('error', $ldap_results['error_message']);
} else {
return redirect()->route('ldap/user')->with('success', "LDAP Import successful.")->with('summary', $ldap_results['summary']);
}
try {
Ldap::bindAdminToLdap($ldapconn);
} catch (\Exception $e) {
return redirect()->back()->withInput()->with('error', $e->getMessage());
}
$summary = array();
$ldap_ou_locations = Location::whereNotNull('ldap_ou')->get();
$results = Ldap::findLdapUsers();
// 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];
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if (in_array($generic_entry[$ldap_result_username][0], $location_users)) {
unset($results[$key]);
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
$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") {
$item = array();
$item["username"] = isset($results[$i][$ldap_result_username][0]) ? $results[$i][$ldap_result_username][0] : "";
$item["employee_number"] = isset($results[$i][$ldap_result_emp_num][0]) ? $results[$i][$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
if( array_key_exists('useraccountcontrol', $results[$i]) ) {
$enabled_accounts = [
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224'
];
$item['activated'] = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
} else {
$item['activated'] = 0;
}
// User exists
$item["createorupdate"] = 'updated';
if (!$user = User::where('username', $item["username"])->first()) {
$user = new User;
$user->password = $pass;
$item["createorupdate"] = 'created';
}
// Create the user if they don't exist.
$user->first_name = $item["firstname"];
$user->last_name = $item["lastname"];
$user->username = $item["username"];
$user->email = $item["email"];
$user->employee_num = e($item["employee_number"]);
$user->activated = $item['activated'];
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} else if ($request->input('location_id')!='') {
$user->location_id = e($request->input('location_id'));
}
$user->notes = 'Imported from LDAP';
$user->ldap_import = 1;
$errors = '';
if ($user->save()) {
$item["note"] = $item["createorupdate"];
$item["status"]='success';
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .='<li>'.$err[0];
}
$item["note"] = $errors;
$item["status"]='error';
}
array_push($summary, $item);
}
}
return redirect()->route('ldap/user')->with('success', "LDAP Import successful.")->with('summary', $summary);
}
@@ -1170,7 +903,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')),
@@ -1182,6 +915,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'),
@@ -1212,6 +946,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(),
@@ -1238,20 +973,23 @@ class UsersController extends Controller
}
public function postTwoFactorReset(Request $request)
/**
* LDAP form processing.
*
* @author Aladin Alaily
* @since [v1.8]
* @return \Illuminate\Http\RedirectResponse
*/
public function printInventory($id)
{
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);
}
$show_user = User::where('id',$id)->withTrashed()->first();
$assets = Asset::where('assigned_to', $id)->where('assigned_type', User::class)->with('model', 'model.category')->get();
$licenses = $show_user->licenses()->get();
$accessories = $show_user->accessories()->get();
$consumables = $show_user->consumables()->get();
return view('users/print')->with('assets', $assets)->with('licenses',$licenses)->with('accessories', $accessories)->with('consumables', $consumables)->with('show_user', $show_user);
}
}

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);
}
}
@@ -68,133 +70,83 @@ class ViewAssetsController extends Controller
public function getRequestableIndex()
{
$assets = Asset::with('model', 'defaultLoc', 'assetloc', 'assignedTo', 'requests')->Hardware()->RequestableAssets()->get();
$assets = Asset::with('model', 'defaultLoc', 'location', 'assignedTo', 'requests')->Hardware()->RequestableAssets()->get();
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
return view('account/requestable-assets', compact('user', 'assets', 'models'));
}
public function getRequestedIndex()
{
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->get();
return view('admin/requested-assets', compact('requestedItems'));
}
public function getRequestItem($itemType, $itemId = null)
{
$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)
{
@@ -202,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'));
}
@@ -278,20 +203,18 @@ 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)
{
if (!$findlog = Actionlog::where('id', $logID)->first()) {
echo 'no record';
//return redirect()->to('account')->with('error', trans('admin/hardware/message.does_not_exist'));
$findlog = Actionlog::where('id', $logID)->first();
if (!$findlog) {
return redirect()->to('account/view-assets')->with('error', 'No matching record.');
}
if ($findlog->accepted_id!='') {
@@ -301,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
@@ -340,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'));
}
@@ -392,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

@@ -19,10 +19,13 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\FrameGuard::class,
\App\Http\Middleware\XssProtectHeader::class,
\App\Http\Middleware\ReferrerPolicyHeader::class,
\App\Http\Middleware\ContentSecurityPolicyHeader::class,
\App\Http\Middleware\NosniffGuard::class,
\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

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

View File

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

View File

@@ -14,8 +14,9 @@ class XssProtectHeader
*/
public function handle($request, Closure $next)
{
$mode = '1;mode=block';
$response = $next($request);
$response->headers->set('X-XSS-Protection', '1');
$response->headers->set('X-XSS-Protection', $mode);
return $response;
}
}

View File

@@ -23,10 +23,14 @@ class AssetCheckoutRequest extends Request
*/
public function rules()
{
return [
"assigned_user" => 'required_without_all:assigned_asset,assigned_location',
"assigned_asset" => 'required_without_all:assigned_user,assigned_location',
"assigned_location" => 'required_without_all:assigned_user,assigned_asset',
$rules = [
"assigned_user" => 'required_without_all:assigned_asset,assigned_location',
"assigned_asset" => 'required_without_all:assigned_user,assigned_location|different:'.$this->id,
"assigned_location" => 'required_without_all:assigned_user,assigned_asset',
"checkout_to_type" => 'required|in:asset,location,user'
];
return $rules;
}
}

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

@@ -36,20 +36,24 @@ class AssetRequest extends Request
'checkin_date' => 'date',
'supplier_id' => 'integer|nullable',
'status' => 'integer|nullable',
'asset_tag' => 'required',
'purchase_cost' => 'numeric|nullable',
"assigned_user" => 'sometimes:required_without_all:assigned_asset,assigned_location',
"assigned_asset" => 'sometimes:required_without_all:assigned_user,assigned_location',
"assigned_location" => 'sometimes:required_without_all:assigned_user,assigned_asset',
];
$model = AssetModel::find($this->request->get('model_id'));
$settings = \App\Models\Setting::getSettings();
if (($model) && ($model->fieldset)) {
$rules += $model->fieldset->validation_rules();
$rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
if($this->request->get('model_id') != '') {
$model = AssetModel::find($this->request->get('model_id'));
if (($model) && ($model->fieldset)) {
$rules += $model->fieldset->validation_rules();
}
}
return $rules;
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
class CustomFieldRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(Request $request)
{
$rules = [];
switch($this->method())
{
// Brand new
case 'POST':
{
$rules['name'] = "required|unique:custom_fields";
break;
}
// Save all fields
case 'PUT':
$rules['name'] = "required";
break;
// Save only what's passed
case 'PATCH':
{
$rules['name'] = "required";
break;
}
default:break;
}
$rules['custom_format'] = 'valid_regex';
return $rules;
}
}

View File

@@ -24,7 +24,8 @@ class ImageUploadRequest extends Request
public function rules()
{
return [
'image' => 'mimes:png,gif,jpg,jpeg,svg|max:2000'
'image' => 'mimes:png,gif,jpg,jpeg,svg',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg',
];
}

View File

@@ -26,7 +26,7 @@ class ItemImportRequest extends FormRequest
public function rules()
{
return [
//
'import-type' => 'required',
];
}

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