Compare commits

..

1217 Commits

Author SHA1 Message Date
snipe d20b5136b8 Nicer display if no files have been uploaded 2015-02-06 22:02:52 -08:00
snipe 29f5b7c5e3 Fixed datetime 2015-02-06 21:54:44 -08:00
snipe 140bd137a4 Minor display fixes 2015-02-06 21:54:36 -08:00
snipe 3a9cb9ba7a Display uploaded files 2015-02-06 21:31:16 -08:00
snipe 6d0828bfe5 Delete file interface and routes 2015-02-06 20:42:40 -08:00
snipe 51a7752771 Moved cancel text to buttons language file 2015-02-06 18:52:09 -08:00
snipe a8681225f5 Added language for add button 2015-02-06 18:46:20 -08:00
snipe 26a59afa44 Basic upload display and form 2015-02-06 18:43:15 -08:00
snipe e22ce0d377 Changes added_on to created_at 2015-02-06 18:43:04 -08:00
snipe 2fbef7f561 Updated field name for asset log 2015-02-06 18:42:48 -08:00
snipe 2678611c79 Included doctrine for field renaming 2015-02-06 18:42:36 -08:00
snipe 416cd236d2 Updated to new fieldname 2015-02-06 18:42:24 -08:00
snipe 4936a5914f Folder for private uploads 2015-02-06 18:41:56 -08:00
snipe 36fce27297 Added uploads methods 2015-02-06 18:41:47 -08:00
snipe 4b2e45c5f2 Added uploads methods 2015-02-06 18:41:35 -08:00
snipe 11f46474ab New file upload language 2015-02-06 18:41:24 -08:00
snipe b0d52a4158 Updated field name 2015-02-06 18:41:16 -08:00
snipe 7b4845c255 Added soft delete to asset log 2015-02-06 18:40:58 -08:00
snipe 223ba349ea Minor change to comment for clarify 2015-02-06 16:57:15 -08:00
snipe feeef4b66c Fix for asset log incorrectly using soft deletes 2015-02-06 14:54:13 -08:00
snipe 42f8a35078 Additional translation files 2015-02-05 17:08:20 -08:00
snipe 0128505213 Overwriting Smfony YAML deps 2015-02-05 15:50:32 -08:00
snipe 06bee68cd2 Testing config for travis 2015-02-05 15:34:26 -08:00
snipe c255edaf61 More travis fixes 2015-02-05 15:30:51 -08:00
snipe 7f0e21d50c Changes to make travis build pass 2015-02-05 15:02:57 -08:00
snipe 1aeb4de0fa Possible fix for #491 - solving for giticide on package dep 2015-02-05 14:43:01 -08:00
snipe bff33cb7f5 removed old package dep 2015-02-05 14:10:25 -08:00
snipe 5695815360 Missed one --force 2015-02-04 16:22:45 -08:00
snipe e67ba8a743 Fix reporting error on depreciation 2015-02-04 16:13:11 -08:00
snipe 3669ab2ae8 Updated travis for forced migration in production 2015-02-04 16:07:41 -08:00
snipe e2ee954592 Updated composer and composer.lock for 4.2 2015-02-04 15:50:54 -08:00
snipe b4750f016d Added new 4.2 SoftDeletingTrait 2015-02-04 15:50:40 -08:00
snipe 7069d9c6b1 Added new cipher to config 2015-02-04 15:50:07 -08:00
snipe 8e0d774023 Added strict option in database config 2015-02-04 15:49:50 -08:00
snipe 394836821c Added catehory nav item in sidebar 2015-02-04 15:01:36 -08:00
snipe a1ac7f8676 Removed cruft 2015-02-04 15:01:08 -08:00
snipe cd338d2a70 Fixes #483 - line breaks in notes on view 2015-02-04 10:21:02 -08:00
snipe 4dc81ce65f Resolves #478 - redirects non-reporting users to view assets page 2015-02-04 09:31:01 -08:00
snipe f5efc368a5 Fixes #487 - maintained and purchase order in strict mode 2015-02-04 08:51:03 -08:00
snipe 1db806f8b2 Fixes #488 - longer user jobtitle 2015-02-04 07:59:45 -08:00
snipe b3af102d22 Fixed safe mode issue with license seeder 2015-02-04 07:49:28 -08:00
snipe 2b86f14e30 Possible fix for #484 - fullname issue 2015-02-01 14:33:58 -08:00
snipe 3f0e494276 Updated contributing info 2015-01-23 15:39:03 -08:00
snipe a4bac03692 Increment version number 2015-01-23 15:38:48 -08:00
snipe 950567e9d6 Fixes issue related to #471 - assigning to user ID 0 causes erro 2015-01-23 15:38:39 -08:00
snipe 3787a3f4ba Fixed language strings for incrementing IDs 2015-01-22 15:34:19 -08:00
snipe 95141d49ba Fixes #432 - add model name to checkout 2015-01-22 15:20:16 -08:00
snipe d85e524680 Fixes #451 - allows spaces in state name 2015-01-22 14:29:53 -08:00
snipe 4a77dee79c Fixes #447 - added asset log entry on save 2015-01-22 14:16:09 -08:00
snipe 6fba41e026 Updated gitignore to ignore crowdin 2015-01-22 13:42:32 -08:00
snipe 53085983f8 Added UK English language file 2015-01-22 13:24:01 -08:00
snipe 4a2ac03ace New Spanish strings 2015-01-22 13:23:29 -08:00
snipe 548de1a1a2 Fixes #468 - updated ordering on license checkout 2015-01-22 13:03:07 -08:00
snipe 82560b1098 Updated language files with new strings 2015-01-22 13:00:38 -08:00
snipe 3c141e8f50 Fixes #467 2015-01-22 12:56:13 -08:00
snipe 5e496672c6 Fixes #469 2015-01-22 12:54:22 -08:00
snipe 4887c53f25 Merge pull request #452 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2015-01-12 15:16:57 -08:00
The Gitter Badger 2b79a61d65 Added Gitter badge 2015-01-12 23:15:02 +00:00
snipe 778a7bb18c Additional translations 2015-01-08 17:24:13 -08:00
snipe 685881cf27 Too many thanks (too many thanks) 2015-01-08 17:19:02 -08:00
snipe cf4b80bbd5 Updated contributing guidelines 2015-01-08 17:18:19 -08:00
snipe e434616db8 Merge pull request #436 from rashivkp/fix-435
fixed #435. using namespace with route groups to increase the readability
2015-01-04 21:50:07 -06:00
snipe 8a7b7418db Merge pull request #433 from thehereward/tidy-up-models
Tidy up models
2015-01-04 21:49:57 -06:00
Mohamed Rashid 533be7f942 fixed #435. using namespace with route groups to increase the readability 2015-01-05 01:24:16 +05:30
Hereward Mills 094ec50a68 TidyUp: Commonise the depreciate method of Asset and License. 2014-12-24 23:03:06 +00:00
Hereward Mills 98a29e460d TidyUp: Remove excess whitespace. 2014-12-24 22:34:16 +00:00
snipe 27fabfd110 Updated readme 2014-12-23 14:50:34 -05:00
snipe dce1d47228 Updated language files 2014-12-23 14:48:43 -05:00
snipe 9bbec53635 Added --no-dev to composer for CI 2014-12-23 14:19:46 -05:00
snipe 82232a34ee Merge pull request #415 from svpernova09/feature/software-maintenance-additions
Feature/software maintenance additions Working on #232
2014-12-23 14:11:20 -05:00
Svpernova09 3d1795e312 Add process handling of new maintained and termination_date fields to the licenses methods for creating and editing licenses. Also add array for getCreate and getEdit to easily display maintained options in drop down 2014-12-10 07:45:53 -06:00
Svpernova09 d858861d0f Add termination_date and maintained fields to the License edit form. Add field labels to language file. 2014-12-10 07:44:47 -06:00
Svpernova09 eec36cb13a Add migration to add maintained and termination_date fields to licenses table 2014-12-10 07:43:57 -06:00
snipe cee966e76e Merge pull request #408 from svpernova09/feature/add-asset-from-model-view
Feature/add asset from model view for #365
2014-12-03 15:25:56 -05:00
Svpernova09 dcf2b428da If we are passing along an id of a previously selected model, use that as the default selection. Otherwise we should fall back to Input::old() 2014-12-03 14:06:33 -06:00
Svpernova09 80d8432c35 Check the optional parameter and if there is a value, grab that model and pass along to the view. Refactored the view return to be easier to read and able to interject if our parameter was set. 2014-12-03 14:05:42 -06:00
Svpernova09 8ec5934af5 Add menu option in dropdown to create new hardware. Add optional parameter in create/hardware route to handle the parameter in the incoming link 2014-12-03 14:04:02 -06:00
snipe 31f77dcb4b Merge pull request #407 from svpernova09/bug/500-error-when-viewing-asset
Check if ->adminuser->id isset before trying to print the fullName so we...
2014-12-03 14:08:09 -05:00
Svpernova09 0663ccb99f Check if ->adminuser->id isset before trying to print the fullName so we can catch when a user that created an asset has been deleted. Fixes #349 2014-12-03 07:46:29 -06:00
snipe 19189f688f Merge pull request #405 from svpernova09/enhancement/make-notes-field-textarea
Enhancement/make notes field textarea
2014-12-02 11:21:36 -05:00
Svpernova09 7849320db0 Convert note fields to text areas for licenses views 2014-12-02 06:59:16 -06:00
Svpernova09 cc8ca54b3f Convert note fields to text areas for hardware views 2014-12-02 06:59:01 -06:00
Svpernova09 ecb4d0d948 Add jetbrains IDE's config folders to gitignore 2014-12-02 06:51:50 -06:00
snipe f20566f222 Merge pull request #393 from madd15/patch-1
Fix #388 - License not showing checked out in view
2014-11-23 19:36:28 -05:00
madd15 73dd339772 Fix #388 - License not showing checked out in view 2014-11-24 11:05:24 +10:30
snipe 5e6c4a250a Updated composer lock 2014-11-21 07:34:06 -05:00
snipe bf96aae9d9 Truncate license in report 2014-11-21 07:32:12 -05:00
snipe daefc86e58 Formattig fixes for #386 2014-11-21 06:10:30 -05:00
snipe 64c16a0bac Fixes #386 and #273 - altered column type to text 2014-11-21 05:47:49 -05:00
snipe 89a2b87e42 Fixes #367 - added model number to dropdown 2014-11-21 05:20:08 -05:00
snipe 1b41c974cf Fixes #348 - remove constraints on license keys 2014-11-20 18:43:33 -05:00
snipe d6a0249c60 Fixed #373 - added ability to auto-increment asset tags 2014-11-20 18:28:45 -05:00
snipe 6a6676b1e7 Merge pull request #383 from madd15/patch-1
Fix half #377 - Checkout users last_name, first_name
2014-11-20 17:19:44 -05:00
madd15 1024816313 Fix #377 - Checkout users last_name, first_name 2014-11-21 08:47:19 +10:30
snipe 6905d82fc4 Fixes #374 to keep the requirement to PHP 5.4 2014-11-20 16:38:50 -05:00
snipe 22746db8b7 Newest translations 2014-11-20 16:28:38 -05:00
snipe b826673d0d Added username field for #377 2014-11-20 16:17:48 -05:00
snipe 637009054c Fixed typo in english 2014-11-20 16:09:30 -05:00
snipe 5f5be6aa2a Removed line break 2014-11-20 16:09:21 -05:00
snipe a863cf1615 Merge pull request #364 from madd15/patch-1
Remove Double </label>
2014-11-13 18:30:40 -05:00
madd15 05d8ff0252 Remove Double </label> 2014-11-14 09:57:02 +10:30
snipe b73ae3147a Fixes #362, reduced asset name to 2 char min 2014-11-13 16:26:39 -05:00
snipe 81b2ce33c2 Polish, german and spanish translation strings 2014-11-12 21:13:46 -05:00
snipe 501e1749b3 Updated localization strings 2014-11-12 17:38:56 -05:00
snipe 53f279f55f Language string for "generate" 2014-11-12 17:34:22 -05:00
snipe f5fb2525de Merge branch 'develop' of github.com:snipe/snipe-it into develop 2014-11-12 17:32:58 -05:00
snipe 53951c8bbc Nicer custom reports form layout 2014-11-12 17:32:48 -05:00
snipe 00755eadf0 Merge pull request #359 from madd15/patch-1
Fix Lang for Reports
2014-11-12 17:17:07 -05:00
madd15 13b173409f Fix License form langs 2014-11-13 08:44:47 +10:30
madd15 7ddc6d497a Fix Report Langs 2014-11-13 08:43:42 +10:30
snipe 2829eeca7c New reports language files 2014-11-12 17:05:46 -05:00
snipe e39227633f Merge pull request #358 from madd15/feature-reports
Add error on empty options and Update to Lang for error and info
2014-11-12 17:01:31 -05:00
Madd15 89c7159c56 Fix array check for empty rows 2014-11-13 08:29:15 +10:30
snipe 6854453bc5 English is hard :( 2014-11-12 16:38:31 -05:00
snipe ce9c616843 Updated language strings 2014-11-12 16:33:11 -05:00
Madd15 40b0ced8da Update Lang for error and info 2014-11-13 08:02:39 +10:30
Madd15 57ec8aa3c2 Add error for no options selected 2014-11-13 08:02:25 +10:30
snipe 02feb69d5d Localization on user's view page, new strings 2014-11-12 16:22:30 -05:00
snipe fc2d472ed3 Fixed english language string 2014-11-12 15:52:08 -05:00
snipe b622d977d9 Merge pull request #355 from madd15/feature-reports
Added Customizable Asset Report
2014-11-12 01:35:44 -05:00
Madd15 6d2a073d04 Custom Asset Report 2014-11-12 16:05:59 +10:30
snipe 564cf90e58 Merge pull request #354 from madd15/feature-reports
Feature Extended Reports
2014-11-12 00:32:51 -05:00
Madd15 c60e40096a Replace static titles with lang 2014-11-12 13:38:31 +10:30
Madd15 74ae2fceea Fix class on th 2014-11-12 13:34:53 +10:30
Madd15 02605bdf69 Add Asset Report with Reduced Load Time 2014-11-12 12:03:33 +10:30
Madd15 432e348e56 Add Report Controller and License Report 2014-11-12 12:02:55 +10:30
snipe d91a7c7aa8 Merge pull request #351 from diwanicki/patch-1
Update view.blade.php
2014-11-07 16:57:39 -05:00
diwanicki f2ae05786b Update view.blade.php
closing parens was missing from Depreciation, EOL was not configured for translations
2014-11-07 16:56:28 -05:00
snipe 3d3e2dc985 Merge pull request #346 from madd15/feature-supplier-image
Add image to supplier
2014-11-06 22:24:37 -05:00
madd15 3c3617d641 Add image to supplier 2014-11-07 13:46:14 +10:30
snipe b518162c50 Merge pull request #345 from madd15/patch-1
Fix #342
2014-11-06 16:23:50 -05:00
madd15 b2d5dc9ec5 Fix #342 2014-11-07 07:52:44 +10:30
snipe cff24309a1 Possibly fixes #343 2014-11-06 15:21:54 -05:00
snipe b25c94e429 Purchase cost is not required 2014-11-06 15:21:32 -05:00
snipe 53a47743e8 Language string udpates 2014-11-05 23:06:06 -05:00
snipe 8a10386737 Merge pull request #338 from madd15/patch-license-lang
Lang changes for new licenses functions
2014-11-05 22:49:00 -05:00
madd15 7e95d36245 Lang changes for new licenses functions 2014-11-06 14:15:09 +10:30
snipe f90327ee9c Merge pull request #337 from madd15/patch-1
Update to v1.1
2014-11-05 22:21:10 -05:00
madd15 3b5670965c Update to v1.1 2014-11-06 13:50:13 +10:30
snipe 8699a90093 Fixed changelog 2014-11-05 22:05:32 -05:00
snipe 562e5cd700 Changelog generator 2014-11-05 22:03:01 -05:00
snipe 3f7486e1a9 Updated changelog 2014-11-05 22:02:51 -05:00
snipe 0eb6183711 New strings for translation 2014-11-05 21:16:26 -05:00
snipe cdf5ecc084 Merge pull request #336 from madd15/feature-updated-setting-view
Update Settings to contain PHP & Laravel versions
2014-11-05 21:03:27 -05:00
madd15 0eda46da0c Move side column back to top on the right 2014-11-06 11:23:07 +10:30
madd15 1047c5c8bb Update Settings to contain PHP & Laravel versions 2014-11-06 11:12:15 +10:30
snipe 5f6f3de25e Merge pull request #335 from madd15/feature-new-license-fields
Add Fields to Licenses and Update Supplier for Licenses
2014-11-05 18:26:57 -05:00
madd15 7833d10006 Add Fields to Licenses and Update Supplier for Licenses 2014-11-06 09:32:50 +10:30
snipe 122b3d89d9 Added assetlog to other status types 2014-11-05 16:11:07 -05:00
snipe 14801eca79 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2014-11-05 16:10:11 -05:00
snipe 4a4b974f41 Merge pull request #333 from madd15/patch-1
Add assetlog to Deployed Loading
2014-11-05 16:09:55 -05:00
snipe ab7b828e33 Romanian translations 2014-11-05 16:08:54 -05:00
snipe b0e87d967a Arr! Pirate translations 2014-11-05 16:08:19 -05:00
snipe a0001fe1ed Arabic translations 2014-11-05 16:08:08 -05:00
madd15 ac5fa1641e Add assetlog to Deployed Loading 2014-11-06 07:35:49 +10:30
snipe 7c16dd8c92 Eager loading on other status types 2014-11-05 15:57:10 -05:00
snipe 294b02127b Don't show asset name field if there is none 2014-11-05 15:16:38 -05:00
snipe e04cc806d9 Fixes #327 2014-11-05 15:16:22 -05:00
snipe 2bee522a7c Included defaultLoc to assets view 2014-11-04 19:23:11 -05:00
snipe 5441dd41f0 Eager loading on assets listing 2014-11-04 18:58:16 -05:00
snipe 7858252fca More brazilian portuguese translations 2014-11-04 18:33:35 -05:00
snipe 6c6d31f15a A few more hungarian translations 2014-11-04 18:32:38 -05:00
snipe 6b2d3efe3e Fixes #237 - reporting-only group 2014-11-04 18:21:10 -05:00
snipe 1078dbf5c5 Fixes #324 2014-11-03 02:39:11 -05:00
snipe 300c262739 Merge pull request #320 from diwanicki/asset-view-edit-new-org
Adjustments to Asset Edit and Asset View layouts
2014-11-03 00:40:44 -05:00
snipe 25a7b05de6 Merge pull request #323 from madd15/patch-1
Fix if no asset selected sql error
2014-11-03 00:22:00 -05:00
madd15 70d7748229 Remove asterisk from asset 2014-11-03 15:48:51 +10:30
madd15 6315db7fd8 Allow checkout to user without asset 2014-11-03 15:35:04 +10:30
snipe 343798d241 Merge pull request #322 from madd15/fix-js-delete-asset
Delegate to table, add button to on()
2014-11-02 20:10:05 -05:00
snipe 5050862979 Merge pull request #321 from madd15/patch-5
Fix #318 - Missing end div causing layout issues
2014-11-02 17:34:36 -05:00
madd15 90baf2eeba Delegate to table, add button to on() 2014-11-03 08:58:19 +10:30
madd15 0022389463 Fix dropdown menu position 2014-11-03 08:24:53 +10:30
madd15 04123b3fe8 Missing end div causing layout issues 2014-11-03 08:12:34 +10:30
snipe 491de30374 Finnish translation via jarnok 2014-11-02 13:41:12 -05:00
snipe 0d402f52c8 Localization 2014-11-02 13:34:08 -05:00
diwanicki 55cd7c0f81 move br in notes div, almost there 2014-11-02 13:27:32 -05:00
diwanicki 63656762b0 changed style for notes, brd in div 2014-11-02 13:26:09 -05:00
diwanicki 87a9399b6b maybe a break? 2014-11-02 13:18:58 -05:00
diwanicki ed5ff9a01b trying hard space 2014-11-02 13:14:56 -05:00
diwanicki 68bf40a7ea typo2 2014-11-02 13:10:01 -05:00
diwanicki 0cf24bdde1 typo 2014-11-02 13:08:44 -05:00
diwanicki 05502cf29d trying serial in col-md12 2014-11-02 13:03:06 -05:00
diwanicki 1305ea9597 move in div? 2014-11-02 11:27:56 -05:00
diwanicki 94e1fcb01b maybe breaks? 2014-11-02 11:24:28 -05:00
diwanicki 9c01ab334f playing with paragraph tags 2014-11-02 11:22:05 -05:00
diwanicki b685ea666c em, small, and paragraphs 2014-11-02 11:17:34 -05:00
diwanicki ee35948980 fix div issues 2014-11-02 11:09:29 -05:00
diwanicki 569714315c Revert c122cc2..f094551
This rolls back to commit c122cc2647.
2014-11-02 10:54:52 -05:00
diwanicki f09455117b . 2014-11-02 10:51:45 -05:00
diwanicki af70559754 . 2014-11-02 10:50:35 -05:00
diwanicki 07e6d49dce . 2014-11-02 10:49:22 -05:00
diwanicki 86597bb863 more 2014-11-02 10:46:57 -05:00
diwanicki 6d057846a1 again 2014-11-02 10:45:33 -05:00
diwanicki ba9f8c61f1 more 2014-11-02 10:41:52 -05:00
diwanicki d3266dd4e8 still more 2014-11-02 10:40:10 -05:00
diwanicki 9143ac461f and more 2014-11-02 10:37:32 -05:00
diwanicki 7199d6fd22 more adjustments 2014-11-02 10:29:24 -05:00
diwanicki 2b21b26950 more layout adjustments 2014-11-02 10:24:34 -05:00
diwanicki c122cc2647 layout adjustments
layout adjustment to Notes field, hide Notes from asset creation log
display
2014-11-02 10:06:23 -05:00
diwanicki ced8610833 cleanup, reorder, restore Asset view and edit
reordered fields on Asset Add/Edit form to flow more logically.
reordered fields on Asset View to flow more logically. Restored serial
number to Asset View. Added Asset specific notes field to Asset View
(they are in the log as part of the initial asset creation, but need to
be more prominent, especially if the device has a lot of history.
adjusted wording in EN lang template for clarity.
2014-11-02 08:48:42 -05:00
snipe af63d34412 Updated language strings 2014-11-01 19:06:44 -04:00
snipe 9e85dcb9fc Fixes #315 - add EOL as display option in settings (run migrations) 2014-11-01 18:38:24 -04:00
snipe 687c629953 Bootstrap HTML cleanup 2014-11-01 17:45:28 -04:00
snipe 13b3e2c4d4 HTML cleanup 2014-11-01 17:42:15 -04:00
snipe 4d8e663bd1 Replaced hard-coded month string for languag support 2014-11-01 17:27:07 -04:00
snipe 3e11ea70da Reverting to old page title 2014-11-01 17:24:57 -04:00
snipe 86b7b6946f Fixes #316 - added model name back to view 2014-11-01 17:14:36 -04:00
snipe 3184c3e03b Updated language files 2014-10-30 07:51:11 -04:00
snipe 55eb4325bc Merge pull request #309 from madd15/patch-2
Allow checkout to user or asset
2014-10-29 23:15:18 -04:00
snipe d4ef8a75b8 Merge pull request #310 from madd15/patch-3
Change file name to str_random(25) & prevent upsizing
2014-10-29 23:14:56 -04:00
madd15 cf4804a994 Added constraint to image to prevent upsizing 2014-10-30 10:34:52 +10:30
madd15 22590e2a43 Change file name to str_random(25) 2014-10-30 09:52:20 +10:30
madd15 d9f86df05f Uncomment User section 2014-10-30 09:34:55 +10:30
madd15 636eb29421 Change rule back to asset_id required_without 2014-10-30 09:33:31 +10:30
snipe 5dea4c7691 Merge pull request #308 from madd15/patch-1
Fix Gravatar email lang and < conflict
2014-10-29 18:56:26 -04:00
madd15 672e2e5fd4 Add gravatar_email 2014-10-30 09:06:13 +10:30
madd15 42ad3da3d3 Moved part of gravatar label to new line
<small> was conflicting with @lang
2014-10-30 09:04:47 +10:30
snipe bb3aa99b9c Added CrowdIn badge 2014-10-29 18:33:13 -04:00
snipe 676155eacb Added translation link to footer 2014-10-29 17:50:21 -04:00
snipe 54071d1837 Updated readme for translation info 2014-10-29 17:49:55 -04:00
snipe 5abc4467df New placeholder language files for translations 2014-10-29 17:38:24 -04:00
snipe ffe095d64b Updates to language files 2014-10-29 17:38:07 -04:00
snipe ce566509ce Merge pull request #306 from madd15/patch-1
Allow spaces in category name
2014-10-29 14:27:48 -04:00
madd15 84cb51b4c3 Allow spaces in category name 2014-10-29 21:59:51 +10:30
snipe 9b0d930a21 Updated requirements in main README 2014-10-29 05:56:05 -04:00
snipe 1680d45163 Updated composer lock 2014-10-29 05:38:03 -04:00
snipe 5e1f61ea2a Ignore new uploads subdirectories 2014-10-29 05:37:48 -04:00
snipe 75d0136d7f Merge pull request #304 from madd15/feature-avatars
Feature local avatars and model images
2014-10-29 05:08:49 -04:00
madd15 30428cb858 Vars for lang - Image 2014-10-29 17:34:47 +10:30
madd15 26ab53c02c Added Location and Website 2014-10-29 17:33:10 +10:30
madd15 d8815b5ef5 Change to Vars for labels 2014-10-29 17:32:19 +10:30
madd15 e8737b156a Add image and avatar lang vars 2014-10-29 17:19:27 +10:30
madd15 f200e81334 Remove circle effect from image 2014-10-29 15:58:39 +10:30
madd15 03f3294873 Add images to models
Appropriate changes to views and controllers
includes migration and folder
2014-10-29 15:54:55 +10:30
madd15 da70d5042c uploads\avatars folder 2014-10-29 14:43:27 +10:30
madd15 239370058c changed to avatar 2014-10-29 14:28:24 +10:30
madd15 0b4c1b8d83 Feature Avatars
Appropriate changes to views and controllers
User Avatar Migration
Add intervention/image
2014-10-29 14:18:54 +10:30
snipe 736c9a1ad7 Merge pull request #302 from madd15/patch-2
Change Locations to sort by name asc
2014-10-28 02:05:05 -04:00
madd15 fb46a04d78 Change Locations to sort by name asc 2014-10-28 16:01:11 +10:30
snipe e9e332e19e Merge pull request #300 from madd15/patch-2
Change paginate to get for Index
2014-10-27 15:19:33 -04:00
madd15 16420dafa9 Change paginate to get for Index
Fixes #261 - Pagination appears to work correctly after this change
2014-10-27 12:14:55 +10:30
rob 22f81912e4 Fixes #299 2014-10-24 17:08:50 -04:00
snipe e61f93deb8 Merge pull request #297 from madd15/patch-4
Changed rtd_location_id for edit to match create
2014-10-24 16:38:18 -04:00
snipe 8d2b09a647 Merge pull request #298 from madd15/patch-1
Changed to == on $setting->qr_code
2014-10-24 12:11:36 -04:00
madd15 b11a0cb436 Changed to == on $setting->qr_code 2014-10-25 00:27:25 +10:30
madd15 dd923984ef Changed rtd_location_id for edit to match create
Forgot to change postEdit
2014-10-24 15:17:07 +10:30
snipe d36c73550b Merge pull request #296 from madd15/patch-4
Change rtd_location_id to allow no location set
2014-10-24 00:24:05 -04:00
madd15 bbfc87dddf Change rtd_location_id to allow no location set 2014-10-24 14:50:27 +10:30
snipe 90497bb6a4 Updated spanish translations 2014-10-23 23:37:15 -04:00
snipe 35ce00f6ab Merge remote-tracking branch 'origin/master' into develop 2014-10-23 23:22:51 -04:00
snipe 3cbd8c9b5d Added CrowdIn link 2014-10-23 23:22:35 -04:00
snipe 599520dff6 Squashed commit of the following:
commit 309772e67d
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 22:43:06 2014 -0400

    Fixed operator for setting bool display

commit 139a0a2e2a
Merge: 88459b7 14a9500
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 22:39:55 2014 -0400

    Merge branch 'develop' of github.com:snipe/snipe-it into develop

commit 88459b755f
Merge: 05fb2a4 e17de3d
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 22:39:22 2014 -0400

    Merge branch 'madd15-patch-2' into develop

commit e17de3d34f
Merge: 062d16b a6cd7c0
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 22:38:11 2014 -0400

    Merge branch 'patch-2' of github.com:madd15/snipe-it into madd15-patch-2

commit a6cd7c0bab
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 13:07:57 2014 +1030

    Changed to only one date for checkout date

commit 062d16b2c0
Merge: 05fb2a4 7f44d6d
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 22:22:55 2014 -0400

    Merge branch 'patch-2' of github.com:madd15/snipe-it into madd15-patch-2

commit 7f44d6dd47
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:48:04 2014 +1030

    Add display_checkout_date to settings table

commit 9125def119
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:41:25 2014 +1030

    Add checkout_date

commit 024972e239
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:35:42 2014 +1030

    Add Checkout Date column

commit b0e1ef6024
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:32:44 2014 +1030

    Add Display Checkout Date

commit d74bddb51f
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:32:06 2014 +1030

    Add display_checkout_date

commit 189523a943
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:30:51 2014 +1030

    Add Display Checkout Date Setting

commit 98b96d372d
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 12:29:58 2014 +1030

    Add Display Checkout Date Setting

commit 14a9500c6f
Merge: 7bea50f 0e9e659
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 20:12:45 2014 -0400

    Merge pull request #293 from madd15/patch-5

    Update Country, Change to Form::, Change state to max 32

commit 0e9e659dc4
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 10:40:14 2014 +1030

    Alter State to varchar(32) in Suppliers Table

commit 7bea50feca
Merge: 05fb2a4 fafd03f
Author: snipe <snipe@snipe.net>
Date:   Thu Oct 23 20:08:31 2014 -0400

    Merge pull request #294 from madd15/patch-4

    Added DB Prefix to Migrations

commit 8f582f29e2
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 10:17:19 2014 +1030

    Change state to max 32

commit cbf23fc455
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 24 10:16:08 2014 +1030

    Update Country, Change to Form::

commit fafd03f00f
Author: madd15 <joshdeakin@adam.com.au>
Date:   Thu Oct 23 11:47:46 2014 +1030

    Added DB Prefix to Migrations

    Added DB::getTablePrefix(); to migrations to fix #284

    not sure if there is another way to achieve this

commit 05fb2a434c
Merge: 255b618 e43a945
Author: snipe <snipe@snipe.net>
Date:   Sun Oct 19 18:47:12 2014 -0400

    Merge pull request #283 from madd15/patch-3

    Update table.action to table.actions

commit e43a94586b
Author: madd15 <joshdeakin@adam.com.au>
Date:   Mon Oct 20 08:46:27 2014 +1030

    Update table.action to table.action

commit 0d12428140
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 17 15:39:58 2014 +1030

    Reordered, Changed title and added per_page

    Reordered, Changed title and added per_page

commit 692c861594
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 17 15:36:31 2014 +1030

    Updated static text to Lang vars

    Updated static text to Lang vars

commit c68d76870e
Author: madd15 <joshdeakin@adam.com.au>
Date:   Fri Oct 17 15:35:56 2014 +1030

    Change all form to use Form:: & update Lang

    Updated <input> to Form::
    Update static text to Lang Vars
2014-10-23 22:46:20 -04:00
snipe 309772e67d Fixed operator for setting bool display 2014-10-23 22:43:06 -04:00
snipe 139a0a2e2a Merge branch 'develop' of github.com:snipe/snipe-it into develop 2014-10-23 22:39:55 -04:00
snipe 88459b755f Merge branch 'madd15-patch-2' into develop 2014-10-23 22:39:22 -04:00
snipe e17de3d34f Merge branch 'patch-2' of github.com:madd15/snipe-it into madd15-patch-2 2014-10-23 22:38:11 -04:00
madd15 a6cd7c0bab Changed to only one date for checkout date 2014-10-24 13:07:57 +10:30
snipe 062d16b2c0 Merge branch 'patch-2' of github.com:madd15/snipe-it into madd15-patch-2 2014-10-23 22:22:55 -04:00
madd15 7f44d6dd47 Add display_checkout_date to settings table 2014-10-24 12:48:04 +10:30
madd15 9125def119 Add checkout_date 2014-10-24 12:41:25 +10:30
madd15 024972e239 Add Checkout Date column 2014-10-24 12:35:42 +10:30
madd15 b0e1ef6024 Add Display Checkout Date 2014-10-24 12:32:44 +10:30
madd15 d74bddb51f Add display_checkout_date 2014-10-24 12:32:06 +10:30
madd15 189523a943 Add Display Checkout Date Setting 2014-10-24 12:30:51 +10:30
madd15 98b96d372d Add Display Checkout Date Setting 2014-10-24 12:29:58 +10:30
snipe 14a9500c6f Merge pull request #293 from madd15/patch-5
Update Country, Change to Form::, Change state to max 32
2014-10-23 20:12:45 -04:00
madd15 0e9e659dc4 Alter State to varchar(32) in Suppliers Table 2014-10-24 10:40:14 +10:30
snipe 7bea50feca Merge pull request #294 from madd15/patch-4
Added DB Prefix to Migrations
2014-10-23 20:08:31 -04:00
madd15 8f582f29e2 Change state to max 32 2014-10-24 10:17:19 +10:30
madd15 cbf23fc455 Update Country, Change to Form:: 2014-10-24 10:16:08 +10:30
madd15 fafd03f00f Added DB Prefix to Migrations
Added DB::getTablePrefix(); to migrations to fix #284

not sure if there is another way to achieve this
2014-10-23 11:47:46 +10:30
snipe 05fb2a434c Merge pull request #283 from madd15/patch-3
Update table.action to table.actions
2014-10-19 18:47:12 -04:00
madd15 e43a94586b Update table.action to table.action 2014-10-20 08:46:27 +10:30
madd15 0d12428140 Reordered, Changed title and added per_page
Reordered, Changed title and added per_page
2014-10-17 15:39:58 +10:30
madd15 692c861594 Updated static text to Lang vars
Updated static text to Lang vars
2014-10-17 15:36:31 +10:30
madd15 c68d76870e Change all form to use Form:: & update Lang
Updated <input> to Form::
Update static text to Lang Vars
2014-10-17 15:35:56 +10:30
snipe 255b618559 Updated for 1.0 release 2014-10-16 18:40:31 -04:00
snipe bf1e075551 Fixes #87 - made datatables responsive 2014-10-16 17:46:47 -04:00
snipe 7c97477a53 Added default location to physical hardware 2014-10-16 17:41:56 -04:00
snipe ee7d9bec70 Fixes #279 - QR checkbox not staying checked 2014-10-16 15:39:05 -04:00
snipe 58a99a2219 Merge pull request #246 from mrshu/patch-2
fix: Use the old asset tag when cloning
2014-09-10 11:51:31 -04:00
Marek Šuppa a829fcf52f fix: Use the old asset tag when cloning
* Removed previous use of placeholder, added code to set the old asset tag as the new one since it is being checked for uniqueness.
* For more rationale see our discussion with @technogenus in #241
2014-09-10 08:55:57 +02:00
snipe af450c9bcc Merge pull request #241 from mrshu/patch-1
views: Added old tag to placeholder when cloning
2014-09-08 14:25:32 -04:00
snipe 5575567726 Merge pull request #235 from rrdial/patch-1
Spelling correction.
2014-09-08 14:25:01 -04:00
snipe 97724a95e3 Merge pull request #242 from mizar1616/spanish
Spanish Translation 1.0
2014-09-08 14:23:15 -04:00
mizar1616 2bf57febd8 Spanish Translation
Ver 1.1
2014-09-08 15:19:35 +02:00
Marek Šuppa a82a35d921 views: Added old tag to placeholder when cloning
* Added the old tag to the placeholder attribute of the `asset_tag` input to improve the UX.
2014-09-08 14:45:06 +02:00
R Ryan Dial 94ead64386 Spelling correction. 2014-09-02 14:57:27 -04:00
snipe e978038cc0 Merge pull request #234 from burlito/typo-fix
fix typo: artisand -> artisan
2014-09-01 23:53:31 -04:00
Andrej Manduch 96e40d72b9 fix typo: artisand -> artisan
In few app.example.php config files was artisand instead artisan.

Signed-off-by: Andrej Manduch <amanduch@gmail.com>
2014-09-02 05:47:12 +02:00
snipe 284154a2d7 Merge remote-tracking branch 'origin/master' into develop 2014-08-17 04:38:19 -04:00
snipe e0da7ffe9b Updated composer lock 2014-08-17 04:38:01 -04:00
snipe 35479bfa40 Removed pre-update-cmd 2014-08-17 04:37:38 -04:00
snipe 582a9bf78b Make location_id nullable 2014-08-17 04:37:30 -04:00
snipe e8c197c6a5 No upper cap to password 2014-08-15 23:10:41 -04:00
snipe ec658b0cf5 Stronger input validation 2014-08-15 22:13:09 -04:00
snipe 68d44e65d3 XSS filtering 2014-08-15 22:12:48 -04:00
snipe 6fd0cbe044 Allow up to 50-character password 2014-08-15 21:02:41 -04:00
snipe 4629875bdf Removed ID from listbox debugging 2014-08-15 20:16:19 -04:00
snipe 297f986c4b Merge remote-tracking branch 'origin/develop' 2014-08-15 20:11:00 -04:00
snipe ccb49f4b23 I don't know why I have to keep committing this file 2014-08-15 20:10:52 -04:00
snipe 49c5607e1c Fixes #222 2014-08-15 20:05:48 -04:00
snipe 13f61e1e8a Newline 2014-08-15 19:46:10 -04:00
snipe cd4e994ef5 Removed erronerous "All the users!" text 2014-08-15 19:27:35 -04:00
snipe 974fb6b9ae Added language string for #222 2014-08-15 19:15:00 -04:00
snipe 4365659a2c Removed IDE files 2014-08-13 15:45:29 -04:00
snipe 363d084bd6 Fixed DB name in travis CI 2014-08-12 15:06:52 -04:00
snipe 69cd6404f5 Merge remote-tracking branch 'origin/develop' 2014-08-12 14:58:48 -04:00
snipe 229291999f ... 2014-08-12 14:58:40 -04:00
snipe 2cb61ab1c5 Added link to @GR360RY's Ubuntu Ansible/Vagrant 2014-08-12 14:57:53 -04:00
snipe 652f1c746c Fixes Travis CI build 2014-08-12 14:53:51 -04:00
snipe caaa96ec67 ... 2014-08-12 14:39:59 -04:00
snipe db49e570fd ... 2014-08-12 14:03:29 -04:00
snipe e242f8da0d Merge pull request #216 from cordeos/develop
A0.4.3 add asset, license, user, model clone and artisan db migration
2014-08-12 13:52:51 -04:00
Cordeos Team 8d3d8c86b1 A0.4.3 add asset, license, user, model clone and artisan db migration 2014-08-12 17:25:07 +09:00
Cordeos Team 144c9eb7f8 A0.4.2: asset, license, user, model cloning and move db migration 2014-08-12 15:54:12 +09:00
Cordeos Team 9ccc7d756f A0.4.2: Added asset, license, user, model cloning and fixed some bugs. 2014-08-12 14:28:41 +09:00
Cordeos Team b1fafa7ca4 Added asset, license, user and model cloning 2014-08-12 13:41:17 +09:00
Cordeos Team 62a3ed671b DB migration, layout and clone fixes 2014-08-11 20:18:45 +09:00
Cordeos Team 7ac06fa004 DB migration script Rev 0.11 2014-08-11 17:45:11 +09:00
Cordeos Team 54c3dfbcce Added licence clone 2014-08-11 17:19:28 +09:00
Cordeos Team 0dfc9660ca alpha 0.4.0 release candidate 2014-08-11 17:13:43 +09:00
Cordeos Team d6404dc734 0.4.1 release candidate 2014-08-11 13:32:24 +09:00
Cordeos Team d482b1ea69 0.4.0 release candidate 2014-08-11 13:22:42 +09:00
Cordeos Team 8c7be8fbb3 0.4.0 release candidate 2014-08-11 13:15:08 +09:00
Cordeos Team 3944ebddf3 0.4.0 release candidate 2014-08-11 13:14:22 +09:00
Cordeos Team 6cfb4ab5d8 0.4.0 release candidate 2014-08-11 13:10:00 +09:00
Cordeos Team 64abdddccb Database migration update 2014-08-10 22:59:57 +09:00
Cordeos Team 23a64cfe26 Database migration update 2014-08-10 22:35:14 +09:00
Cordeos Team 448b54d8e2 DBUpdate 2014-08-10 22:08:38 +09:00
Cordeos Team 72671d3e1d initial beta .4 draft 2014-08-10 20:01:38 +09:00
Cordeos Team efbe7b0fee initial beta .4 draft 2014-08-10 19:58:23 +09:00
Cordeos Team af083570a0 initial beta .4 draft 2014-08-10 19:09:08 +09:00
Cordeos Team 87c2a4c9c0 initial commit 2014-08-10 16:42:40 +09:00
root cb8369e9de Initial QA Patch to fix errors
Add License
Add Asset
Show Deleted Users
License Assign
2014-08-08 00:03:12 +09:00
root 6c5bbec44e Changes:
Updated user create to make LOCATION required - same as user edit
Own account password change updated to require only 6-12 characters
2014-08-05 14:56:37 +09:00
root d71a02982d Changes:
set new user create to active YES as default
Changed user password validation to 6-12 characters
Licenses controller removed asset_id from the INSERT column list
2014-08-05 13:36:47 +09:00
root b8a4c7e4d6 Initial COMMIT to set the file modified tracking. No changes made. 2014-08-05 13:22:08 +09:00
snipe 8197e6174e Fixes #200 2014-07-22 17:57:50 -04:00
snipe 3de929bb28 Merge pull request #199 from buzzedword/feature/gitignore-bin
Add bin directory to gitignore so vendor assets don't get committed.
2014-07-22 15:10:57 -04:00
snipe f2036e0951 Merge pull request #197 from jarnokurlin/develop
user id 3 does not exists!
2014-07-22 14:54:42 -04:00
Danny Garcia 213e313e45 Add bin directory to gitignore so vendor assets don't get committed. 2014-07-22 14:52:08 -04:00
snipe a695d8513c Merge pull request #198 from buzzedword/feature/bug-58
Fixed #58
2014-07-22 14:24:02 -04:00
Danny Garcia 69fca5d1ed Fixed #58
Added module scoping for all components, and stored lookups in event handlers instead
of querying live. Delays would erroniously cause values to persist.
2014-07-22 13:33:58 -04:00
Jarno Kurlin 94daba9bab user id 3 does not exists! 2014-07-22 18:53:45 +03:00
snipe 56d619b218 Fixed missing language string 2014-07-19 18:10:10 -04:00
snipe dc1033025a Fixed #151 - more complete translations 2014-07-19 17:53:04 -04:00
snipe a4b220d6c3 Fixed #191 - allow admins to assign a user during asset creation 2014-07-19 17:28:51 -04:00
snipe a8c2955218 Updated changelog 2014-07-19 01:30:24 -04:00
snipe 03cd9f893c Better asset dropdown to include user it's assigned to where applicable 2014-07-19 01:16:53 -04:00
snipe 4f04b96a13 Adding 5.6 back in 2014-07-18 23:55:44 -04:00
snipe d2d85436ad Tweaks to travis 2014-07-18 23:54:11 -04:00
snipe 85cd343bb6 Tried again - tweaking travis settings 2014-07-18 23:51:49 -04:00
snipe 6f6b98186e NFC why this is suddenly failing :( 2014-07-18 23:48:43 -04:00
snipe f855be45aa Removed 5.6 2014-07-18 23:45:01 -04:00
snipe ddf946ad5e Tried to fix failing build caused by travis composer 2014-07-18 23:42:36 -04:00
snipe 4a13cded60 Fixed path for Travis composer 2014-07-18 23:40:57 -04:00
snipe 27e43eebcb Added asset tag to license index view 2014-07-18 23:29:05 -04:00
snipe 196dbb856e Merge remote-tracking branch 'origin/develop' 2014-07-18 23:25:48 -04:00
snipe 3d91a43554 Added asset tag to asset list 2014-07-18 23:25:33 -04:00
snipe 9df9371ffe Merge remote-tracking branch 'origin/develop' 2014-07-18 20:25:12 -04:00
snipe ca24ef1cd8 Merge branch 'refs/heads/feature/add-asset-to-software' into develop
Conflicts:
	app/controllers/admin/LicensesController.php
2014-07-18 20:24:51 -04:00
snipe ac4c905c42 Fixed #140 2014-07-18 20:09:58 -04:00
snipe 7f48ca04b0 Updated changelog 2014-07-17 14:01:04 -04:00
snipe 39fff92bdb Updated changelog 2014-07-17 13:57:58 -04:00
snipe 43b98eff92 Merge remote-tracking branch 'origin/develop' 2014-07-17 13:54:10 -04:00
snipe e203fc516f Fixes #194 2014-07-17 13:51:58 -04:00
snipe ff61705a8f More edits for #140 2014-07-17 13:38:53 -04:00
snipe aa8bbb88e9 Updated MVC for #140, ability to assign software to asset (incomplete) 2014-07-17 08:33:30 -04:00
snipe 7d04ded02a DB migration for #140 2014-07-17 08:32:11 -04:00
snipe 04ea0528a8 Merge remote-tracking branch 'origin/develop' 2014-07-16 18:21:42 -04:00
snipe 89000440f3 Added admin ability to unsuspend locked accounts #changelog 2014-07-16 18:21:22 -04:00
snipe 73b469621f Merge remote-tracking branch 'origin/develop' 2014-07-13 07:35:28 -04:00
snipe e94087d62b Changed country to dropdown 2014-07-11 19:12:53 -04:00
snipe 21251b4623 Fixed #190, where depreciation on licenses would not be saved 2014-07-09 21:12:58 -04:00
snipe 381f99543f Merge remote-tracking branch 'origin/develop' 2014-07-09 11:58:47 -04:00
snipe 6f2b78ad9f Added DB migration for #15 2014-07-09 11:50:54 -04:00
snipe 6043321bed Added checkbox for marking an asset as requestable 2014-07-09 10:35:15 -04:00
snipe 03dffa3541 Added language string for requestable field 2014-07-09 10:34:54 -04:00
snipe 99003e126a Fixed bug with edit (supplier_id), added requestable field for assets 2014-07-09 10:34:39 -04:00
snipe 82a84310ed Added links to documentation and issues in footer 2014-07-09 08:45:11 -04:00
snipe 9dc40a3a9c Fixed a few CSS quirks 2014-07-09 08:45:02 -04:00
snipe 784f7e8dec WTF is wrong with me this morning? Hurr. 2014-07-09 07:08:15 -04:00
snipe deb9f42ec8 Added gitbook.io badge 2014-07-09 07:07:22 -04:00
snipe beb4d6500a Added back the line separators 2014-07-09 07:05:47 -04:00
snipe 45fa66b41e Updated copy 2014-07-09 07:03:10 -04:00
snipe 8eb2523293 Updated README to point to new documentation URL/branch 2014-07-09 07:02:08 -04:00
snipe 2b9494ed82 Fixed inconsistent password requirements across admin user creation, user profile and forgotten password 2014-07-09 05:18:53 -04:00
snipe 66c9264fcb More consistent spacing on forgotten password page (col-md-3) 2014-07-09 05:11:53 -04:00
snipe f22f23f21d Fixed button label on forgotten password page 2014-07-09 05:10:57 -04:00
snipe 90eb883b67 Fixed #187 - replaced dummy site name with site name from admin panel preferences 2014-07-09 05:04:49 -04:00
snipe 300358bc46 Link to demo 2014-07-09 03:38:09 -04:00
snipe 3cffff3415 Added "still maintained" badge 2014-07-09 03:22:02 -04:00
snipe 4253744d9d Merge remote-tracking branch 'origin/develop' 2014-07-09 03:10:42 -04:00
snipe 6b5a552d95 Fixed #186 - Bug with select box not retaining selections on edit screen 2014-07-09 03:09:55 -04:00
snipe 43485e1a51 Fixed language definition 2014-07-09 02:50:58 -04:00
snipe 5cdceb1db1 Merge remote-tracking branch 'origin/develop' 2014-07-08 19:55:03 -04:00
snipe 0c285d64bc Tightened up Bootstrap 3 design, took care of leftover elements from 2.3 2014-07-08 19:47:19 -04:00
snipe 38cdbf8804 Added submit button language file 2014-07-08 19:44:17 -04:00
snipe 9606834e14 Made password length match 10 character standard imposed by admin user creation 2014-07-08 19:44:06 -04:00
snipe 276b1553c1 Mark required form elements as required 2014-07-08 19:17:31 -04:00
snipe 182f2bfa62 Merge remote-tracking branch 'origin/develop' 2014-07-08 05:38:27 -04:00
snipe 5266d9a30a Fixed title tag for user list view 2014-07-08 05:38:07 -04:00
snipe 6068a940bd Added employee number to user view, per #183 2014-07-08 04:44:36 -04:00
snipe fc032c95b4 Misc indenting 2014-07-08 04:40:09 -04:00
snipe 2265b9321e Additional language strings for #151 2014-07-08 04:40:01 -04:00
snipe a7b809cb91 Updated title/header for users, additional fixes for #151 2014-07-08 04:39:51 -04:00
snipe af269b536e Refactored users to use same form for edit/create 2014-07-08 04:35:23 -04:00
snipe 89a8f3c277 Fixes #183 - added employee number to users 2014-07-08 02:57:15 -04:00
snipe 57b3f6c44f Security recommendations 2014-07-07 13:58:51 -04:00
snipe 380ff9917d Updated changelog for newest release 2014-07-02 22:12:06 -04:00
snipe 6532f1aba5 Merge remote-tracking branch 'origin/develop' 2014-07-02 22:08:38 -04:00
snipe 49dfd259b4 Props where props are due, yo. 2014-07-02 22:05:09 -04:00
snipe fad2ec66fa More README fixes and refinement 2014-07-02 22:00:45 -04:00
snipe e455de65f9 Added table of contents outline to README for easier readability 2014-07-02 21:54:33 -04:00
snipe 7a0c1cc6ff Streamlined README for easier production install, developer instructions moved to the bottom. 2014-07-02 21:43:37 -04:00
snipe 2517359f3a Merge remote-tracking branch 'origin/develop' 2014-07-02 14:58:27 -04:00
snipe c782f29f5a Pulled account sub-nav into top menu to free up UI space. #changelog 2014-07-02 13:56:12 -04:00
snipe 2825bcb431 Fixed #179 - account page to show assets checked out to the logged in user 2014-07-02 13:55:34 -04:00
snipe 1117c2958a Slightly nicer output 2014-06-27 20:17:42 -04:00
snipe 4464c221b7 Better changelog 2014-06-27 19:05:13 -04:00
snipe 21e6f2c249 Merge remote-tracking branch 'origin/develop' 2014-06-27 17:28:22 -04:00
snipe ec9eab81bf Closes #177 - change log file added, needs refining 2014-06-27 17:27:53 -04:00
snipe b863bd2c53 Merge remote-tracking branch 'origin/develop' 2014-06-26 13:28:41 -04:00
snipe 09ca2be28d Merge pull request #175 from leostat/develop
Fix for ~167
2014-06-26 13:28:12 -04:00
leostat a170efae27 fix for #167 2014-06-26 11:10:13 +01:00
leostat 919119c5b9 Sanatise fullname class #167 2014-06-26 10:45:31 +01:00
snipe 7cb0db5b83 Added local flag for easier install 2014-06-25 04:24:13 -04:00
snipe d24c4ed7eb Widened column for nicer layout 2014-06-24 18:22:26 -04:00
snipe a0f7083e9c Improved bootstrap nav for mobile 2014-06-24 18:04:15 -04:00
snipe 8b914102b7 Updated spacing for PSR-2 2014-06-24 17:49:39 -04:00
snipe 77f4848d6e Missed a few 2014-06-24 17:45:21 -04:00
snipe afabd28142 Additional fixes for #167 2014-06-24 16:28:20 -04:00
snipe 67d23bdb45 Janky url validator to add http:// 2014-06-24 15:51:13 -04:00
snipe 124acc89c4 Resolves #174 2014-06-24 14:15:39 -04:00
snipe 5c0e912080 Merge remote-tracking branch 'origin/develop' 2014-06-23 23:52:51 -04:00
snipe 47a8363f1d Merge pull request #173 from madd15/develop
Remove Double 'Display QR Codes' and change triple =
2014-06-23 23:48:15 -04:00
snipe b09aec67f1 Fixes #155 2014-06-23 23:46:14 -04:00
snipe 86400f435b New list URL 2014-06-23 23:36:13 -04:00
madd15 82f0c6f00c Change to Triple = and no ' 2014-06-24 13:02:13 +09:30
madd15 9fe76a21f6 Remove double 'Display QR Codes' 2014-06-24 12:46:37 +09:30
snipe 264a6e304d Link to mailing list 2014-06-23 23:12:45 -04:00
snipe ad62ec7540 Merge pull request #172 from madd15/develop
Check boxes in settings not showing correct settings
2014-06-23 23:06:25 -04:00
madd15 3b9f96419b Check boxes not showing correct settings 2014-06-24 12:31:43 +09:30
snipe 6b8ed1a1ad Merge pull request #171 from madd15/develop
Change Back Button Icon on Update Supplier
2014-06-23 22:44:06 -04:00
madd15 164ca5f77b Change Back Button Icon on Update Supplier 2014-06-24 12:12:45 +09:30
snipe a142afe177 Oops. Forgot zip code. 2014-06-23 22:38:19 -04:00
snipe 15d48c903e Helpful links for hardware view 2014-06-23 22:33:42 -04:00
snipe cbd3420327 Better phrasing 2014-06-23 22:23:14 -04:00
snipe 380417953b Fixed missing string 2014-06-23 22:17:07 -04:00
snipe 6f42b3045e Resolves #155 2014-06-23 22:09:32 -04:00
snipe b717a3ee34 Merge pull request #170 from madd15/develop
Fix #117
2014-06-23 20:59:16 -04:00
madd15 945cb3253f Fix #117
Fix CCS for #117
#166 also fixed edit user page
2014-06-24 10:25:35 +09:30
snipe 3e7a9e45d1 Fixes PR #168 and #167 2014-06-23 19:40:09 -04:00
snipe 0c9d94deb1 Merge pull request #166 from madd15/develop
No Result on user page not showing correctly
2014-06-23 18:19:41 -04:00
madd15 19de7425e6 Select2.css image paths - #2
Change image paths in select2.css to match correct location of images.
Damn I fail.
2014-06-23 10:37:54 +09:30
madd15 3cc038208f Revert "Select2.css image paths"
This reverts commit a48efb8855.
2014-06-23 10:33:22 +09:30
madd15 a48efb8855 Select2.css image paths
Change image paths in select2.css to match correct location of images.
2014-06-23 10:18:02 +09:30
Josh Deakin 7ddf84adf7 Change Warning to Info User no results 2014-06-21 12:43:18 +09:30
Josh Deakin 57628d725e User No Results
Change No Results on user page to general no results
2014-06-21 12:37:51 +09:30
snipe 45f353e575 Merge remote-tracking branch 'origin/develop' 2014-06-19 22:23:09 -04:00
snipe bef5170b7c More cleanup 2014-06-19 22:19:20 -04:00
snipe e7e75631c5 Merge remote-tracking branch 'origin/bugs/160' into develop 2014-06-19 22:15:44 -04:00
snipe a4c931e4fd Cleanup 2014-06-19 22:14:54 -04:00
snipe d1499deecd Provisions for strict mode, fixed license deletion bug 2014-06-19 22:13:59 -04:00
snipe b365ada0ce Provisions for strict mode 2014-06-19 22:13:43 -04:00
snipe 185ffd3df1 Merge remote-tracking branch 'origin/develop' 2014-06-19 18:27:59 -04:00
snipe 5fdb61589f Linked to Waffle in text 2014-06-19 18:20:14 -04:00
snipe b3292c3a35 Updated README with waffle.io info 2014-06-19 18:14:21 -04:00
snipe 4ce1c94bc3 Merge remote-tracking branch 'origin/master' into develop 2014-06-19 16:10:27 -04:00
snipe 08d05b481f Fixes #158 2014-06-19 16:10:00 -04:00
snipe 7742bb30c1 Merge remote-tracking branch 'origin/develop' 2014-06-18 16:04:46 -04:00
snipe bf3062f4ef Merge pull request #160 from exula/issue-125-license-seat
Improve License Editing to add or delete seats
2014-06-18 15:48:52 -04:00
Brad Coudriet 8f744e02fd Improve License Editing to add or delete seats 2014-06-18 13:39:54 -04:00
snipe ba3e56df9a Merge remote-tracking branch 'origin/develop' 2014-06-17 14:07:32 -04:00
snipe 8aebaaf502 Few more localization strings for #151 2014-06-10 04:15:50 -04:00
snipe c937796224 More language strings, per #151 2014-06-09 21:30:58 -04:00
snipe 74653d3480 More refactoring of language structure, language strings per #151 2014-06-09 19:56:47 -04:00
snipe 65d298a557 Merge branch 'develop' of github.com:snipe/snipe-it into develop
Conflicts:
	.travis.yml
2014-06-09 18:34:58 -04:00
snipe 04f23ef792 Encrypted slack API token after regen 2014-06-09 18:27:16 -04:00
snipe 507b6e5128 Merge pull request #154 from buzzedword/patch-2
Update .travis.yml
2014-06-09 18:22:21 -04:00
Danny Garcia 4271f5265d Update .travis.yml
Added composer validation to fail fast on travis build instead of going directly to install.
2014-06-09 18:19:22 -04:00
snipe d9a8ede623 Merge pull request #153 from buzzedword/patch-1
Update composer.json
2014-06-09 18:06:03 -04:00
Danny Garcia 117ed3294b Update composer.json
Modified composer.json away from laravel defaults to be more on brand.

- Specified support locations
  - Issues
  - Source
- Added license shorthand as defined by http://spdx.org
- Added homepage
- Added keywords
- Added name
- Added description
2014-06-09 18:03:08 -04:00
snipe cd0f42c508 Alphabetized language files, more for #151, refactoring language string hierarchy. 2014-06-09 17:45:52 -04:00
snipe 2fe66c1bdc More internationalization per #151 2014-06-09 16:01:44 -04:00
snipe dae3220f2b Nicer formatting when 0 years 2014-06-09 02:52:19 -04:00
snipe 7448db6334 Notes on feature branches 2014-06-09 02:04:21 -04:00
snipe 2f90069792 Updated to request translation files 2014-06-09 01:59:03 -04:00
snipe ffab309940 More internationalization improvements per #151 2014-06-09 01:47:05 -04:00
snipe 34b4615f4e Additional internationalization for Hardware per #151 2014-06-09 01:12:08 -04:00
snipe 75590b6852 More internationalization strings for #151 2014-06-09 00:49:52 -04:00
snipe 610c405aa5 More fixes for #151 2014-06-09 00:25:45 -04:00
snipe 94016678f8 More internationalization per #151 2014-06-09 00:02:31 -04:00
snipe dc256583d4 Fixes #144 2014-06-06 13:40:54 -04:00
snipe cbc945299e Fixes #148 2014-06-05 13:39:14 -04:00
snipe 607484b294 Fixes #141 for mysql with strict mode enabled 2014-05-27 19:51:24 -04:00
snipe 9714178ce1 Fixes #141 2014-05-24 05:48:50 -04:00
snipe 46afc0cc73 Merge remote-tracking branch 'origin/develop' 2014-05-12 20:30:30 -04:00
snipe dfa5d1c642 Fixes #136 2014-05-12 18:06:49 -04:00
snipe e463b28b37 Deliberately vague message when user account doesn't exist 2014-05-06 16:16:19 -04:00
snipe 9db6605085 Smaller name requirement, to allow for folks like AJ Li, etc. 2014-05-06 16:08:05 -04:00
snipe d57955c7e7 Enforce password requirements on password reset 2014-05-06 16:07:00 -04:00
snipe ad9b1fc446 Fixes #139 2014-05-06 16:06:43 -04:00
snipe 3518ab9f55 Fixes #139 2014-05-06 16:06:22 -04:00
snipe cd8ebbd7b6 Merge remote-tracking branch 'origin/develop' 2014-05-06 05:29:03 -04:00
snipe c2ed0f6b21 More auth refinement 2014-05-06 04:26:21 -04:00
snipe f4c5da57d0 Fixed auth error when user isn't logged in 2014-05-06 04:17:46 -04:00
snipe 8a7f71ec56 Better handling of non-admin roles 2014-05-06 04:14:14 -04:00
snipe b627c5cfbf More translation substitutions 2014-05-06 03:39:02 -04:00
snipe c21c3a4a9e Removed erroneous PDF option 2014-05-06 03:38:51 -04:00
snipe d29e30bcf6 Translations 2014-05-06 03:38:05 -04:00
snipe 1cf42932fb Added currency symbol 2014-05-06 03:35:47 -04:00
snipe fee5fcf608 Titles for Group admin 2014-05-06 03:35:34 -04:00
snipe e512b8f7ef Removed hardcoded USD symbol 2014-05-06 03:35:03 -04:00
snipe 5e1fedda45 Added Travis-CI badge 2014-05-06 01:28:21 -04:00
snipe d0789da472 Copy the mail config file to prod for CI build as well. Duh. 2014-05-06 01:22:01 -04:00
snipe fc21fc1aab Put prod database file in app/config 2014-05-06 01:13:24 -04:00
snipe 24ae1b948f Updated username so the DB can be created
(travis CI doesn't allow custom DB usernames)
2014-05-06 01:07:16 -04:00
snipe f1cc547151 Copy DB config file for travis 2014-05-06 01:02:49 -04:00
snipe 594212db6b Merge remote-tracking branch 'origin/develop' 2014-05-06 00:50:05 -04:00
snipe ca0f5df63a Reverting Pull Request #138 2014-05-06 00:48:09 -04:00
snipe 57e903a926 Added Diff language string 2014-05-06 00:10:29 -04:00
snipe 06067fc19c Fixed CSV rexport 2014-05-06 00:10:19 -04:00
snipe 67aba76d3f Fixing this change from #109 that somehow got reverted 2014-05-05 23:41:41 -04:00
snipe 96e47612fa Longer default password requirement 2014-05-05 23:40:05 -04:00
snipe 97af71d592 Merge remote-tracking branch 'origin/develop' 2014-05-05 22:19:31 -04:00
snipe 71c0c01430 Updated environmental config for hostname-only 2014-05-05 22:02:32 -04:00
snipe 78f4615d01 Travis CI 2014-05-05 21:59:23 -04:00
snipe d93639d480 Updated README for 4.1 2014-05-05 21:38:18 -04:00
snipe 70fb2d98a0 Merge remote-tracking branch 'origin/develop'
Conflicts:
	.gitignore
2014-05-05 21:36:16 -04:00
snipe 34960e26cc Merge pull request #138 from refinery29/qr-codes-ftw
Add site setting to define text below asset QR codes
2014-05-05 21:31:45 -04:00
snipe 3473ef5fa6 Updated to Laravel 4.1 2014-05-05 21:28:58 -04:00
snipe 2fc197623a Update to Laravel 4.1 2014-05-05 21:21:56 -04:00
snipe 7bc681dfef Bumped to laravel 4.1 2014-05-05 20:38:02 -04:00
Jake McGraw af603f0380 make text below asset tags configurable 2014-05-03 20:06:59 +00:00
snipe 1d10425d19 Merge pull request #135 from Zyg0te/develop
Adjusted zip and state field length requirements
2014-04-29 17:21:03 -04:00
Zyg0te 5690e9ce1f Fixed code indentation for Apache virtualhost example
The code indentation was incorrect, borking the syntax highlighting in
the README.
2014-04-24 11:25:55 +02:00
Zyg0te 792e45bd12 Adjusted zip and state field length requirements
State abbreviations, if used at all, are not neccessarily 2 characters
outside the US and zip codes aren't always 5 digits either. It's
probably a good idea not to enforce US centric length requirements to
these fields in the Location model.
2014-04-24 11:20:20 +02:00
snipe 5a798110ad Merge branch 'refs/heads/develop' 2014-04-22 02:26:54 -04:00
snipe ad8b95f07c Merge pull request #130 from FleischKarussel/patch-1
vhost.conf for linux mod_rewrite enabled
2014-04-04 14:07:50 -04:00
FleischKarussel 729f94331f vhost.conf for linux mod_rewrite enabled 2014-04-04 13:17:31 +02:00
snipe faa9b95a6f Merge pull request #128 from FleischKarussel/patch-1
https for getcomposer and github downloads
2014-04-03 15:11:03 -04:00
FleischKarussel 0e633efca2 https for getcomposer and github downloads 2014-04-03 17:10:41 +02:00
snipe b2015e7e08 Merge pull request #127 from domtancredi/develop
Fix for broken datatables in upgrading to v2.2
2014-03-18 17:07:12 -04:00
Dom Tancredi efad5db082 Fix for broken datatables in upgrading to v2.2 2014-03-16 15:00:30 -04:00
snipe 9423c9e9d3 Merge pull request #124 from DGAzr/develop
Adding escape to the dash so that it is a literal rather than a range
2014-02-11 14:31:49 -05:00
Dustan Ashley 366e7d01a3 Adding escape to the dash so that it is a literal rather than a range 2014-02-11 07:24:27 -05:00
snipe 869cd3ea14 Merge branch 'refs/heads/develop'
Conflicts:
	.gitignore
2014-01-28 23:24:07 -05:00
snipe a2f068eb8f Missed one 2014-01-28 20:28:31 -05:00
snipe 580590b4c0 Fixed === to == (PHP is loosely typed) 2014-01-28 20:24:37 -05:00
snipe 5ec87e81b1 Handle logs/views dir better 2014-01-28 18:05:15 -05:00
snipe 60d94eea04 Changes serial to text area, allow new lines. Fixes #118 2014-01-28 17:42:20 -05:00
snipe 78e3171fa6 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2014-01-28 17:41:35 -05:00
snipe d03d692868 Merge pull request #121 from buzzedword/feature/default-directories-in-storage
Added default folder structure for laravel.
2014-01-28 14:37:46 -08:00
Daniel Garcia 4b4eefb4cc Added default folder structure for laravel. 2014-01-28 17:35:57 -05:00
snipe 940cf7b227 Merge pull request #120 from buzzedword/feature/patch-1
Add logs and views folder to repo, omit additional files.
2014-01-28 14:02:35 -08:00
Daniel Garcia 3e47e47add Add logs and views folder to repo, omit additional files. 2014-01-28 16:59:10 -05:00
snipe 8b3f409703 Added logs and views dir 2014-01-28 16:56:33 -05:00
snipe d497a84245 Merge pull request #119 from refinery29/qr-codes-ftw
Added support for QR codes in Asset view
2014-01-28 11:52:22 -08:00
Jake McGraw 01612652b5 use same dimensions in render and display 2014-01-28 05:33:03 +00:00
Jake McGraw 5cb0a1465e added QR code route for asset controller
added QR code inclusion on asset controller getView  action/template
2014-01-28 05:16:57 +00:00
Jake McGraw 55b54e0a42 added sql migration for qr_code in settings table
added baconqr to composer package list
added admin settings page for qr code
2014-01-28 04:25:58 +00:00
snipe a3eb61b307 Cleaned up gitignore 2014-01-14 18:41:07 -05:00
snipe 9e5e9ee96b Minor formatting change 2014-01-14 18:34:36 -05:00
snipe 92347c6707 Full path to avoid any confusion 2014-01-14 18:26:52 -05:00
snipe dc5ea81d34 Backticks are hard. 2014-01-14 18:23:56 -05:00
snipe 9c369d72e1 Fixed typo 2014-01-14 18:20:41 -05:00
snipe ea0560e1ce Updated readme for #116 2014-01-14 18:19:22 -05:00
snipe cd62be0de0 Minor div fix 2014-01-04 18:35:06 -05:00
snipe e186bb115d Merge branch 'refs/heads/develop' 2014-01-04 18:28:05 -05:00
snipe 7a3f6fb714 Merge branch 'refs/heads/feeva-localization' into develop 2014-01-04 18:25:57 -05:00
snipe 1ed761c97f Merge branch 'localization' of github.com:feeva/snipe-it into feeva-localization
Conflicts:
	app/views/backend/layouts/default.blade.php
2014-01-04 18:25:25 -05:00
snipe a193e3118d Merge pull request #113 from feeva/issue58-modal-failure
Issue #58 - Modal stops passing title/message/href through if modal is canceled
2014-01-04 15:09:44 -08:00
feeva e9a675a9a4 Merge branch 'issue58-modal-failure' of https://github.com/feeva/snipe-it into issue58-modal-failure 2014-01-04 18:24:42 +00:00
feeva 439a139d7c Issue #58 - Modal stops passing title/message/href through if modal is
canceled
2014-01-04 18:23:52 +00:00
feeva 40d6ad09ae Hard-coded English text strings should be moved to their language files 2014-01-04 17:55:19 +00:00
mars 3acd88d451 Issue #58 - Modal stops passing title/message/href through if modal is
canceled
2013-12-26 02:51:01 +09:00
snipe 8806208e83 More gracefully handle TokenMismatchException errors 2013-12-13 14:34:09 -05:00
snipe 5ba487d74e Removed CSRF filters - they already run elsewhere 2013-12-13 14:33:42 -05:00
snipe 7c12d5fed0 Merge branch 'refs/heads/develop' 2013-12-12 01:52:59 -05:00
snipe f6717d9f49 Added manager drop down for users, manager name in index 2013-12-12 01:49:38 -05:00
snipe 3ec6df492d Added manager_id to DB, install script 2013-12-12 01:12:42 -05:00
snipe cdddbcf695 Using URL::previous to determine back button address 2013-12-12 00:50:56 -05:00
snipe 483f9b1289 Slight layout tweak for smaller buttons 2013-12-10 17:13:33 -05:00
snipe b2912195ac Added EOL to report view 2013-12-10 14:56:16 -05:00
snipe 19b524e113 Merge branch 'refs/heads/develop' 2013-12-10 13:01:05 -05:00
snipe 1cec46f997 Updated comment, made save on edit return to listing page 2013-12-10 05:17:15 -05:00
snipe 4468cbd096 Fixes #112 2013-12-10 04:48:40 -05:00
snipe 4c3e2a9a68 DB seeding for #112 2013-12-10 03:52:23 -05:00
snipe da9f28fbe7 DB migration for #112 2013-12-10 03:48:20 -05:00
snipe 8ac394bd3f Escaping parens 2013-12-06 12:36:43 -05:00
snipe ff22ee8292 Merge branch 'refs/heads/develop' 2013-12-06 06:40:43 -05:00
snipe d36409d037 Layout improvements 2013-12-06 05:18:04 -05:00
snipe 7e9be9b935 Truncate serial on index view at 40, UX improvements 2013-12-06 05:07:26 -05:00
snipe dd701d253c Set values to null if they are 0 2013-12-06 04:59:24 -05:00
snipe 78b3a0e1f9 Make license fields nullable migration 2013-12-06 04:58:53 -05:00
snipe 1e9a81f59d Cleanup for licenses view page 2013-12-06 04:58:37 -05:00
snipe 432f44118f Fixed FK in licenses model 2013-12-06 04:58:21 -05:00
snipe b697c77cd8 Fixing error when no depreciation 2013-12-06 02:50:14 -05:00
snipe 23ce0a9e67 Missing closing divs 2013-12-06 02:46:38 -05:00
snipe 0cd6c60fd9 Cleaner license UI 2013-12-06 02:41:53 -05:00
snipe 82de635a75 Fixed invalid seed data 2013-12-06 02:10:08 -05:00
snipe 299393aa26 Set the log to track the license_id instead of seat id 2013-12-06 02:09:46 -05:00
snipe 7bc9a53b13 Fixed error when user is invalid 2013-12-06 02:09:23 -05:00
snipe c1bf27fcba Better comments 2013-12-05 22:14:24 -05:00
snipe d72daf14ff Added filename to log seeder for mysql strict mode 2013-12-04 12:14:02 -05:00
snipe c5678414ae Groundwork for #11 2013-12-04 12:13:23 -05:00
snipe 329980c6c6 Unsquished blade for group display 2013-12-04 12:05:37 -05:00
snipe 5fb649b59e Fixed error message layout 2013-12-03 19:26:01 -05:00
snipe 900a441908 Merge branch 'refs/heads/develop' 2013-12-03 13:32:20 -05:00
snipe 19de8a15cf Allow numbers signs, parens 2013-12-03 13:31:42 -05:00
snipe ab99e6a64f Changed alpha_dash to alpha_space for Axure licenses
(which have slashes in them)
2013-12-03 06:50:58 -05:00
snipe 5dc9c30bca Duplicate field 2013-12-03 06:34:05 -05:00
snipe 9308059230 Can't enforce unique here, or it will see itself and fail 2013-12-03 06:26:27 -05:00
snipe ac9dfe2edc DESC? WTF? Hurr. 2013-12-03 06:14:02 -05:00
snipe ae26b683fb Merge branch 'refs/heads/develop' 2013-12-03 06:08:44 -05:00
snipe cf9cd1a7a0 Allow slashes 2013-12-03 06:08:05 -05:00
snipe 194150bbf0 Hacky shit to fix pagination weirdness - will come back to this 2013-12-03 06:02:29 -05:00
snipe b97f5fc043 Serial should be unique for hardware 2013-12-03 05:46:19 -05:00
snipe aaebaa8d36 Bugfix to put custom title in the page title 2013-12-03 05:41:03 -05:00
snipe ef4743ed49 Specifying feature branches 2013-12-03 05:40:44 -05:00
snipe 6737a9a91a Added quick create menu to topnav 2013-12-03 05:14:56 -05:00
snipe 965d329ae5 Order by name in user drop down list 2013-12-03 04:53:07 -05:00
snipe e4fd2373fb Changed table to show asset tag 2013-12-03 04:52:51 -05:00
snipe e784854e05 Changed column from asset name to model 2013-12-03 04:24:34 -05:00
snipe c4c4db201f Changed th sizes 2013-12-03 04:07:54 -05:00
snipe 33df88d434 Only print name if there is one 2013-12-03 04:07:34 -05:00
snipe f02f4341ca Tweaked table layout 2013-12-03 03:40:28 -05:00
snipe 211a527ae1 Fixed FK 2013-12-03 03:40:15 -05:00
snipe cc275367bf Removed close button on 'no results' error 2013-12-03 03:18:39 -05:00
snipe 65e0e4d749 Merge branch 'refs/heads/develop' 2013-12-03 03:15:40 -05:00
snipe 1dc7fd1bdf Fixed depreciation display on asset view 2013-12-03 03:15:02 -05:00
snipe e49420f62d Fixed bug in depreciation display 2013-12-03 03:02:00 -05:00
snipe 9fe8ace39b Order form lists by name 2013-12-03 02:41:16 -05:00
snipe d591c1ea04 Fixed bug where RTD = 1 instead of 0 2013-12-03 02:14:05 -05:00
snipe ae05f27015 Fixed depreciation bug 2013-12-03 02:05:00 -05:00
snipe b8ebcf769a Added model name to listing 2013-12-03 01:28:21 -05:00
snipe 9c718dce59 Removed serial requirement 2013-12-03 01:11:23 -05:00
snipe efb3ed4932 Merge branch 'refs/heads/develop' 2013-12-03 00:20:25 -05:00
snipe e09d250dd6 Removed name as required field 2013-12-03 00:19:22 -05:00
snipe 4d27008c5d Commented out breaking data table 2013-12-03 00:18:59 -05:00
snipe cfd6c4301c Merge pull request #110 from flashingcursor/develop
Started implementing Datatables ... User table partially implemented.
2013-12-02 03:09:10 -08:00
snipe dddba1198d Merge pull request #109 from desmondmorris/report-csv-export
Implements first pass at csv exports of reports
2013-12-02 03:08:57 -08:00
Gil Rutkowski a0267db832 Datatables partially implemented on user table. 2013-12-02 00:21:04 -06:00
Desmond Morris 0d46eba76e Updates csv export link path 2013-12-01 22:23:55 -05:00
Desmond Morris c8e47ad00e Adds fullname of user assigned user to report export 2013-12-01 22:16:16 -05:00
Desmond Morris 47b065208e Implements first pass at csv exports of reports (#35) 2013-12-01 22:04:56 -05:00
snipe edf71a7692 Merge branch 'refs/heads/develop' 2013-12-01 21:44:06 -05:00
snipe 775ec10114 Change cursor so that it doesn't change to a hand if not sortable 2013-12-01 21:43:34 -05:00
snipe d19721d17a Merge pull request #108 from desmondmorris/disable-sortable-actions
Removes sorting from actions column.  Fixes #56
2013-12-01 18:04:22 -08:00
Desmond Morris c11510fdca Removes sorting from actions column. Fixes #56 2013-12-01 20:54:43 -05:00
snipe 1f2b3f8505 Merge pull request #107 from desmondmorris/composer-json-formatting
Fixes composer formatting issues
2013-12-01 17:32:49 -08:00
Desmond Morris fba853a6a7 Fixes composer formatting issues 2013-12-01 20:24:02 -05:00
snipe 1af24827bd Merge branch 'refs/heads/develop' 2013-12-01 19:03:23 -05:00
snipe 392cc8eb44 Grammar is hard. 2013-12-01 19:03:06 -05:00
snipe c21a1ef16b Merge branch 'refs/heads/develop' 2013-12-01 19:00:34 -05:00
snipe 46281dd39b Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-12-01 19:00:08 -05:00
snipe db2c751480 Merge pull request #105 from desmondmorris/develop
Fixes mail config documentation
2013-12-01 15:59:24 -08:00
Desmond Morris c08d672e49 Fixes mail config documentation 2013-12-01 18:50:58 -05:00
snipe 70c446cb35 Baseline test class - nothing good here yet 2013-12-01 18:47:40 -05:00
snipe 2de48f2b91 This gets created by the user 2013-12-01 18:45:47 -05:00
snipe 391334ee13 WTF seriously? 2013-12-01 18:40:46 -05:00
Gil Rutkowski a678ec4171 Fixed Gravatar Hash 2013-12-01 17:11:44 -06:00
snipe ea42a18930 Merge pull request #99 from flashingcursor/develop
Datatables groundwork, minor tweaks.
2013-12-01 13:34:44 -08:00
Gil Rutkowski 36a714b154 Published debug bar assets on install. Moved jQuery & data tables to the head. Added Chumper Datatables. 2013-12-01 15:15:52 -06:00
snipe bee260b1f0 Added filename to log 2013-12-01 06:45:03 -05:00
snipe 45f46466d7 More fleshed out default db for testing 2013-11-30 22:04:33 -05:00
snipe 8f235881c5 Added phpunit in require-dev, removed ardent, as we're not actually using it at this point 2013-11-30 22:04:00 -05:00
snipe 8bf6a34c58 Unit testing conf files 2013-11-30 22:01:59 -05:00
snipe ca591ccb5d One more 2013-11-30 22:01:42 -05:00
snipe 9d22ce5ac7 Added gitignore lines for new conf files 2013-11-30 22:00:44 -05:00
snipe 067ddb9aac Config tweaks 2013-11-30 17:53:21 -05:00
snipe 66bddba6fe Merge branch 'refs/heads/develop' 2013-11-30 17:43:10 -05:00
snipe fbe3ace0dd Ignore configs 2013-11-30 17:42:58 -05:00
snipe b352817407 Ignoring configs 2013-11-30 17:42:15 -05:00
snipe e2dc24d926 Updated readme 2013-11-30 17:39:02 -05:00
snipe baab758377 Fixed language support 2013-11-30 17:30:02 -05:00
snipe d91d75abe4 Fixing JSON parse errors from #97 2013-11-30 17:08:39 -05:00
snipe 12d3d10dec Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-30 17:02:22 -05:00
snipe 7693e83d7f wtf 2013-11-30 17:02:11 -05:00
snipe 3bc35208af Merge pull request #97 from flashingcursor/develop
Making things a bit more environmentally sensitive...
2013-11-30 13:53:49 -08:00
Gil Rutkowski 78d1c0345e Merge remote-tracking branch 'upstream/develop' into develop 2013-11-30 11:52:27 -06:00
Gil Rutkowski ca39b34b8c Automatically run Sentry Migrations and DB migrations after composer install. 2013-11-30 11:36:05 -06:00
Gil Rutkowski 3d1a533bf2 Environmental cleanup. 2013-11-30 11:17:02 -06:00
Gil Rutkowski 71eaeb2783 Updated README for Environments and revised the order of things. 2013-11-29 23:56:48 -06:00
Gil Rutkowski f9d8cb1b93 Made things a bit more environmentally sensitive. 2013-11-29 23:22:48 -06:00
snipe d535adcc5e Merge branch 'refs/heads/develop' 2013-11-29 17:09:07 -05:00
snipe f62ce4039d @uberbrady is a pedantic shitbag. 2013-11-29 17:07:49 -05:00
snipe 8f9a82e400 Prevent user editing on deleted user 2013-11-29 17:05:55 -05:00
snipe 2be6c6f5da Hurr. 2013-11-29 16:57:49 -05:00
snipe 35155dfc4c Prevent user from being deleted if they still have assets assigned to them 2013-11-29 16:57:24 -05:00
Gil Rutkowski 2e82794ab4 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-29 15:45:41 -06:00
snipe 7142e37372 Deleted users 2013-11-29 16:44:48 -05:00
snipe dca7a7f2eb Allow deleted users 2013-11-29 16:39:24 -05:00
snipe a7ca9f0c9f Layout tweaks 2013-11-29 16:36:11 -05:00
Gil Rutkowski 7c3a9faf53 Updating from upstream
Merge remote-tracking branch 'upstream/develop' into develop
2013-11-29 15:20:28 -06:00
Gil Rutkowski 2fe58f8d6d Merge branch 'patch-1' of github.com:flashingcursor/snipe-it into develop 2013-11-29 15:16:01 -06:00
snipe bc16276a38 Merge pull request #96 from flashingcursor/patch-1
Removed post-install-cmd php artisan optimize.
2013-11-29 13:10:47 -08:00
snipe 3420f3ed8b Merge branch 'refs/heads/develop' 2013-11-29 16:09:36 -05:00
snipe 11c657ffa4 Fixes #36 2013-11-29 16:08:18 -05:00
snipe 80afa7cbc7 Disallow checkout of undeployable items 2013-11-29 15:27:44 -05:00
snipe 75fc552637 Merge branch 'refs/heads/develop' 2013-11-29 15:22:54 -05:00
snipe 56adc67e67 Require manufacturer ID 2013-11-29 15:21:11 -05:00
snipe 02f1977d30 Fixed error message 2013-11-29 15:20:29 -05:00
snipe b68e39472d Updating to bootstrap 3 2013-11-29 15:19:00 -05:00
snipe 10df2bc950 Bootstrap 3 2013-11-29 14:46:38 -05:00
Gil Rutkowski f6aecd8af9 Removed post-install-cmd php artisan optimize. Causes initial install to fail due to lack of required config (mail.php) 2013-11-29 14:37:44 -05:00
snipe 567f395afa More fixes for #88 2013-11-29 14:37:06 -05:00
snipe 7003eff42c More fixes for #88 2013-11-29 14:36:46 -05:00
snipe d54eb94497 Improved layout 2013-11-29 14:35:24 -05:00
Gil Rutkowski 295cef61df Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-29 12:52:03 -06:00
Gil Rutkowski 8ed5584565 Removed post-install artisan optimize from composer.json — this breaks first time install as a mail config doesn’t exist yet. 2013-11-29 12:42:14 -06:00
snipe e305099b45 Fixed model view 2013-11-29 09:52:51 -05:00
snipe 091de54bab BS3 error validation text 2013-11-29 09:45:30 -05:00
snipe 112d5d5994 Style change - removed underline from header 2013-11-29 09:35:51 -05:00
snipe 428998c6fb Prettier buttons 2013-11-29 09:32:10 -05:00
snipe 56b52392e8 honor user soft-deletes 2013-11-29 09:28:45 -05:00
snipe ddf0997a93 Signin BS3 2013-11-29 09:05:30 -05:00
snipe e1207e0e27 Merge branch 'refs/heads/develop' 2013-11-29 08:41:47 -05:00
snipe 3544be0960 Merge branch 'refs/heads/feature-bootstrap3' into develop 2013-11-29 08:41:24 -05:00
snipe 2e916d0246 MOAR BOOTSTRAP 2013-11-29 08:39:10 -05:00
snipe 809e176908 Updating for bootstrap3 2013-11-29 07:39:41 -05:00
snipe c2bf033fb8 More form/button class changes 2013-11-29 07:11:33 -05:00
snipe d67c01e652 Whitespace 2013-11-29 05:21:00 -05:00
snipe 8d82338e1e Remove underline from stats blocks 2013-11-29 05:20:49 -05:00
snipe 89e6f52142 More bootstrap 3 2013-11-29 05:15:51 -05:00
snipe 030db6e267 Upgrading to bootstrap 3
Ugh.
2013-11-29 04:10:25 -05:00
snipe aae590adc4 Merge branch 'refs/heads/develop' 2013-11-28 23:33:33 -05:00
snipe 06cbd0723e Fixes #93 2013-11-28 23:32:44 -05:00
snipe ddabdaca02 Merge branch 'refs/heads/develop' 2013-11-28 20:59:05 -05:00
snipe 839d01a3af Fixed relationship method because fuck you ORM 2013-11-28 20:58:40 -05:00
snipe 4d421c9a50 Leftover method that's not used since the license/asset split 2013-11-28 20:58:15 -05:00
snipe 9fa826b2e7 Fixed assigned to id 2013-11-28 20:40:02 -05:00
snipe 7718e8d928 Checks that checkedout_to isn't 0 before trying to get fullName() 2013-11-28 20:28:09 -05:00
snipe 23374b7fc6 Another leftover from #88 2013-11-28 20:27:44 -05:00
snipe f0f07b578b Fixing pivot for users 2013-11-28 20:19:23 -05:00
snipe e23f1f10b7 Switched validation to alpha_space on notes 2013-11-28 20:02:11 -05:00
snipe 1b56c31a22 Fixed relationship between users/locations because fuck you ORM 2013-11-28 19:58:16 -05:00
snipe 9f7cbc8a4f Another fix related to #88, also accounts for users with invalid location_ids 2013-11-28 19:57:42 -05:00
snipe f504175578 Smaller char req for first/last name, longer req for password 2013-11-28 19:45:47 -05:00
snipe 103faca6ef Clearer display of current vs trashed 2013-11-28 19:45:25 -05:00
snipe 2fce4884d7 Cosmetic changes 2013-11-28 19:45:05 -05:00
snipe 4142629665 Layout tweaks for mobile - related to #54 2013-11-28 19:22:39 -05:00
snipe 03821838eb Merge branch 'refs/heads/develop' 2013-11-28 18:48:15 -05:00
snipe d2959e82a1 Redirect to /hardware to finish cleaning up re-routing from #88 2013-11-28 18:47:54 -05:00
snipe 6a5ccc89df Updated urls from #88 2013-11-28 18:44:20 -05:00
snipe 5858bfb99b Fixed language file paths caused by #88 2013-11-28 18:44:04 -05:00
snipe 3cce78c087 Weird layout 2013-11-28 18:43:30 -05:00
snipe 0d0d42f86a Merge branch 'refs/heads/develop' 2013-11-28 18:32:07 -05:00
snipe d9a37f3822 Fixes #88 2013-11-28 18:31:36 -05:00
snipe 4fcdf72ab6 Responsive design bug fixes, placeholder code for #64 (commented out) 2013-11-28 16:50:53 -05:00
snipe 8680d98bc3 Fixed status display bug 2013-11-28 16:46:34 -05:00
snipe b3bb2d4f11 Added soon-out-of-warranty example to test notifications 2013-11-28 16:37:24 -05:00
snipe 5c28089f2b RTD has to be 0, or it will conflict with the custom status labels 2013-11-28 16:37:05 -05:00
snipe c3769adf80 Merge branch 'refs/heads/develop' 2013-11-28 15:52:23 -05:00
snipe 9e189aa309 Fixes #80, also prevents the user from checking out an asset that is undeployable, etc 2013-11-28 15:52:00 -05:00
snipe 7a3d92522e Merge pull request #86 from peelman/develop
Cancel and back buttons on edit forms now take you back properly
2013-11-28 11:13:32 -08:00
Nick Peelman 54013cfd42 Cancel and back buttons on edit forms now take you back properly 2013-11-28 10:23:52 -05:00
snipe ccb7a5ddba Merge branch 'refs/heads/develop' 2013-11-28 01:16:05 -05:00
snipe 54d9d72549 Tweaks to example configs to prevent data leakage 2013-11-28 01:15:30 -05:00
snipe 819a606204 Changed modelno validator to alpha_space 2013-11-28 00:58:34 -05:00
snipe 33f81abcd3 Added a few more symbols for alpha_space 2013-11-28 00:58:08 -05:00
snipe fbb0b22522 vi or GTFO
:P
2013-11-28 00:57:40 -05:00
snipe 6b017d12c0 Merge pull request #85 from snipe/gitignore_clean
Replace enumeration of files with wildcard
2013-11-27 21:33:23 -08:00
Brady Wetherington 6691447624 Replace enumeration of files with wildcard 2013-11-27 19:26:53 -05:00
snipe 39cc0c6ad7 Merge pull request #83 from peelman/IgorningProductionConfigs
Igorning production configs
2013-11-27 16:22:10 -08:00
Nick Peelman cb87b16e28 its code, not a block quote...
i are stupid.
2013-11-27 19:22:04 -05:00
Nick Peelman 5be13d5eb5 Ignore production configs and sqlite files. Provide example files for renaming. Update install instructions in README accordingly. 2013-11-27 19:19:07 -05:00
snipe eb015b1e22 Merge branch 'refs/heads/develop' 2013-11-27 14:14:41 -05:00
snipe 012a889444 Corrected license seat count associated with license. Fixes #75 2013-11-27 14:13:29 -05:00
snipe e79d27c984 Merge branch 'refs/heads/develop' 2013-11-27 12:26:15 -05:00
snipe 6552401fd8 Fixes #77 2013-11-27 12:25:48 -05:00
snipe da96126445 Merge pull request #78 from peelman/ExpandManufacturer
Expand Manufacturer
2013-11-27 09:05:10 -08:00
Nick Peelman 301ef3fddb Add Manufacturer and Model info to asset page 2013-11-27 11:37:22 -05:00
Nick Peelman d6ba98252d Change N/A to Select One 2013-11-27 11:37:02 -05:00
snipe 9aa6b519c9 Wider display for address field in index blade 2013-11-27 10:34:51 -05:00
Nick Peelman 9f19792985 Save manufacturer ID 2013-11-27 10:32:50 -05:00
snipe e4d56dba92 Merge pull request #73 from peelman/EmailInputTypes
Email input types
2013-11-27 07:28:15 -08:00
snipe e49cb9eb85 Merge pull request #72 from peelman/AddAddressToLocation
Add address to location form
2013-11-27 07:27:45 -08:00
Nick Peelman b7579a1420 Copy/Paste Error 2013-11-27 10:25:25 -05:00
Nick Peelman 6f59617546 Add to controller's edit method too... 2013-11-27 10:23:11 -05:00
Nick Peelman 996458dde3 missing semicolon 2013-11-27 10:18:43 -05:00
Nick Peelman c2d889de12 Add necessary use statements, clean up the run-on return statement that was forming... 2013-11-27 10:18:15 -05:00
Nick Peelman b5a0da03a8 Add manufacturer to edit form 2013-11-27 10:06:36 -05:00
Nick Peelman 8d3d337820 Change input type to email where relevant (better mobile support) 2013-11-27 08:42:31 -05:00
Nick Peelman 1314a1ffea Add location address values to controller save methods... 2013-11-27 08:30:51 -05:00
Nick Peelman bed78dd935 Remove erroneous rule from address2 2013-11-27 08:28:00 -05:00
Nick Peelman 02e5333101 Add address fields to Location edit form 2013-11-27 08:21:12 -05:00
snipe 0aa3c676c2 Merge branch 'refs/heads/develop' 2013-11-27 03:40:21 -05:00
snipe acd9505597 Two-digit decimal for purchase_cost 2013-11-27 03:40:01 -05:00
snipe 65cd28aad3 Fixes #14 2013-11-27 03:39:28 -05:00
snipe 12684b214a Merge branch 'refs/heads/develop' 2013-11-27 02:57:21 -05:00
snipe 5c72aabdea Updated readme for DB migrations reminder 2013-11-27 02:57:02 -05:00
snipe a2b353fb6d Merge branch 'refs/heads/develop' 2013-11-27 02:41:04 -05:00
snipe aa38d83ba6 Fixes #63 and #70 2013-11-27 02:38:16 -05:00
snipe af69467bea Added notes to log table 2013-11-27 01:29:57 -05:00
snipe 432600ab7b Merge branch 'refs/heads/develop' 2013-11-27 01:21:19 -05:00
snipe b1aa67ddb7 Fixes #69
Removed problem migration file
2013-11-27 01:20:57 -05:00
snipe c16382e9d4 Merge branch 'refs/heads/develop' 2013-11-26 22:07:26 -05:00
snipe 9f9bb96076 Merge pull request #67 from abrahamvegh/patch-1
Patch 1
2013-11-26 18:56:54 -08:00
snipe f1e280ccde Merge pull request #68 from abrahamvegh/patch-2
Fix insecure content warnings
2013-11-26 18:55:14 -08:00
Abraham Vegh cfe28ad903 Update default.blade.php
Remove protocol from Google and jQuery CDN URLs, to fix insecure content warnings in SSL-only environments
2013-11-26 20:33:44 -05:00
Abraham Vegh bda827c590 Update User.php
Fix incorrect Gravatar domain name
2013-11-26 20:19:30 -05:00
Abraham Vegh ebc40857e4 Update AssetsController.php
Fix invalid SQL
2013-11-26 20:15:41 -05:00
snipe 69ede7f82c Merge branch 'refs/heads/develop' 2013-11-26 18:11:00 -05:00
snipe 0e3bcfecd0 Should be final fixes for mysql strict mode 2013-11-26 18:10:46 -05:00
snipe 96368444ac Default user id 2013-11-26 17:53:03 -05:00
snipe ec4172d41a Merge branch 'refs/heads/develop' 2013-11-26 17:49:51 -05:00
snipe 8eeae70313 Formatting cleanup 2013-11-26 17:48:51 -05:00
snipe a33435ee12 No seriously. Fuck you, MySQL strict. 2013-11-26 17:47:21 -05:00
snipe eb7801abf9 Fuck you, MySQL strict mode. Right in the eye. 2013-11-26 17:36:06 -05:00
snipe 42450d9cd6 Merge branch 'refs/heads/master' into develop 2013-11-26 17:24:05 -05:00
snipe 4c0af1eae1 Fixed typo causing failure with mysql strict 2013-11-26 17:22:40 -05:00
snipe 35e5df8bdc Merge branch 'refs/heads/develop' 2013-11-26 16:51:44 -05:00
snipe dfd374b875 Removed optional from db seed 2013-11-26 16:51:23 -05:00
snipe 7722c50312 Merge branch 'refs/heads/develop' 2013-11-26 16:37:09 -05:00
snipe 1527b88fd6 Fixes #61 2013-11-26 16:36:42 -05:00
snipe 7f82a6e9fa Merge branch 'refs/heads/develop' 2013-11-26 14:55:49 -05:00
snipe 196710dac8 Fixed bug in error validation display 2013-11-26 14:55:04 -05:00
snipe 194c0b6c7b Implemented new alpha_space validation 2013-11-26 14:54:46 -05:00
snipe b3b67abaa0 New alpha_space custom validation 2013-11-26 14:08:17 -05:00
snipe 0f0059342c CSRF routing
Not sure if this method will work
2013-11-26 12:49:04 -05:00
snipe d7a8cac1dc Fixed validation 2013-11-26 12:46:23 -05:00
snipe c8288b430e Merge branch 'refs/heads/develop' 2013-11-26 11:57:39 -05:00
snipe dc5ad87686 Fixes #59 2013-11-26 11:57:14 -05:00
snipe 1ff93f65b3 Merge branch 'refs/heads/develop' 2013-11-26 07:29:08 -05:00
snipe 4ccb93665b Sexy typeahead select2 boxes for license and asset checkout 2013-11-26 07:28:47 -05:00
snipe 32a63cdd56 Switched asset->id to license->id 2013-11-26 07:26:07 -05:00
snipe 635cd71ef7 Herp-derp. Braces. 2013-11-26 07:23:47 -05:00
snipe 116af7e809 Merge branch 'refs/heads/develop' 2013-11-26 07:12:14 -05:00
snipe ee3fb2785e Fixed XSS on modal
Weird thing. The values were being escaped on the way out but something in the modal javascript was parsing them out as if they weren't. Using data-html="false" seemed to have no effect on the modal. *shrug* Problem for another time.
2013-11-26 07:05:04 -05:00
snipe 2b1ddd9a45 Escaping HTML in modal title 2013-11-26 06:58:14 -05:00
snipe 1fe925ad5f Removing alpha_dash 2013-11-26 06:56:54 -05:00
snipe c220c0a427 Alpha_dash is too contrictive 2013-11-26 06:56:20 -05:00
snipe 52fa847769 Updated to prevent the XSS in the menu 2013-11-26 06:20:46 -05:00
snipe 9854752036 Don't need it 2013-11-26 04:45:11 -05:00
snipe 37fd854003 Merge branch 'refs/heads/develop' 2013-11-26 03:13:33 -05:00
snipe e6fc1956fc Fixes #55 2013-11-26 03:13:09 -05:00
snipe 5bf38b1d02 Fixes #50 2013-11-26 02:33:16 -05:00
snipe 5b8adda5da Merge branch 'refs/heads/develop' 2013-11-26 02:14:31 -05:00
snipe d0cbb9f3a7 Fixes #52 2013-11-26 02:14:06 -05:00
snipe 1295855302 Fixes #51 2013-11-26 02:12:32 -05:00
snipe 4d1dd1c446 Fixed #50 - MOAR VALIDATION! 2013-11-26 02:09:37 -05:00
snipe e77b47717e More validation 2013-11-26 02:04:29 -05:00
snipe 3f4ce4025f Tighter data validation on name 2013-11-26 01:58:57 -05:00
snipe 680b377bd1 Validation on price 2013-11-26 01:28:45 -05:00
snipe 45c04b1cde Fixed display bug 2013-11-26 01:27:27 -05:00
snipe e13578d97d Fixed error when no category is given 2013-11-26 01:21:55 -05:00
snipe 1d50724c91 Removed parent from category
Since the field was already removed
2013-11-26 01:15:25 -05:00
snipe d469a9601b Merge branch 'refs/heads/develop' 2013-11-25 19:57:40 -05:00
snipe fa246aade1 Removed line thingie 2013-11-25 19:57:28 -05:00
snipe a27be4e053 Merge branch 'refs/heads/develop' 2013-11-25 19:56:41 -05:00
snipe 7e6013b125 Added seat numbers 2013-11-25 19:56:22 -05:00
snipe e310d16fb7 Merge branch 'refs/heads/develop' 2013-11-25 18:33:53 -05:00
snipe 6b224ea8aa Removed dummy text 2013-11-25 18:33:27 -05:00
snipe 7a5b133c58 Fixed bug linking to assets when they should be pointing to licenses 2013-11-25 18:31:59 -05:00
snipe 4e25a9e815 Removed extra columns 2013-11-25 18:31:44 -05:00
snipe 084ec064cb Removed created_at from listing. Not important enough to waste the screen space on 2013-11-25 13:38:14 -05:00
snipe 0eb86ccd1d Merge branch 'refs/heads/develop' 2013-11-25 13:35:32 -05:00
snipe e87e5b9cae Fixes #46 2013-11-25 13:34:52 -05:00
snipe 20cce33273 Merge branch 'refs/heads/develop' 2013-11-25 13:18:49 -05:00
snipe e235a4565c Added manufacturer to model display 2013-11-25 13:16:59 -05:00
snipe a345cbd907 Added view for model in routes 2013-11-25 12:40:09 -05:00
snipe 5959e1f9fc Layout tweaks 2013-11-25 12:39:59 -05:00
snipe 3ea0ad87c8 Fixes #22 2013-11-25 12:39:47 -05:00
snipe cf438d0207 Whitespace 2013-11-25 12:37:26 -05:00
snipe 1624dbffa4 Added model view 2013-11-25 12:37:11 -05:00
snipe e2a1cda50e Changed method name 2013-11-25 12:36:51 -05:00
snipe 6fee1067bc Added depreciate flag for assets and licenses 2013-11-25 11:31:02 -05:00
snipe 3ba2880a35 Layout tweaks 2013-11-25 10:12:41 -05:00
snipe 5a2c6391bf Added category relationship 2013-11-25 10:12:23 -05:00
snipe 447a74928b Ditched parent ID - extraneous feature, not really needed 2013-11-25 10:11:40 -05:00
snipe 62e03e0910 Merge branch 'refs/heads/develop' 2013-11-25 08:26:05 -05:00
snipe 1a970716cc Experimenting with nicer error messages 2013-11-25 08:25:47 -05:00
snipe 57d0f36435 Fixes #4 2013-11-25 08:25:34 -05:00
snipe a8d01550dc Prep for unit testing 2013-11-25 08:09:14 -05:00
snipe f071fe5867 Merge branch 'refs/heads/develop' 2013-11-25 07:57:59 -05:00
snipe acfda8dc82 One more change to tighten the sorting.
Still looks weird, still need to revisit.
2013-11-25 07:57:43 -05:00
snipe 3cc0ed8743 Fixed sorting weirdness
Sort of. Still need to revisit, but this works for now.
2013-11-25 07:55:18 -05:00
snipe ffcfc853b3 Merge branch 'refs/heads/develop' 2013-11-25 07:47:18 -05:00
snipe 051ef0a218 Weird sorting on License table
Need to come back to this
2013-11-25 07:38:06 -05:00
snipe 5fcbc9ba78 ALL THE DATATABLES! 2013-11-25 07:24:51 -05:00
snipe 9dbb147ada Removed pagination, since that's handled via data tables now 2013-11-25 07:07:15 -05:00
snipe 718236b2d4 Fixed #9 2013-11-25 07:06:49 -05:00
snipe efb5269a0c Removed unnecessary image 2013-11-25 07:03:42 -05:00
snipe 74034ae69e Sortable, searchable datatables 2013-11-25 07:03:32 -05:00
snipe 9284164e96 Removed unnecessary graphics 2013-11-25 07:03:15 -05:00
snipe dc26a901d4 More paring down the licenses interface
Still looks assy, but it's a little better now
2013-11-25 06:07:52 -05:00
snipe ad680a337e Fixed column name spelling in DB 2013-11-25 05:47:17 -05:00
snipe dab7ec3457 Sigh. 2013-11-25 05:41:57 -05:00
snipe 46714172ba Spelling is still hard. 2013-11-25 05:41:25 -05:00
snipe 4844cd0b38 Copy edit 2013-11-25 05:40:01 -05:00
snipe 79f93ab475 Merge branch 'refs/heads/develop' 2013-11-25 05:37:02 -05:00
snipe 2b088a3fb2 Fixes #17 2013-11-25 05:36:42 -05:00
snipe 2353eda611 Removed extra columns in license table to make it easier to read, moved buttons for more compact view 2013-11-25 05:12:23 -05:00
snipe 343eb31c56 More info on license page 2013-11-25 05:11:46 -05:00
snipe 56586b7e83 Smaller flat buttons 2013-11-25 05:07:20 -05:00
snipe 1b152396ad Merge branch 'refs/heads/develop' 2013-11-25 04:43:42 -05:00
snipe 69badc7fda Filter action log for hardware 2013-11-25 04:41:03 -05:00
snipe 2727ec13c6 Action log seeder 2013-11-25 04:40:51 -05:00
snipe e457aaee6a Remove software models from model seeder 2013-11-25 04:33:14 -05:00
snipe 03edbc008c Fixes #43 2013-11-25 04:28:51 -05:00
snipe 2203789f4c Fixes #41 2013-11-25 04:00:04 -05:00
snipe 74607c0534 Added set count 2013-11-25 03:59:28 -05:00
snipe 0d9184870c More license tweaks - view blade, asset log 2013-11-25 03:48:54 -05:00
snipe faae6a21d3 More license tweaks 2013-11-25 03:22:38 -05:00
snipe 1b01074b9e Changed license structure to use join table to handle multiple seats 2013-11-25 01:04:39 -05:00
snipe 54894227f0 Removed unneeded 2013-11-25 01:02:27 -05:00
snipe c72e98a193 Drop old tables 2013-11-24 19:07:08 -05:00
snipe 295c2e35eb Edits to status dropdown 2013-11-24 19:06:52 -05:00
snipe 75c48c351c Merge branch 'refs/heads/develop' 2013-11-22 18:16:42 -05:00
snipe 37e1ae7f5a Changed settings table structure
Need to refactor this later
2013-11-22 18:04:16 -05:00
snipe e9bd8397d8 Added site settings into controllers for page_num, added site_name to blade 2013-11-22 18:03:57 -05:00
snipe 9b5274fdcd Nicer title display based on filter 2013-11-22 06:47:51 -05:00
snipe d5bbf38915 View status on show all state 2013-11-22 06:07:03 -05:00
snipe c32d45e901 Fixed ID 2013-11-22 06:01:27 -05:00
snipe 2774b07bce Show asset status on undeployable 2013-11-22 05:54:55 -05:00
snipe a356dbd73d Change table display based on filters 2013-11-22 05:43:40 -05:00
snipe 9249786a25 Fixed status display 2013-11-22 05:37:36 -05:00
snipe 135d21e27d Filtering options on assets page 2013-11-22 05:14:21 -05:00
snipe 95fb2f6efd Include filtering 2013-11-22 04:57:31 -05:00
snipe 6434db2a8e Buttons for asset filtering 2013-11-22 04:31:57 -05:00
snipe d0d94bac44 Merge branch 'refs/heads/develop' 2013-11-22 03:25:25 -05:00
snipe b7251d9992 Fixed potential bug in blade 2013-11-22 03:24:48 -05:00
snipe 1b1efec858 Fixed foreign key in assets 2013-11-22 03:24:34 -05:00
snipe 52020d09f0 Fixed bomb when asset is unassigned 2013-11-22 03:18:11 -05:00
snipe 8dd5704697 Added settings 2013-11-22 03:17:55 -05:00
snipe 7b2cf64d7f DB seed info 2013-11-22 03:17:32 -05:00
snipe 60b577166a Added label for setting readability 2013-11-22 03:17:19 -05:00
snipe da93fb18b8 Added settings controller 2013-11-22 03:16:58 -05:00
snipe a3db6ba99c Comment typo 2013-11-22 03:16:44 -05:00
snipe 08db7fbb38 Hurr. Already had a settings table. 2013-11-22 01:07:41 -05:00
snipe 7b9eebe51c Settings table 2013-11-22 00:53:50 -05:00
snipe 8a348ead33 Merge pull request #38 from rashivkp/develop
Develop
2013-11-21 12:30:56 -08:00
Mohamed Rashid fc8c3c086c fixed #37 2013-11-21 20:34:54 +05:30
snipe 5fae91d4e9 Added status label functionality
remember to run artisan DB migrations to make this work
2013-11-20 22:06:41 -05:00
snipe f7cdc8d5c0 Status addition, seeder stuff 2013-11-20 16:57:45 -05:00
snipe de98f20b9c Added status label table 2013-11-20 08:22:36 -05:00
snipe 904a024636 Status table for non-deployed 2013-11-20 08:10:49 -05:00
snipe 261690986a Prevent orphans in categories 2013-11-20 07:43:18 -05:00
snipe 7ada2cc021 Adding soft-delete to manufacturers 2013-11-20 07:33:54 -05:00
snipe 1b5f0e1dd1 Add check to prevent orphaned models 2013-11-20 07:33:41 -05:00
snipe 595f151ff9 Add soft delete to locations - migration 2013-11-20 07:28:34 -05:00
snipe aa34c4a4dd Add checks to prevent orphaned records 2013-11-20 07:28:24 -05:00
snipe 1d04235520 Associated error messages 2013-11-20 07:27:27 -05:00
snipe ed3fd129bf Get the number of models for the depreciation 2013-11-20 07:27:10 -05:00
snipe 53e6d22f83 Soft delete for locations 2013-11-20 07:26:39 -05:00
snipe f3114dec7a Prevent licenses, models and assets from being deleted if they are assigned to something else 2013-11-20 06:48:25 -05:00
snipe d46556a83d Fixed phone display if no value set 2013-11-20 05:56:22 -05:00
snipe 7f77f1e0b1 Better handling for displaying assets that have been soft-deleted so you don't lose their history 2013-11-20 05:55:18 -05:00
snipe 11a6ee5e8d Merge branch 'refs/heads/develop' 2013-11-20 05:08:41 -05:00
snipe 549c0a9fd6 Linked header boxes
Need to add filters on these asset screens to filter avail vs checked out
2013-11-20 04:54:45 -05:00
snipe 84dbb64caf Removed placeholder copy 2013-11-20 04:32:39 -05:00
snipe c93469c465 Fixed query for asset/license header boxes 2013-11-20 04:31:40 -05:00
snipe 781deb2551 Added live counts to the top callout boxes 2013-11-20 04:12:04 -05:00
snipe 44c5817992 Deleted unnecessary crap 2013-11-20 04:11:42 -05:00
snipe c25122273d Merge branch 'refs/heads/develop' 2013-11-20 01:12:37 -05:00
snipe 4e9aa99e42 Merge branch 'develop' of github.com:snipe/snipe-it into develop
Conflicts:
	app/views/backend/layouts/default.blade.php
2013-11-20 01:08:50 -05:00
snipe fb9f0d837e More detailed modal confirm 2013-11-20 01:05:55 -05:00
snipe 080f38a899 enable soft-deletes on assets 2013-11-20 01:04:35 -05:00
snipe b00f3430cb Added a check where the system wouldn't blow up if asset isn't found 2013-11-20 00:57:21 -05:00
snipe 97a69625ba Please submit your pull requests to develop - thanks! 2013-11-20 00:57:03 -05:00
snipe 34bbf460ee Added modal confirm-delete 2013-11-20 00:56:39 -05:00
snipe 171346b5ea Merge branch 'refs/heads/Chandu-feature-confirm-deletes' into develop 2013-11-19 22:29:11 -05:00
snipe 61cceb5f0b Fixed open tag in CSS 2013-11-19 22:28:35 -05:00
snipe f7032f0eaa Merge branch 'feature-confirm-deletes' of github.com:Chandu/snipe-it into Chandu-feature-confirm-deletes 2013-11-19 22:19:40 -05:00
Chandu e3160957e6 Added confirmation popups for deletes 2013-11-19 21:37:01 -05:00
snipe 57f5f5705f Merge branch 'refs/heads/develop' 2013-11-19 18:28:46 -05:00
snipe 4e8c3517a7 Tightened up display if admin is deleted 2013-11-19 17:50:20 -05:00
snipe 0ee08e47e9 Fixed fatal error if admin who created the asset was deleted. (This should never happen, but...) 2013-11-19 17:48:53 -05:00
snipe 9c08d6fcf7 weird bug 2013-11-19 17:43:55 -05:00
snipe d6a718e997 Fixed bug in views for created_at date 2013-11-19 17:40:06 -05:00
snipe 8daffa82fe Added logging to license checkin/checkout 2013-11-19 17:32:50 -05:00
snipe 7352eaec2e Helper methods for license view display 2013-11-19 17:32:33 -05:00
snipe 67aaeaf7f8 Added route for license view 2013-11-19 17:32:18 -05:00
snipe 76da83cbe1 Fixed links to edit/checkout 2013-11-19 17:31:52 -05:00
snipe 4929f803cc Added view links to index 2013-11-19 17:31:39 -05:00
snipe 24972a330a Added license view blade 2013-11-19 17:31:23 -05:00
snipe 1b160d251e Tidied up the user view, added more info 2013-11-19 17:13:35 -05:00
snipe fa175feb45 Added placeholder "out for digs" option 2013-11-19 17:13:15 -05:00
snipe f0f0019d3b Get the user's location 2013-11-19 17:12:48 -05:00
snipe 8cc367f9e6 Get the admin user details 2013-11-19 17:12:30 -05:00
snipe 55aa41dc32 Tightened up display a little 2013-11-19 16:14:34 -05:00
snipe 08f6d7e17d Added user detail on who it's checked out to 2013-11-19 15:54:59 -05:00
snipe 961baffa4f Improved assetloc function 2013-11-19 15:54:36 -05:00
snipe 9e235b6825 Check asset exists before displaying view 2013-11-19 05:55:58 -05:00
snipe db80b36d0f Added some error checking for invalid users 2013-11-19 05:51:40 -05:00
snipe 02829cb138 Re-ordered columns 2013-11-19 05:51:25 -05:00
snipe 123b6f2a37 Linked assets to user view 2013-11-19 05:31:33 -05:00
snipe 1567a8bde1 Asset view blade 2013-11-19 05:31:07 -05:00
snipe 30a8a96113 Linked asset tags to their new view page 2013-11-19 05:30:54 -05:00
snipe cab75ed7d6 Added asset view to routes 2013-11-19 05:30:32 -05:00
snipe e2c1671d21 Spelling continues to be hard. Apparently. 2013-11-19 05:30:20 -05:00
snipe 5b27a34f05 Fixed fieldname in userlog 2013-11-19 05:30:04 -05:00
snipe ea312fc161 Added asset view in controller 2013-11-19 05:29:42 -05:00
snipe b8908bf246 Add checkin/checkout log to user profile view 2013-11-19 03:58:26 -05:00
snipe f7f79681f3 Added logging action on checkin and checkout 2013-11-19 02:00:17 -05:00
snipe e22d381a20 Added asset logger 2013-11-19 02:00:02 -05:00
snipe 9ebe62f044 Added asset logger 2013-11-19 01:59:45 -05:00
snipe 719c36d482 migration to fix a few fields in the log table 2013-11-19 01:57:34 -05:00
snipe 8c1f6ada67 Number format on diff column 2013-11-18 22:08:00 -05:00
snipe 5bcfca18c7 Added download button, right-aligned fields, added diff math 2013-11-18 22:03:05 -05:00
snipe a28775b9b7 Routes updated for reports 2013-11-18 21:51:02 -05:00
snipe bbaed47e90 Added nav option to get to reports 2013-11-18 21:50:20 -05:00
snipe 8bf1640721 Basic report blade - more to come here 2013-11-18 21:48:44 -05:00
snipe 6265f530ed Added reports controller method 2013-11-18 21:48:28 -05:00
snipe 015c09f560 Swapped people and licenses 2013-11-18 21:48:15 -05:00
snipe 0023b04396 Removed columns from assets view for more compact table
Purchase cost and value are not important day to day. That can be figured out in the reports.
2013-11-18 21:48:03 -05:00
snipe 98cf8767f2 Locked new top boxes down to admin 2013-11-18 21:23:34 -05:00
snipe 3bbd98f1c7 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 21:20:16 -05:00
snipe 532c03a73d Placeholder top summary boxes
There aren't wired up yet
2013-11-18 21:19:30 -05:00
snipe 44ba90f9f0 Fixed icon set to white in main nav 2013-11-18 21:11:50 -05:00
snipe 981ff2b6e5 Asset action log table 2013-11-18 21:10:30 -05:00
Brady Wetherington e819c642db Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 15:29:49 -05:00
Brady Wetherington 8268edae81 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 15:16:02 -05:00
snipe edbf195034 Changed footer 2013-11-18 14:52:01 -05:00
snipe 1590c1a300 whitespace 2013-11-18 14:40:32 -05:00
snipe 6dd320171f log files 2013-11-18 14:36:38 -05:00
snipe 5c43caf057 More table fixes 2013-11-18 13:30:48 -05:00
snipe ddfd69e510 More table fixes 2013-11-18 13:23:15 -05:00
snipe 0e11b29cf9 Sigh. 2013-11-18 13:15:38 -05:00
snipe 3401fee672 Another table fix 2013-11-18 13:14:26 -05:00
snipe ffdb4b1aa8 Fixed wonky layout issue 2013-11-18 13:10:38 -05:00
snipe f409ade3a5 So wow. Missed tables disappoint. Such code. 2013-11-18 12:51:20 -05:00
Brady Wetherington 064d7da38e Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 12:40:22 -05:00
snipe 4854228851 Moved font awesome files 2013-11-18 12:39:26 -05:00
Brady Wetherington 88599fad7f Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 12:37:59 -05:00
snipe d0a5afbf02 Tweaking backend display 2013-11-18 12:36:41 -05:00
snipe c2f5b48635 Removed extraneous images 2013-11-18 12:36:18 -05:00
snipe 5172954aef Added wildcard to nav display 2013-11-18 12:33:32 -05:00
Brady Wetherington 14de269474 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-18 12:25:23 -05:00
snipe ad914b73b0 Ghetto way to grab location by asset
Need to move this out of the view
2013-11-18 11:51:09 -05:00
snipe c679d446fd Nulled extra values for artisan install 2013-11-18 11:50:17 -05:00
snipe 889b66d578 hurrr 2013-11-18 10:53:20 -05:00
snipe b966bb2889 Added location to asset view 2013-11-18 10:45:14 -05:00
snipe abac6489c4 Removed location_id from assets table
Going to handle this via the people it's checked out to instead
2013-11-18 10:33:35 -05:00
snipe 4ef506cde7 New language entry for no results on user filter 2013-11-18 10:28:03 -05:00
snipe c7c072b07c New fields included in table view 2013-11-18 10:27:48 -05:00
snipe 7b59f42fb0 Fixed pagination bug, styled buttons 2013-11-18 10:27:26 -05:00
snipe 50387874bf Added complete info to the locations seeder 2013-11-18 10:26:47 -05:00
snipe 02ed943ffa New fields in form view 2013-11-18 09:55:07 -05:00
snipe 8f665e82ae Style update for action buttons 2013-11-18 09:54:52 -05:00
snipe 897bc63621 Location drop down to user in views 2013-11-18 09:54:37 -05:00
snipe 673f5accf9 Added location drop down to user 2013-11-18 09:54:19 -05:00
snipe 6214c7b706 migration for new user and location fields 2013-11-18 09:54:00 -05:00
snipe 7c37f91dd5 Linked user in asset view to user profile view 2013-11-18 07:58:20 -05:00
snipe ca90300d2f Added user profile view 2013-11-18 07:53:33 -05:00
snipe d2427bdea2 Cosmetic tweaks 2013-11-18 06:54:12 -05:00
snipe 57107dceee Fixed checkin text
Was mistakenly using checkout text on error/confirmation
2013-11-18 04:54:22 -05:00
snipe 48e180c691 Fixed asset bug caused by unique validation in assets model
Pulled validation back out into the controller for asset tag
2013-11-18 04:50:28 -05:00
snipe b554a9f9db Updated blades with new style buttons 2013-11-18 04:49:54 -05:00
snipe e3fa628bac Reskin files 2013-11-18 04:04:06 -05:00
snipe c29b3c93f7 Removed old bootstrap files 2013-11-18 04:03:20 -05:00
snipe 524d3d233d Added count of assets in asset models table 2013-11-18 01:32:22 -05:00
snipe 2fd8b1760c Removed the note that it's not ready for you yet :) 2013-11-18 01:24:54 -05:00
snipe 7bc7ffb728 Fixed some error reporting/required fields around checkout/checkin 2013-11-18 01:16:08 -05:00
snipe 7d38a7db61 Added checkout/checkin to index display 2013-11-18 00:56:14 -05:00
snipe 785b32c8ff Cleaned up user function for assets 2013-11-18 00:55:57 -05:00
snipe 2d9f56c02a Cleaned up user method 2013-11-18 00:55:36 -05:00
snipe 36bef5bfa5 Language update 2013-11-18 00:55:24 -05:00
snipe ec4239693d Checkin/Checkout for licenses 2013-11-18 00:55:05 -05:00
snipe 17cb895633 Checkin/Checkout for licenses 2013-11-18 00:54:56 -05:00
Brady Wetherington a8372dc998 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-17 23:44:26 -05:00
snipe d9f434ca12 Short tags FTL 2013-11-17 23:33:21 -05:00
snipe 3a5300e805 license indentation 2013-11-17 23:03:29 -05:00
snipe b22621ab55 typo 2013-11-17 23:03:13 -05:00
snipe 4da2bccbf7 Merge pull request #27 from snipe/depreciation_fix
Depreciation fix
2013-11-17 20:01:46 -08:00
snipe eae1c29262 moar notes!! 2013-11-17 23:00:50 -05:00
Brady Wetherington 127c378b36 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-17 22:50:22 -05:00
snipe 284645fa7c Added depreciation seed to DatabaseSeeder file
depreciation seed info wasn't being included in the initial seed
2013-11-17 22:49:12 -05:00
snipe 03959b4783 Added note on permissions and documentroot 2013-11-17 22:46:24 -05:00
Brady Wetherington 90c4dfecdf Merge branch 'develop' of github.com:snipe/snipe-it into develop 2013-11-17 22:44:47 -05:00
Brady Wetherington 8f86ec9dae Change depreciation to be by month; formatting fixes. 2013-11-17 22:44:32 -05:00
snipe 3d5dcf7c0e Moved validation to models 2013-11-17 22:34:51 -05:00
snipe 31d323fb71 Cosmetic fix on form 2013-11-17 22:34:43 -05:00
snipe 940281dd4d Fixed bug on validation display 2013-11-17 22:28:59 -05:00
snipe 238e933f00 Moved asset model validation into model 2013-11-17 22:28:49 -05:00
snipe 03e95743e2 Moved manufacturer validation into model 2013-11-17 22:24:01 -05:00
snipe 2f67ee659f Moved validation into models 2013-11-17 22:19:09 -05:00
snipe ea571535b0 extra whitespace 2013-11-17 22:14:32 -05:00
snipe 7bdd5505fd Moved validation to model 2013-11-17 22:14:02 -05:00
snipe 3aea00841a Category using Elegant validation now 2013-11-17 22:05:56 -05:00
snipe 76ac391153 Fixed edit bug where new Category was invoked twice 2013-11-17 22:05:39 -05:00
snipe 7d1a2b2bec Removed validation rules, as they're now in the model 2013-11-17 21:47:59 -05:00
snipe ca842ee447 Added Elegant model and validation rules 2013-11-17 21:47:46 -05:00
snipe 16afe9eb02 Validation abstraction model 2013-11-17 21:46:19 -05:00
snipe 343e13efee Checkout/checkin functionality for assets
Need to refactor a bit and add to licenses as well
2013-11-17 20:36:54 -05:00
snipe a7281ef5b5 programming is hard.
added missing comma
2013-11-17 20:07:42 -05:00
snipe cfb37dcec0 Replaced english text with language reference on buttons 2013-11-17 20:05:12 -05:00
snipe cb5b365631 view logic to determine whether to print out a checkout button 2013-11-17 20:04:43 -05:00
snipe 23c35522c8 Unified save/cancel language option for buttons 2013-11-17 20:04:25 -05:00
snipe 92eab82328 english language messages for checkout 2013-11-17 20:04:13 -05:00
snipe 774b182e9f mark assets as physical, new methods for checkout 2013-11-17 20:03:56 -05:00
snipe 51c1bf4b63 new checkout view 2013-11-17 20:03:34 -05:00
snipe c9a78b319a Added asset checkout routes 2013-11-17 20:03:18 -05:00
snipe f62fdd9aeb Email validation if the license_email field is filled in 2013-11-17 18:41:16 -05:00
snipe 89ede0f8cf Added date picker 2013-11-17 01:53:21 -05:00
snipe 939648e9de Switched to use assets table 2013-11-17 01:53:04 -05:00
snipe 66c9b81118 Seeder changes for new schema 2013-11-17 01:52:44 -05:00
snipe 4143fa6a25 Changes to migrations tables to merge license table 2013-11-17 01:52:18 -05:00
snipe 961a4068b4 Deleted these files - not needed 2013-11-17 01:51:35 -05:00
snipe 38d85afc2b Added where clause for physical = 0 for non-physical assets 2013-11-17 01:51:19 -05:00
snipe 5adb65b4cb Added where clause for physical assets 2013-11-17 01:50:03 -05:00
snipe 17be6b3efe Note about enabling swap in EC2 micros for composer. 2013-11-17 00:14:51 -05:00
snipe a3837eb641 Added note on publishing the debugbar 2013-11-17 00:05:44 -05:00
snipe 90ac04eac7 Edits to DB seeder 2013-11-16 22:42:53 -05:00
snipe eddbb7d973 User/assets tie-in 2013-11-16 22:04:52 -05:00
snipe eee22a9f2b Updated link, added pledgie 2013-11-16 12:47:07 -05:00
snipe 98990d852d Updated Trello url to GH Issues url
Better to keep it all in one place
2013-11-16 12:39:34 -05:00
snipe 33a0d2e191 DB seeds 2013-11-16 11:32:25 -05:00
snipe cf59ec8c67 Fixed pagination bug
Code required
@if ($models->getTotal() > 10)

NOT

@if (count($models) > 10)
2013-11-16 11:32:18 -05:00
snipe 24c3a153e5 Database seeders 2013-11-16 10:57:07 -05:00
snipe ffe786f659 Fixed routes. 2013-11-16 09:33:30 -05:00
snipe 173a7d120f SRSLY WTF OMG.
No idea how that broke.
2013-11-16 09:21:08 -05:00
snipe 4756e950ef oops 2013-11-16 09:17:40 -05:00
snipe 9e96e4b60a very simple lockdown for demo site 2013-11-16 09:03:57 -05:00
snipe c1e1323e68 cleanup 2013-11-16 08:12:25 -05:00
snipe ea7b0c1c0c added alpha reqs to state/country 2013-11-16 07:40:14 -05:00
snipe 27514f85c3 Fixed typo in variable name 2013-11-16 07:19:01 -05:00
snipe 5dc449a99e Added locations CRUD 2013-11-16 07:18:47 -05:00
snipe 7def599141 redirect login to assets page by default, removed dashboard link for now 2013-11-16 06:49:47 -05:00
snipe e4443b8988 Removing unnecessary cruft 2013-11-16 06:38:51 -05:00
snipe bb167aac59 minor cosmetic changes to form 2013-11-16 06:29:14 -05:00
snipe 4742c5e906 require unique asset tags 2013-11-16 06:22:51 -05:00
snipe a62ffa96cf history table, location table 2013-11-16 05:51:38 -05:00
snipe 0894832e60 Cleanup, adding model CRUD 2013-11-16 03:55:18 -05:00
snipe 2e132331e7 Refactoring
Removes extraneous create.blade.php, cleaned up some functions
2013-11-16 03:25:40 -05:00
snipe 4f0a1789db Fixed license blades 2013-11-16 01:14:52 -05:00
snipe 23a6e867e6 Math is hard. :(
Some ghetto straight-line depreciation shit happening here. Assumign 30 days in a month, blah blah blah. Will need a better fix later, but this is fine for MVP
2013-11-16 01:09:21 -05:00
snipe 9720ff9fd5 Added (non-functional) search field 2013-11-15 23:46:20 -05:00
snipe 32eb9d1d30 Added depreciations 2013-11-15 23:46:03 -05:00
snipe 31b90cf2a3 removed ID column. No one cares about the ID 2013-11-15 22:33:02 -05:00
snipe e475846fb6 nav lockout for non-logged in users 2013-11-15 22:27:13 -05:00
snipe 95750cb3a9 Added auth check in header 2013-11-15 22:08:48 -05:00
snipe 86087b2c4f merged fronted with backend 2013-11-15 22:08:36 -05:00
snipe 9c3be972f0 Skip all the public crap. Login or GTFO 2013-11-15 22:01:19 -05:00
snipe 6a70acbe5f sexy calendar date picker icon 2013-11-15 22:01:01 -05:00
snipe f60cba1a4a Added date picker for purchase date 2013-11-15 21:32:54 -05:00
snipe 38154c0df8 typo 2013-11-15 21:31:33 -05:00
snipe 3af94b4e7e Added model drop down 2013-11-15 21:30:48 -05:00
snipe 4cd458cc7a Minor column edits 2013-11-15 19:21:20 -05:00
snipe ccf59fb79d updated url with asset link
this link is dumb and I will probably fix it later
2013-11-15 19:20:49 -05:00
snipe 7e840ec273 Asset view blades 2013-11-15 19:20:26 -05:00
snipe 8b57cd8ab9 Updated routes with assets 2013-11-15 19:20:04 -05:00
snipe 0839510807 Placeholder asset model 2013-11-15 19:19:54 -05:00
snipe b0eb5e5bdd Language files for messages + index table 2013-11-15 19:19:41 -05:00
snipe f12e6b0837 Added DB 2013-11-15 19:18:48 -05:00
snipe 16143c4ff7 New asset controller 2013-11-15 19:18:28 -05:00
snipe 40713d04f9 Add/Edit/View blades 2013-11-15 15:48:30 -05:00
snipe 2a08e00a16 License routes 2013-11-15 15:48:11 -05:00
snipe 5e6a22d973 Added currency 2013-11-15 15:48:00 -05:00
snipe f27fa40f1c Added license name 2013-11-15 15:47:47 -05:00
snipe abd060679e License CRUD 2013-11-15 15:47:34 -05:00
snipe 34cb25296f Added purchase date, etc to language files 2013-11-15 15:47:02 -05:00
snipe 9bbc25fc76 License language files 2013-11-15 15:00:50 -05:00
snipe e9fe59444f Placeholder licenses model 2013-11-15 14:59:50 -05:00
snipe 9364c46c2d Fixed licensed link 2013-11-15 14:59:35 -05:00
snipe 50dfa27689 Licenses route 2013-11-15 14:59:03 -05:00
snipe d7dedf84ad Added assets column to index blade 2013-11-15 14:28:07 -05:00
snipe fd0e5959b5 Nav tweaks
Still working on the info architecture of this - added licenses, and removed users, as users will go under People
2013-11-15 14:27:43 -05:00
snipe ba8cf36691 Added assets column 2013-11-15 14:26:35 -05:00
snipe 5a46c85533 asset count per user.
this shit is very broken right now.
2013-11-15 14:25:57 -05:00
snipe 9159d5f771 Added license/asset tables 2013-11-15 14:25:28 -05:00
snipe c9293897fb manufacturer CRUD 2013-11-15 06:39:58 -05:00
snipe 0e4e26206b fixed broken redirect route 2013-11-15 06:39:07 -05:00
snipe 884131f729 added user_id to manufacturer table 2013-11-15 06:38:50 -05:00
snipe 1f2badfc39 minor UI tweaks
Added icons to action buttons, removed ability to edit a deleted user
2013-11-15 06:20:06 -05:00
snipe 79da13dff7 Fixed layout for category view 2013-11-15 05:07:34 -05:00
snipe b8d4823cae Removed created_at column in results.
No one cares when a category or manufacturer was created.
2013-11-15 05:06:26 -05:00
snipe 0789108e74 Added Trello board URL 2013-11-15 04:59:36 -05:00
snipe c591a71141 only show pagination if results > 10 2013-11-15 04:46:41 -05:00
snipe 2b930ff1e7 minor UI flow changes, disallow a category to be its own parent 2013-11-15 04:34:10 -05:00
snipe af03d60d28 Copy tweak 2013-11-15 04:30:49 -05:00
snipe 51cd0b0208 only paginate if there are > 10 results, added parentname output 2013-11-15 04:30:38 -05:00
snipe e5aa5f1a5b Added parentname method to get the name of the parent category for display 2013-11-15 04:30:05 -05:00
snipe 1bd87cca05 removed excess white space 2013-11-15 04:27:26 -05:00
snipe 0fcd64c386 categories dropdown in create/edit form 2013-11-14 22:32:16 -05:00
snipe 590dbb2bd5 Create form
I will probably make these modals later - the UX is a little clumsy this way
2013-11-14 21:46:13 -05:00
snipe 54cd3d04b1 Added buttons for edit/delete 2013-11-14 21:45:33 -05:00
snipe 6218e1912f Added edit/delete routes for categories 2013-11-14 21:45:01 -05:00
snipe 14972f894b fixed typo 2013-11-14 21:44:51 -05:00
snipe cf1134db37 Edit/Delete categories 2013-11-14 21:44:42 -05:00
snipe 963d3a764a adding user ID to category table 2013-11-14 21:44:19 -05:00
snipe 07e71a547c controller cleanup 2013-11-13 17:49:07 -05:00
snipe 98afb39ef0 manufacturer's nav 2013-11-13 17:48:54 -05:00
snipe 5d3d1eb319 manufacturers language files 2013-11-13 17:48:37 -05:00
snipe a1f9296700 new route for manufacturers 2013-11-13 17:47:26 -05:00
snipe a8d192d263 updated category view 2013-11-13 17:47:16 -05:00
snipe 7785743d85 new manufacturers views 2013-11-13 17:46:55 -05:00
snipe 4ac4b2fde0 Added model number to blades 2013-11-13 17:46:37 -05:00
snipe f387f7e005 spelling is really hard :( 2013-11-13 17:29:28 -05:00
snipe 721a858aa3 Added categories to router 2013-11-13 17:04:05 -05:00
snipe b3c85cd554 rough category model 2013-11-13 17:02:48 -05:00
snipe 760c41896e first manufacturer controller 2013-11-13 17:02:37 -05:00
snipe b18f69330f language files for category view 2013-11-13 17:02:25 -05:00
snipe e08741b104 created category controller 2013-11-13 17:02:11 -05:00
snipe dd8d23bcf7 category view blades 2013-11-13 17:01:52 -05:00
snipe 1ff564ad9f updated nav 2013-11-13 17:01:33 -05:00
snipe f7466a335d nav additions 2013-11-13 16:23:36 -05:00
snipe 261049e848 placeholder model/category models 2013-11-13 16:23:18 -05:00
snipe 9747815960 license/contribution info 2013-11-13 16:22:59 -05:00
snipe 4c9b66150d added travis-ci yml 2013-11-13 15:24:25 -05:00
snipe 41efea5929 Updated read me 2013-11-13 14:45:48 -05:00
snipe 52acfafc42 updated read me 2013-11-13 14:44:11 -05:00
snipe 5721636b71 minor change to readme 2013-11-13 05:57:03 -05:00
snipe 01dadedc78 Rough model view blades 2013-11-13 05:56:24 -05:00
snipe 6bca4fa187 Rough scaffold for models controller 2013-11-13 05:56:05 -05:00
snipe 92f80f979f Adding english language phrases for the mode display table 2013-11-13 05:54:57 -05:00
snipe fc6e41e464 Removing extraneous nav
This view/nav will probably go away, since we don't really have a frontend, everything is behind a login
2013-11-13 05:54:23 -05:00
snipe 574822f278 modified nav 2013-11-13 05:53:30 -05:00
snipe 91b85de6cf New routes for new sections 2013-11-13 05:53:14 -05:00
snipe dd20f519b5 DB seeds 2013-11-13 05:50:20 -05:00
snipe 6f80a2dedd Default DB creds 2013-11-13 05:49:22 -05:00
snipe 53ca5e9c5d Create models/categories/manufacturer table migrations 2013-11-13 05:49:03 -05:00
snipe feda21a6e3 Initial laravel import 2013-11-13 01:59:39 -05:00
snipe 87e90f12ea Initial commit 2013-11-12 14:25:47 -08:00
10352 changed files with 114072 additions and 807813 deletions
-4276
View File
File diff suppressed because it is too large Load Diff
Executable
+37
View File
@@ -0,0 +1,37 @@
ls
pwd
cd app
ls
cd views/
cd backend/
ls
cd users/
ls
vi view.blade.php
ls -alF
cd
pwd
ls -alF
pwd
cd app
ls
cd views/
cd backend/
cd users/
ls
ls -alF
chown snipeitftp view.blade.php
sudo chown snipeitftp view.blade.php
pwd
ls
vi view.blade.php
set noswapfile
set nobackup
vi view.blade.php
touch delete.me
vi delete.me
ls
ls -alF
touch view.blade.php
touch index.blade.php
vi view.blade.php
-13
View File
@@ -1,13 +0,0 @@
.git
.github
.gitattributes
.gitignore
.dockerignore
app/storage/logs/*
app/storage/views/*
vendor/*
storage/framework/cache/*
node_modules
.vagrant
.idea
-180
View File
@@ -1,180 +0,0 @@
# --------------------------------------------
# REQUIRED: DB SETUP
# --------------------------------------------
# https://mariadb.com/kb/en/mariadb-server-docker-official-image-environment-variables/
MYSQL_DATABASE=snipeit
MYSQL_USER=snipeit
MYSQL_PASSWORD=changeme1234
MYSQL_ROOT_PASSWORD=changeme1234
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=develop
APP_DEBUG=true
# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en-US
MAX_RESULTS=500
# --------------------------------------------
# REQUIRED: UPLOADED FILE STORAGE SETTINGS
# --------------------------------------------
PRIVATE_FILESYSTEM_DISK=local
PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=mariadb
DB_DATABASE=snipeit
DB_USERNAME=snipeit
DB_PASSWORD=changeme1234
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=true
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
DB_SSL=false
DB_SSL_IS_PAAS=false
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
DB_SSL_CIPHER=null
DB_SSL_VERIFY_SERVER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_DRIVER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_AUTO_EMBED_METHOD='attachment'
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: BACKUP SETTINGS
# --------------------------------------------
MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=false
COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=40
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
# --------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
CACHE_PREFIX=snipeit
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
# --------------------------------------------
# OPTIONAL: MEMCACHED SETTINGS
# --------------------------------------------
MEMCACHED_HOST=null
MEMCACHED_PORT=null
# --------------------------------------------
# OPTIONAL: PUBLIC S3 Settings
# --------------------------------------------
PUBLIC_AWS_SECRET_ACCESS_KEY=null
PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: PRIVATE S3 Settings
# --------------------------------------------
PRIVATE_AWS_ACCESS_KEY_ID=null
PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: AWS Settings
# --------------------------------------------
AWS_ACCESS_KEY_ID=null
AWS_SECRET_ACCESS_KEY=null
AWS_DEFAULT_REGION=null
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
APP_FORCE_TLS=false
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
-188
View File
@@ -1,188 +0,0 @@
# --------------------------------------------
# REQUIRED: DOCKER SPECIFIC SETTINGS
# --------------------------------------------
APP_VERSION=
APP_PORT=8000
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=production
APP_DEBUG=false
# Please regenerate the APP_KEY value by calling `docker compose run --rm app php artisan key:generate --show`. Copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier
APP_TIMEZONE='UTC'
APP_LOCALE=en-US
MAX_RESULTS=500
# --------------------------------------------
# REQUIRED: UPLOADED FILE STORAGE SETTINGS
# --------------------------------------------
PRIVATE_FILESYSTEM_DISK=local
PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=db
DB_SOCKET=null
DB_PORT='3306'
DB_DATABASE=snipeit
DB_USERNAME=snipeit
DB_PASSWORD=changeme1234
MYSQL_ROOT_PASSWORD=changeme1234
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=true
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
DB_SSL=false
DB_SSL_IS_PAAS=false
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
DB_SSL_CIPHER=null
DB_SSL_VERIFY_SERVER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_TLS_VERIFY_PEER=true
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_AUTO_EMBED_METHOD='attachment'
# --------------------------------------------
# REQUIRED: DATA PROTECTION
# --------------------------------------------
ALLOW_BACKUP_DELETE=false
ALLOW_DATA_PURGE=false
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: BACKUP SETTINGS
# --------------------------------------------
MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=false
COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=40
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.16.0.0/12
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
# --------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
CACHE_PREFIX=snipeit
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=null
REDIS_PASSWORD=null
REDIS_PORT=6379
# --------------------------------------------
# OPTIONAL: MEMCACHED SETTINGS
# --------------------------------------------
MEMCACHED_HOST=null
MEMCACHED_PORT=null
# --------------------------------------------
# OPTIONAL: PUBLIC S3 Settings
# --------------------------------------------
PUBLIC_AWS_SECRET_ACCESS_KEY=null
PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: PRIVATE S3 Settings
# --------------------------------------------
PRIVATE_AWS_ACCESS_KEY_ID=null
PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: AWS Settings
# --------------------------------------------
AWS_ACCESS_KEY_ID=null
AWS_SECRET_ACCESS_KEY=null
AWS_DEFAULT_REGION=null
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=stderr
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
APP_FORCE_TLS=false
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
-107
View File
@@ -1,107 +0,0 @@
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=local
APP_DEBUG=false
APP_KEY=base64:hTUIUh9CP6dQx+6EjSlfWTgbaMaaRvlpEwk45vp+xmk=
APP_URL=http://127.0.0.1:8000
APP_TIMEZONE='US/Eastern'
APP_LOCALE=en-US
APP_LOCKED=false
MAX_RESULTS=200
# --------------------------------------------
# REQUIRED: UPLOADED FILE STORAGE SETTINGS
# --------------------------------------------
PRIVATE_FILESYSTEM_DISK=local
PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
DB_PREFIX=null
#DB_DUMP_PATH=
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
DB_SSL=false
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
DB_SSL_CIPHER=null
DB_SSL_VERIFY_SERVER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_MAILER="log"
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=true
COOKIE_NAME=snipeit_v5_local
SECURE_COOKIES=true
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
REFERRER_POLICY=same-origin
ENABLE_CSP=true
CORS_ALLOWED_ORIGINS="*"
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
# --------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=50000
LOGIN_LOCKOUT_DURATION=1000
RESET_PASSWORD_LINK_EXPIRES=15
# --------------------------------------------
# OPTIONAL: API
# --------------------------------------------
API_MAX_REQUESTS_PER_HOUR=200
# --------------------------------------------
# OPTIONAL: SAML SETTINGS
# --------------------------------------------
DISABLE_NOSAML_LOCAL_LOGIN=true
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_LEVEL=debug
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=null
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=true
ENABLE_HSTS=false
WARN_DEBUG=false
APP_CIPHER=AES-256-CBC
-256
View File
@@ -1,256 +0,0 @@
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=production
APP_DEBUG=false
APP_KEY=ChangeMe
APP_URL=null
APP_TIMEZONE='UTC'
APP_LOCALE='en-US'
MAX_RESULTS=500
# --------------------------------------------
# REQUIRED: UPLOADED FILE STORAGE SETTINGS
# --------------------------------------------
PRIVATE_FILESYSTEM_DISK=local
PUBLIC_FILESYSTEM_DISK=local_public
#PRIVATE_FILESYSTEM_DISK=s3_private
#PUBLIC_FILESYSTEM_DISK=s3_public
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_SOCKET=null
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_DUMP_SKIP_SSL=false
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
DB_SANITIZE_BY_DEFAULT=false
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
# Enable SSL connection to database (true/false)
DB_SSL=false
# Set to true for cloud databases like AWS RDS, Azure Database, Google Cloud SQL
# Set to false for self-hosted databases with client certificates
DB_SSL_IS_PAAS=false
# Required when DB_SSL_IS_PAAS=false (client certificate authentication)
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
# Path to CA certificate bundle (required for SSL connections)
# For AWS RDS, download from: https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
DB_SSL_CA_PATH=null
# SSL cipher (optional, leave null for default)
DB_SSL_CIPHER=null
# Verify server certificate (true/false, defaults to false if not set)
# Set to false for development or when using self-signed certificates
DB_SSL_VERIFY_SERVER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_MAILER=smtp
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=YOURUSERNAME
MAIL_PASSWORD=YOURPASSWORD
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_AUTO_EMBED_METHOD='attachment'
MAIL_TLS_VERIFY_PEER=true
# MAIL_ENCRYPTION is no longer supported. SymfonyMailer will use tls if it's
# advertised, and won't if it's not. If you want to use your mail server's IP but it's failing
# because of certificate errors, set MAIL_TLS_VERIFY_PEER-true
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: BACKUP SETTINGS
# Backup filesystem configuration
# - BACKUP_FILESYSTEM_DRIVER: Driver to use (local, s3, etc.)
# Default: local (backward compatible)
# Set to s3 to use S3 for backups (requires PRIVATE_AWS_* credentials)
# - BACKUP_FILESYSTEM_ROOT: Root path/prefix
# For local driver: leave commented for default to storage_path("app")
# For S3 driver: empty string = bucket root, or specify prefix like "backups/"
#--------------------------------------------
BACKUP_FILESYSTEM_DRIVER=local
#BACKUP_FILESYSTEM_ROOT=
MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
ALLOW_BACKUP_DELETE=false
ALLOW_DATA_PURGE=false
ALL_BACKUP_KEEP_DAYS=7
DAILY_BACKUP_KEEP_DAYS=16
WEEKLY_BACKUP_KEEP_WEEKS=8
MONTHLY_BACKUP_KEEP_MONTHS=4
YEARLY_BACKUP_KEEP_YEARS=2
BACKUP_PURGE_OLDEST_AT_MEGS=5000
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
SESSION_DRIVER=file
SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=false
COOKIE_NAME=snipeit_session
PASSPORT_COOKIE_NAME='snipeit_passport_token'
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=15
BS_TABLE_STORAGE=localStorage
BS_TABLE_DEEPLINK=true
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
ADDITIONAL_CSP_URLS=null
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
# --------------------------------------------
CACHE_DRIVER=file
QUEUE_DRIVER=sync
CACHE_PREFIX=snipeit
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=null
REDIS_PASSWORD=null
REDIS_PORT=null
# --------------------------------------------
# OPTIONAL: MEMCACHED SETTINGS
# --------------------------------------------
MEMCACHED_HOST=null
MEMCACHED_PORT=null
# --------------------------------------------
# OPTIONAL: S3 PROXY
# When enabled, public uploads (images, logos, avatars) are served through
# the application instead of directly from S3. This allows using a single
# fully private S3 bucket for all storage.
# --------------------------------------------
PUBLIC_S3_PROXY=false
# --------------------------------------------
# OPTIONAL: PUBLIC S3 Settings
# --------------------------------------------
PUBLIC_AWS_SECRET_ACCESS_KEY=null
PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_ENDPOINT=null
PUBLIC_AWS_PATH_STYLE=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: PRIVATE S3 Settings
# --------------------------------------------
PRIVATE_AWS_ACCESS_KEY_ID=null
PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_ENDPOINT=null
PRIVATE_AWS_PATH_STYLE=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: AWS Settings
# --------------------------------------------
AWS_ACCESS_KEY_ID=null
AWS_SECRET_ACCESS_KEY=null
AWS_DEFAULT_REGION=null
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
LOGIN_AUTOCOMPLETE=false
# --------------------------------------------
# OPTIONAL: FORGOTTEN PASSWORD SETTINGS
# --------------------------------------------
RESET_PASSWORD_LINK_EXPIRES=15
PASSWORD_CONFIRM_TIMEOUT=10800
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_DEPRECATIONS=false
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
APP_FORCE_TLS=false
APP_ALLOW_INSECURE_HOSTS=false
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
BACKUP_TIME_LIMIT=600
IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000
API_THROTTLE_PER_MINUTE=120
CSV_ESCAPE_FORMULAS=true
LIVEWIRE_URL_PREFIX=null
MAX_UNPAGINATED=5000
# --------------------------------------------
# OPTIONAL: SAML SETTINGS
# --------------------------------------------
REQUIRE_SAML=false
SAML_KEY_SIZE=2048
# --------------------------------------------
# OPTIONAL: HASHING
# --------------------------------------------
HASHING_DRIVER='bcrypt'
BCRYPT_ROUNDS=10
ARGON_MEMORY=1024
ARGON_THREADS=2
ARGON_TIME=2
# --------------------------------------------
# OPTIONAL: SCIM
# --------------------------------------------
SCIM_TRACE=false
SCIM_STANDARDS_COMPLIANCE=false
-38
View File
@@ -1,38 +0,0 @@
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV='testing-ci'
APP_DEBUG=false
APP_KEY='base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU='
APP_URL='http://localhost:8000'
APP_TIMEZONE='US/Pacific'
APP_LOCALE='en-US'
FILESYSTEM_DISK=local
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=sqlite
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE='sqlite_testing'
DB_USERNAME=root
DB_PASSWORD=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_MAILER=log
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: APP LOG FORMAT
# --------------------------------------------
LOG_CHANNEL=single
-19
View File
@@ -1,19 +0,0 @@
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=testing
APP_DEBUG=true
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE='en-US'
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
-23
View File
@@ -1,23 +0,0 @@
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=mysql
DB_DEFAULT=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=snipeittests
DB_USERNAME=snipeit
DB_PASSWORD=snipe
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# (LOGIN_LOCKOUT_DURATIONin minutes)
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=1000000
LOGIN_LOCKOUT_DURATION=100000000
MAIL_MAILER=log
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME=Snipe-IT
-20
View File
@@ -1,20 +0,0 @@
APP_ENV=testing
APP_DEBUG=true
APP_URL=http://snipe-it.localapp
DB_CONNECTION=sqlite_testing
DB_DEFAULT=sqlite_testing
DB_HOST=localhost
DB_PORT=3306
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# (LOGIN_LOCKOUT_DURATIONin minutes)
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=1000000
LOGIN_LOCKOUT_DURATION=100000000
MAIL_MAILER=log
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME=Snipe-IT
-66
View File
@@ -1,66 +0,0 @@
# Pint: Models
9623fa4d87e7fb38307028338c6991afb7d4e099
# Pint: Actions
a3c7410c35388af08997b1c52adebda1056488a6
# Pint: Console
8bce38b9187d23089a28a4f3a4ab960ac7471e90
# Pint: Enums and Events
f7b82ad1ff513a25d775c20b58e9a8ce23461ec2
# Pint: Exceptions
2e7046a810ce1f7562dec9d3ee4fee0cbc7262db
# Pint: Non-api controllers
9bc92f57c8a29ac0e89c2d3f72f23c6c64567dd8
# Pint: Api controllers
1e5d426e70dcd72fd7e87c2b11ff42fe3cc7a1a4
# Pint: Middleware
ec6caf9b5959c6c57bd7be047e91bbb70fc303a7
# Pint: Requests
93168326da54fa87880570c82df3ccbf3ff152e1
# Pint: Traits and Transformers
a613380811f63f51e2951d2f4b8454d5274d5cdf
# Pint: Importer
3e831bf9b3cc060f11c88ec69a9313131de8ee1f
# Pint: Jobs and Listeners
317b1a462e079bf96d492dd3782de38b7144be9f
# Pint: Livewire
53f2ef2ca11b0571de758b101f08f259de7830cf
# Pint: Mail
de607e7d83704b30f809238c44d3d759196a77db
# Pint: Notifications
31043d1f5cb5d287c0ab2ca2ba1ae08665bc6ad5
# Pint: Observers and policies
b2c0a21230977443536655e43e524773e2ad9e27
# Pint: Presenters
55d46cbefec5fe0bb7e28b859d540977d2cfee46
# Pint: Providers
8b658a19b9182bf9a19e34bc9101ee11a13ed85b
# Pint: Config
c1a93e3ac890ed1fc1c27ba6c431f6b58ff661d6
# Pint: Lang and resources
84fdb5d6c19bf7882cb91d42fe8768fc0db0ce67
# Pint: Database
b5a46a370f85c6e87c8a9fa4a4593424bb027712
# Pint: Tests
d84eb43278177a9bcdfffe04c94d933eb49f2c48
446f5f3cefdc1837a65fd4bc983741b29f821a78
+1 -3
View File
@@ -1,3 +1 @@
* text=auto
public/js/** binary
public/css/** binary
* text=auto
-22
View File
@@ -1,22 +0,0 @@
# Code ownership for pull request reviews, per
# the feature detailed here:
# https://github.com/blog/2392-introducing-code-owners
# https://help.github.com/articles/about-codeowners/
# These owners will be the default owners for everything in the repo.
* @snipe
# Order is important. The last matching pattern has the most precedence.
# So if a pull request only touches javascript files, only these owners
# will be requested to review.
# For example:
# *.js @octocat @github/js
app/Http/Controllers/CustomFields* @uberbrady
app/Http/Controllers/Api/CustomFields* @uberbrady
resources/views/custom_fields/* @uberbrady
docker/* @uberbrady
app/Providers/SamlServiceProvider.php @uberbrady
-5
View File
@@ -1,5 +0,0 @@
# You can add one username per supported platform and one custom link
# patreon: # Replace with your Patreon username
# open_collective: # Replace with your Open Collective username
# ko_fi: # Replace with your Ko-fi username
custom: https://snipeitapp.com/donate
-163
View File
@@ -1,163 +0,0 @@
name: Bug Report
description: File a bug report.
title: "[Bug]: "
projects: ["grokability/snipe-it"]
type: bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Most issues are documented in the [Snipe-IT repository's issues](https://github.com/grokability/snipe-it/issues) or in the official [Common Issues section of the Documentation](https://snipe-it.readme.io/docs/common-issues#/) and are due to the following:
- `.env` misconfiguration
- [Server Permissions](https://snipe-it.readme.io/docs/debugging-permissions#/)
- [Database Migrations](https://snipe-it.readme.io/docs/database-issues#run-migrations)
Please make sure you've checked these resources before submitting a new issue. If you find an existing issue, please add your context to it instead of opening a new issue. If your issue is more of a question, consider [opening a new discussion](https://github.com/grokability/snipe-it/discussions) or [pop by our Discord](https://discord.gg/yZFtShAcKk) instead of creating an issue.
**Please write your bug report in English.** You can use tools like [DeepL](https://www.deepl.com) or [Google Translate](https://translate.google.com/) to translate if necessary.
**If you choose to upload screenshots or videos (which we always encourage), please make sure they do not contain any sensitive information.**
- type: input
id: version
attributes:
label: Snipe-IT Version
description: What version of Snipe-IT are you seeing this issue on? You can find the version number in the footer of any page in Snipe-IT.
placeholder: ex. v8.3.2 - build 19577 (master)
validations:
required: true
- type: input
id: php-version
attributes:
label: PHP Version
description: What version of PHP are you running? You can find the version of PHP your webserver is running in the `Admin Settings` section in the footer, and the cli version by running `php -v` via command line .
placeholder: ex. v8.3.1 (web), PHP 8.4.12 (cli)
validations:
required: true
- type: input
id: composer-version
attributes:
label: Composer Version
description: What version of composer are you running? You can find the version number by running `composer --version`.
placeholder: ex. 2.8.10
validations:
required: true
- type: input
id: db-version
attributes:
label: MySQL/MariaDB version
description: What database are you using, and what version?
placeholder: ex. MySQL 5.7
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: How did you install Snipe-IT?
options:
- Git install
- Manual install (downloading zip/tar.gz)
- Docker
- install.sh
- Hosted by Grokability
- Other
- Not sure
validations:
required: true
- type: dropdown
id: upgrade-or-fresh
attributes:
label: Is this a fresh install or an upgrade?
options:
- Fresh install
- Upgrade
- NA
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see! (Be nice!)
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
- Other
- type: dropdown
id: on-demo
attributes:
label: Can you reproduce this on the public demo?
description: You can check this at https://demo.snipeitapp.com.
options:
- 'Yes'
- 'No'
- N/A
validations:
required: true
- type: dropdown
id: fmcs
attributes:
label: Do you have full multiple company support enabled?
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
options:
- 'Yes'
- 'No'
validations:
required: true
- type: dropdown
id: fmcs-location
attributes:
label: If you have full multiple company support enabled, do you have location scoping to company enabled?
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
options:
- 'Yes'
- 'No'
- I do not have full multiple company support enabled
validations:
required: true
- type: textarea
id: server-logs
attributes:
label: Application log output
description: Please copy and paste any relevant log output from `storage/logs/laravel.log`. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: textarea
id: browser-logs
attributes:
label: Browser console output
description: Please copy and paste any relevant log output from your browser console. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: checkboxes
id: common-issues
attributes:
label: Common Issues
description: Please make sure you have done the following before submitting your issue.
options:
- label: I have searched this repo for existing issues related to my issue (including closed issues)
required: true
- label: My APP_URL is set correctly in my .env file (including http or https and no trailing slash)
required: true
- label: I have searched the official Snipe-IT documentation and have checked the Common Issues documentation (where applicable)
required: true
- label: I have run database migrations (where applicable).
required: true
- label: I have attached screenshots and/or videos of the issue (where applicable)
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/grokability/snipe-it/blob/master/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
@@ -1,38 +0,0 @@
name: Feature Request
description: Request a new feature.
title: "[Feature]: "
projects: ["grokability/snipe-it"]
type: feature
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request! Please make sure to search the existing issues in this repository to see if your feature has already been requested, and feel free to add your context to any existing requests.
**Please write your issue in English.** You can use tools like [DeepL](https://www.deepl.com) or [Google Translate](https://translate.google.com/) to translate if necessary.
**If you choose to upload screenshots or videos (which we always encourage), please make sure they do not contain any sensitive information.**
- type: input
id: version
attributes:
label: Snipe-IT Version
description: What version of Snipe-IT are you currently running? You can find the version number in the footer of any page in Snipe-IT.
placeholder: ex. v8.3.1 - build 19577 (master)
validations:
required: true
- type: textarea
id: feature-description
attributes:
label: How can we help?
description: Let us know in detail what feature you'd like to see added. While we can't promise to implement every feature request, we do read every one and take them into consideration when planning future releases.
placeholder: Tell us what you'd like to see in Snipe-IT! (Be nice!)
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/grokability/snipe-it/blob/master/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
-23
View File
@@ -1,23 +0,0 @@
frontend: ["*.js", "*.css", "*.scss", "*.less", "*.blade.*", "resources/views/livewire/*","resources/views/layouts/default.blade.php"]
skins: ["*.js", "*.css", "*.scss", "*.less"]
css: ["*.css","*.scss", "*.less"]
javascript: ["*.js", "package.json", "package.lock"]
backend: ["/app/*", "composer.json", "composer.lock"]
translations: ["/resources/lang/*"]
livewire: ["/app/Http/Livewire/*", "resources/views/livewire/*"]
blade-components: ["resources/views/blade/*"]
backups: ["*backup*"]
restore: ["*restore*"]
saml: ["*saml*"]
scim: ["*scim*"]
custom fields: ["*fields*", "*fieldsets*"]
dependencies: ["composer.json", "composer.lock", "package.json", "package.lock"]
consumables: ["*consumables*"]
api: ["/app/Http/Controllers/Api/*"]
notifications: ["/app/Notifications/*"]
importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views/livewire/importer.php"]
cli / artisan: ["/app/Console/*"]
LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php", "/resources/views/users/ldap.blade.php","/resources/views/settings/ldap.blade.php"]
docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"]
tests: ["/tests/*", "/database/factories/*", "/stubs"]
config: .github
-33
View File
@@ -1,33 +0,0 @@
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
# Comment to be posted to on first time issues
newIssueWelcomeComment: |
👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: |
💖 Thanks for this pull request! 💖
We use [semantic commit messages](https://snipe-it.readme.io/docs/contributing-overview#section-pull-request-guidelines) to streamline the release process and easily generate changelogs between versions. Before your pull request can be merged, you should **update your pull request title** to start with a semantic prefix if it doesn't have one already.
Examples of commit messages with semantic prefixes:
- `Fixed #<issue number>: don't overwrite prevent_default if default wasn't prevented`
- `Added #<issue number>: add checkout functionality to assets`
- `Improved Asset Checkout: use new notification method for checkout`
Things that will help get your PR across the finish line:
- Document any user-facing changes you've made.
- Include tests when adding/changing behavior.
- Include screenshots and animated GIFs whenever possible.
We get a lot of pull requests on this repo, so please be patient and we will get back to you as soon as we can.
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
firstPRMergeComment: >
Congrats on merging your first pull request! 🎉🎉🎉
-119
View File
@@ -1,119 +0,0 @@
# GitHub Copilot Custom Instructions for Snipe-IT
These instructions guide Copilot to generate code that aligns with modern Laravel 12 standards, PHP 8.2/8.4 features,
software engineering principles, and industry best practices to improve software quality, maintainability, and security.
## ✅ General Coding Standards
- Prefer short, expressive, and readable code.
- Use **meaningful, descriptive variable, function, class, and file names**.
- Apply proper PHPDoc blocks for classes, methods, and complex logic.
- Organize code into small, reusable functions or classes with single responsibility.
- Avoid magic numbers or hard-coded strings; use constants or config files.
## ✅ PHP 8.2/8.4 Best Practices
- Use **readonly properties** to enforce immutability where applicable.
- Use **Enums** instead of string or integer constants.
- Utilize **First-class callable syntax** for callbacks.
- Leverage **Constructor Property Promotion**.
- Use **Union Types**, **Intersection Types**, and **true/false return types** for strict typing.
- Apply **Static Return Type** where needed.
- Use the **Nullsafe Operator (?->)** for optional chaining.
- Adopt **final classes** where extension is not intended.
- Use **Named Arguments** for improved clarity when calling functions with multiple parameters.
## ✅ Laravel 12 Project Structure & Conventions
- Follow the official Laravel project structure:
- `app/Http/Controllers` - Controllers
- `app/Models` - Eloquent models
- `app/Http/Requests` - Form request validation
- `app/Http/Resources` - API resource responses
- `app/Enums` - Enums
- `app/Actions` - Single-responsibility action classes
- `app/Policies` - Authorization logic
- `app/Models/Builders` - Query scoping logic
- Controllers must:
- Use dependency injection.
- Use Form Requests for validation. The request class should utilize the rules set on the model.
- Return typed responses (e.g., `JsonResponse`).
- Use Transformers for API responses.
## ✅ Eloquent ORM & Database
- Use **Eloquent Models** with proper `$fillable` or `$guarded` attributes for mass assignment protection.
- Utilize **casts** for date, boolean, JSON, and custom data types.
- Apply **accessors & mutators** for attribute transformation.
- Avoid direct raw SQL unless absolutely necessary; prefer Eloquent or Query Builder.
- Migrations:
- Always use migrations for schema changes.
- Include proper constraints (foreign keys, unique indexes, etc.).
- Prefer UUIDs or ULIDs as primary keys where applicable.
## ✅ API Development
- Use **Transformer classes** for consistent and structured JSON responses.
- Apply **route model binding** where possible.
- Use Form Requests for input validation.
## ✅ Blade & Frontend (if applicable)
- Keep Blade templates clean and logic-free; use View Composers or dedicated View Models for complex data.
- Use `@props`, `@aware`, `@once` Blade features appropriately.
- Utilize Alpine.js or Livewire for interactive frontend logic (optional).
## ✅ Security Best Practices
- Never trust user input; always validate and sanitize inputs.
- Use prepared statements via Eloquent or Query Builder to prevent SQL injection.
- Use Laravel's built-in CSRF, XSS, and validation mechanisms.
- Store sensitive information in `.env`, never hard-code secrets.
- Apply proper authorization checks using Policies or Gates.
- Follow principle of least privilege for users, roles, and permissions.
## ✅ Testing Standards
- Use **factories** for test data setup.
- Include feature tests for user-facing functionality.
- Include unit tests for business logic, services, and helper classes.
- Mock external services using Laravel's `Http::fake()` or equivalent.
- Maintain high code coverage but focus on meaningful tests over 100% coverage obsession.
## ✅ Software Quality & Maintainability
- Follow **SOLID Principles**:
- Single Responsibility Principle (SRP)
- Open/Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
- Follow **DRY** (Don't Repeat Yourself) and **KISS** (Keep It Simple, Stupid) principles.
- Apply **YAGNI** (You Aren't Gonna Need It) to avoid overengineering.
- Document complex logic with PHPDoc and inline comments.
## ✅ Performance & Optimization
- Eager load relationships to avoid N+1 queries.
- Use caching with Laravel's Cache system for frequently accessed data.
- Paginate large datasets using `paginate()` instead of `get()`.
- Queue long-running tasks using Laravel Queues.
- Optimize database indexes for common queries.
## ✅ Modern Laravel Features to Use
- Use **Event Broadcasting** if real-time updates are needed.
- Use **Full-text search** if search functionality is required.
- Use **Rate Limiting** for API routes.
## ✅ Additional Copilot Behavior Preferences
- Generate **strictly typed**, modern PHP code using latest language features.
- Prioritize **readable, clean, maintainable** code over cleverness.
- Avoid legacy or deprecated Laravel patterns (facade overuse, logic-heavy views, etc.).
- Suggest proper class placement based on Laravel directory structure.
- Suggest tests alongside new features where applicable.
- Default to **immutability**, **dependency injection**, and **encapsulation** best practices.
No newline at end of file
-7
View File
@@ -1,7 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
schedule:
interval: "weekly"
-39
View File
@@ -1,39 +0,0 @@
# This workflow checks out code, performs a CodeQL analysis (for JavaScript) and integrates the results
# with the GitHub Advanced Security code scanning feature.
# More information: https://codeql.github.com/
name: CodeQL Security Scan
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# schedule:
# - cron: '15 17 * * 1'
jobs:
analyze:
name: CodeQL Security Scan
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
-21
View File
@@ -1,21 +0,0 @@
name: Crowdin Action
on:
push:
branches: [ develop ]
jobs:
upload-sources-to-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Crowdin push
uses: crowdin/github-action@v2
with:
upload_sources: true
upload_translations: false
download_translations: false
project_id: ${{ secrets.CROWDIN_PROJECT_ID }}
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
-86
View File
@@ -1,86 +0,0 @@
# Snipe-IT (Alpine) Docker image build for hub.docker.com
name: Docker images (Alpine)
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process
on:
push:
branches:
- master
- develop
tags:
- 'v**'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
pull_request:
permissions:
contents: read
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
# For a new commit on other branches, use the branch name as the tag for Docker image.
# For a new tag, copy that tag name as the tag for Docker image.
IMAGE_TAGS: |
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=tag,suffix=-alpine
type=semver,pattern=v{{major}}-latest-alpine
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
TAGS_FLAVOR: |
latest=false
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v6
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v4
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
###############################################
# Build/Push the 'snipe/snipe-it' image
###############################################
# https://github.com/docker/metadata-action
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
id: meta_build
uses: docker/metadata-action@v6
with:
images: snipe/snipe-it
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
# https://github.com/docker/build-push-action
- name: Build and push 'snipe-it' image
id: docker_build
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile.alpine
platforms: linux/amd64,linux/arm64
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
-86
View File
@@ -1,86 +0,0 @@
# Snipe-IT Docker image build for hub.docker.com
name: Docker images (Ubuntu)
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process
on:
push:
branches:
- master
- develop
tags:
- 'v**'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
pull_request:
permissions:
contents: read
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
# For a new commit on other branches, use the branch name as the tag for Docker image.
# For a new tag, copy that tag name as the tag for Docker image.
IMAGE_TAGS: |
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
type=semver,pattern=v{{major}}-latest
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
TAGS_FLAVOR: |
latest=false
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v6
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v4
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
###############################################
# Build/Push the 'snipe/snipe-it' image
###############################################
# https://github.com/docker/metadata-action
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
id: meta_build
uses: docker/metadata-action@v6
with:
images: snipe/snipe-it
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
# https://github.com/docker/build-push-action
- name: Build and push 'snipe-it' image
id: docker_build
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
@@ -1,22 +0,0 @@
name: Update Docker Hub Description
on:
push:
branches:
- master
- develop
paths:
- README.md
- .github/workflows/dockerhub-description.yml
jobs:
dockerHubDescription:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Docker Hub Description
uses: grokability/dockerhub-description@7ea9d275c7cdbe2b676a093a0308c50665e3b8b4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
repository: snipe/snipe-it
readme-filepath: ./README.md
-40
View File
@@ -1,40 +0,0 @@
name: 'Close stale issues'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
# contents: write # only for delete-branch option
issues: write
# pull-requests: write
steps:
- uses: actions/stale@v10
with:
debug-only: true
ascending: true
operations-per-run: 1000 # just while we're debugging
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
days-before-close: 7
exempt-all-milestones: true
stale-issue-message: >
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!
close-issue-message: >
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.
# There doesn't seem to be a 'reopen issue message'?
# Since there is no 'stale-pr-message' - PR's should not be stale'd
stale-issue-label: stale
exempt-issue-labels: >
pinned,security,:woman_technologist: ready for dev,:moneybag: bounty,:hand: bug,🔐 security,👩‍💻 ready for dev,💰 bounty,✋ bug
-92
View File
@@ -1,92 +0,0 @@
name: Tests in MySQL
on:
push:
branches:
- master
- develop
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: snipeit
ports:
- 33306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-version:
- "8.2"
- "8.3"
- "8.4"
- "8.5"
name: PHP ${{ matrix.php-version }}
steps:
- uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v6
- name: Get Composer Cache Directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Copy .env
run: |
cp -v .env.testing.example .env
cp -v .env.testing.example .env.testing
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Setup Laravel
env:
DB_CONNECTION: mysql
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install --no-interaction
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: mysql
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7
-91
View File
@@ -1,91 +0,0 @@
name: Tests in Postgres
on: workflow_dispatch
jobs:
tests:
runs-on: ubuntu-latest
services:
postgresql:
image: postgres
env:
POSTGRES_DB: snipeit
POSTGRES_USER: snipeit
POSTGRES_PASSWORD: password
ports:
- 5432:5432
options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-version:
- "8.2"
- "8.3"
- "8.4"
- "8.5"
name: PHP ${{ matrix.php-version }}
steps:
- uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v6
- name: Get Composer Cache Directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Copy .env
run: |
cp -v .env.testing.example .env
cp -v .env.testing.example .env.testing
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Setup Laravel
env:
DB_CONNECTION: pgsql
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install --no-interaction
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: pgsql
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7
-76
View File
@@ -1,76 +0,0 @@
name: Tests in SQLite
on:
push:
branches:
- master
- develop
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version:
- "8.5"
name: PHP ${{ matrix.php-version }}
steps:
- uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v6
- name: Get Composer Cache Directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Copy .env
run: |
cp -v .env.testing.example .env
cp -v .env.testing.example .env.testing
- name: Create database file
run: touch database/database.sqlite
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Generate key
run: php artisan key:generate
- name: Setup Passport
run: php artisan passport:keys
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
LOG_CHANNEL: single
LOG_LEVEL: debug
run: php artisan test
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
storage/logs/*.log
if-no-files-found: ignore
retention-days: 7
+16 -69
View File
@@ -1,73 +1,20 @@
.couscous
.DS_Store
.env
.env.testing
phpstan.neon
.idea
/bin/
/bootstrap/compiled.php
/node_modules
/vendor
app/database/*.sqlite
app/storage/meta/services.json
composer.phar
crowdin.yaml
Homestead.json
Homestead.yaml
output
phpDocumentor.phar
public/uploads/*.gif
public/uploads/barcodes/*.png
public/uploads/*.jpg
public/uploads/*.png
public/uploads/*.svg
public/uploads/*.tif
public/uploads/assets/*
public/uploads/avatars/*
public/uploads/logo.gif
public/uploads/logo.png
public/uploads/logo.svg
.DS_Store
/app/config/database.php
/app/config/mail.php
/app/database/*.sqlite
/app/storage/meta/services.json
/app/config/*/mail.php
/app/config/*/database.php
/app/config/*/app.php
public/packages/*
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
storage/logs/*
storage/private_uploads/users/*
tests/_data/scenarios
tests/_output/*
tests/_support/_generated/*
tests/coverage/*
/npm-debug.log
/storage/oauth-private.key
/storage/oauth-public.key
logs/*
*.cache
.vagrant
*.log
*.retry
\.php_cs\.dist
phpmd\.xml
/public/storage
_ide_helper.php
.phpstorm.meta.php
_ide_helper_models.php
/.phplint-cache
storage/ldap_client_tls.cert
storage/ldap_client_tls.key
/storage/framework/testing
/.phpunit.cache
public/uploads/avatars/*
/app/storage/views/*
/app/storage/logs/*
/app/storage/debugbar/
/bin/
.idea
crowdin.yaml
-19
View File
@@ -1,19 +0,0 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
# Make sure .env files not not browseable if in a sub-directory.
<FilesMatch "\.env$">
# Apache 2.2
<IfModule !authz_core_module>
Deny from all
</IfModule>
# Apache 2.4+
<IfModule authz_core_module>
Require all denied
</IfModule>
</FilesMatch>
</IfModule>
-1
View File
@@ -1 +0,0 @@
v18.16.0
-240
View File
@@ -1,240 +0,0 @@
{
"standard": "WCAG2AA",
"level": "error",
"defaults": {
"useIncognitoBrowserContext": false,
"timeout": 500000,
"wait": 5000,
"ignore" : [
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.Fail",
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
],
"viewport": {
"width": 1280,
"height": 1024
}
},
"urls": [
{
"__NOTE" : "this should always be FIRST (if browser context is preserved)",
"url": "https://snipe-it.test/login",
"actions": [
"navigate to https://snipe-it.test/login",
"screen capture tests/pa11y/login.png",
"set field input[name='username'] to admin",
"set field input[name='password'] to password",
"click element button[type=submit]",
"wait for url to be https://snipe-it.test/",
"screen capture tests/pa11y/dashboard.png"
]
},
{
"url" : "https://snipe-it.test/admin",
"actions" : [
"navigate to https://snipe-it.test/admin",
"screen capture tests/pa11y/admin-settings.png"
]
},
{
"url" : "https://snipe-it.test/admin/branding",
"actions" : [
"navigate to https://snipe-it.test/admin/branding",
"screen capture tests/pa11y/admin-branding.png"
]
},
{
"url" : "https://snipe-it.test/admin/general",
"actions" : [
"navigate to https://snipe-it.test/admin/general",
"screen capture tests/pa11y/admin-general.png"
]
},
{
"url" : "https://snipe-it.test/hardware/create",
"actions" : [
"navigate to https://snipe-it.test/hardware/create",
"screen capture tests/pa11y/asset-create.png"
]
},
{
"url" : "https://snipe-it.test/hardware",
"actions" : [
"navigate to https://snipe-it.test/hardware",
"screen capture tests/pa11y/asset-list.png"
]
},
{
"url" : "https://snipe-it.test/hardware/1",
"actions" : [
"navigate to https://snipe-it.test/hardware/1",
"screen capture tests/pa11y/asset-detail.png"
]
},
{
"url" : "https://snipe-it.test/account/view-assets",
"actions" : [
"navigate to https://snipe-it.test/account/view-assets",
"screen capture tests/pa11y/profile.png"
]
},
{
"url" : "https://snipe-it.test/licences",
"actions" : [
"navigate to https://snipe-it.test/licenses",
"screen capture tests/pa11y/license-list.png"
]
},
{
"url" : "https://snipe-it.test/licences/create",
"actions" : [
"navigate to https://snipe-it.test/licenses/create",
"screen capture tests/pa11y/license-create.png"
]
},
{
"url" : "https://snipe-it.test/licences/1",
"actions" : [
"navigate to https://snipe-it.test/licenses/1",
"screen capture tests/pa11y/license-view.png"
]
},
{
"url" : "https://snipe-it.test/consumables",
"actions" : [
"navigate to https://snipe-it.test/consumables",
"screen capture tests/pa11y/consumable-list.png"
]
},
{
"url" : "https://snipe-it.test/consumables/create",
"actions" : [
"navigate to https://snipe-it.test/consumables/create",
"screen capture tests/pa11y/consumable-create.png"
]
},
{
"url" : "https://snipe-it.test/consumables/1",
"actions" : [
"navigate to https://snipe-it.test/consumables/1",
"screen capture tests/pa11y/consumable-view.png"
]
},
{
"url" : "https://snipe-it.test/accessories",
"actions" : [
"navigate to https://snipe-it.test/accessories",
"screen capture tests/pa11y/accessory-list.png"
]
},
{
"url" : "https://snipe-it.test/accessories/create",
"actions" : [
"navigate to https://snipe-it.test/accessories/create",
"screen capture tests/pa11y/accessory-create.png"
]
},
{
"url" : "https://snipe-it.test/accessories/1",
"actions" : [
"navigate to https://snipe-it.test/accessories/1",
"screen capture tests/pa11y/accessory-view.png"
]
},
{
"url" : "https://snipe-it.test/locations",
"actions" : [
"navigate to https://snipe-it.test/locations",
"screen capture tests/pa11y/location-list.png"
]
},
{
"url" : "https://snipe-it.test/locations/create",
"actions" : [
"navigate to https://snipe-it.test/locations/create",
"screen capture tests/pa11y/location-create.png"
]
},
{
"url" : "https://snipe-it.test/locations/1",
"actions" : [
"navigate to https://snipe-it.test/locations/1",
"screen capture tests/pa11y/location-view.png"
]
},
{
"url" : "https://snipe-it.test/models",
"actions" : [
"navigate to https://snipe-it.test/models",
"screen capture tests/pa11y/model-list.png"
]
},
{
"url" : "https://snipe-it.test/models/create",
"actions" : [
"navigate to https://snipe-it.test/models/create",
"screen capture tests/pa11y/model-create.png"
]
},
{
"url" : "https://snipe-it.test/models/1",
"actions" : [
"navigate to https://snipe-it.test/models/1",
"screen capture tests/pa11y/model-view.png"
]
},
{
"url" : "https://snipe-it.test/companies",
"actions" : [
"navigate to https://snipe-it.test/companies",
"screen capture tests/pa11y/company-list.png"
]
},
{
"url" : "https://snipe-it.test/companies/create",
"actions" : [
"navigate to https://snipe-it.test/companies/create",
"screen capture tests/pa11y/company-create.png"
]
},
{
"url" : "https://snipe-it.test/companies/1",
"actions" : [
"navigate to https://snipe-it.test/companies/1",
"screen capture tests/pa11y/company-view.png"
]
},
{
"url" : "https://snipe-it.test/departments",
"actions" : [
"navigate to https://snipe-it.test/departments",
"screen capture tests/pa11y/department-list.png"
]
},
{
"url" : "https://snipe-it.test/departments/create",
"actions" : [
"navigate to https://snipe-it.test/departments/create",
"screen capture tests/pa11y/department-create.png"
]
},
{
"url" : "https://snipe-it.test/departments/1",
"actions" : [
"navigate to https://snipe-it.test/departments/1",
"screen capture tests/pa11y/department-view.png"
]
},
{
"url" : "https://snipe-it.test/invalid-url",
"actions" : [
"navigate to https://snipe-it.test/invalid-url",
"screen capture tests/pa11y/404.png"
]
}
]
}
Executable
+40
View File
@@ -0,0 +1,40 @@
addons:
hosts:
- AlisonMBP
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php
# list any PHP version you want to test against
php:
- 5.4
- 5.5
- 5.6
# optionally specify a list of environments, for example to test different RDBMS
env:
- DB=mysql
# execute any number of scripts before the test run, custom env's are available as variables
before_script:
- if [[ "$DB" == "mysql" ]]; then mysql -e "create database IF NOT EXISTS snipeit_laravel;" -utravis; fi
- curl -s http://getcomposer.org/installer | php
- cp app/config/testing/app.example.php app/config/testing/app.php
- cp app/config/testing/database.example.php app/config/testing/database.php
- cp app/config/testing/mail.example.php app/config/testing/mail.php
- composer self-update
- composer install --prefer-source --no-interaction
- php artisan key:generate --env=testing
- php artisan migrate:install --env=testing --no-interaction -vvv
- php artisan migrate --package cartalyst/sentry --env=testing --no-interaction -vvv
- php artisan migrate --env=testing --no-interaction -vvv
- php artisan db:seed --env=testing --no-interaction -vvv
# omitting "script:" will default to phpunit
# use the $DB env variable to determine the phpunit.xml to use
script: phpunit
# configure notifications (email, IRC, campfire etc)
notifications:
slack:
secure: vv9we1RxB9RsrMbomSdq6D7vz/okobw87pEkgIZjB+hj1QpQ2by90gsPsOa+NgsJEFaEP7e4KlT6SH8kK+zhbmuKaUd3d1//XdcancE22LZXi6tkiB5yuR/Jhhb1LLDqyGJTB4D92hMnnCPiUjpxNA3r437ttNeYRdYIEEP3drA=
-10
View File
@@ -1,10 +0,0 @@
{
"DOC1": "This file is meant to be pulled from the current HEAD of the desired branch, NOT referenced locally",
"DOC2": "In other words, what you see locally are the requirements for your _current_ install",
"DOC3": "Please don't rely on these versions for planning upgrades unless you've fetched the most recent version",
"DOC4": "You should really just ignore it and run upgrade.php. Really",
"php_min_version": "8.2.0",
"php_max_major_minor": "8.4",
"php_max_wontwork": "8.5.0",
"current_snipeit_version": "8.0"
}
Executable
+236
View File
@@ -0,0 +1,236 @@
### v1.1 - Released Nov 06, 2014 02:16:26
* <a href="http://github.com/snipe/snipe-it/commit/fafd03f00f49cb9ffe81d50aa9ca3d1f1fa1d043">view</a> &bull; Added DB Prefix to Migrations
* <a href="http://github.com/snipe/snipe-it/commit/309772e67d62989f1f60141a4a10d724a44c6baa">view</a> &bull; Fixed operator for setting bool display
* <a href="http://github.com/snipe/snipe-it/commit/22f81912e416cfe1227a08b5d8b6323e32e1ef36">view</a> &bull; Fixes #299
* <a href="http://github.com/snipe/snipe-it/commit/86b7b6946f1e1bd404127e228c615e4abe0b20fb">view</a> &bull; Fixes #316 - added model name back to view
* <a href="http://github.com/snipe/snipe-it/commit/9e85dcb9fcb4cc932ecddd23caa6b51e83110543">view</a> &bull; Fixes #315 - add EOL as display option in settings (run migrations)
* <a href="http://github.com/snipe/snipe-it/commit/ee35948980c22f9256241c02d7febd90275f6229">view</a> &bull; fix div issues
* <a href="http://github.com/snipe/snipe-it/commit/0022389463e251e73402450cfbc280cb05633bd1">view</a> &bull; Fix dropdown menu position
* <a href="http://github.com/snipe/snipe-it/commit/343798d241172707145ef365445062db9bb6b99a">view</a> &bull; Merge pull request #322 from madd15/fix-js-delete-asset
* <a href="http://github.com/snipe/snipe-it/commit/1078dbf5c5f6a5bec1a2ef864cebfcc7883d31e6">view</a> &bull; Fixes #324
* <a href="http://github.com/snipe/snipe-it/commit/6b2d3efe3e76429c9c2f5de78f04c4f7e2748a3d">view</a> &bull; Fixes #237 - reporting-only group
* <a href="http://github.com/snipe/snipe-it/commit/e04cc806d9209b54a1833172ebb66ab55b5f0c06">view</a> &bull; Fixes #327
### v1.0 - Released Oct 16, 2014 09:46:47
* <a href="http://github.com/snipe/snipe-it/commit/a4b220d6c31c97f6504023f0b52527825a4cd897">view</a> &bull; Fixed #191 - allow admins to assign a user during asset creation
* <a href="http://github.com/snipe/snipe-it/commit/dc1033025a0bc1b2d657e06c89058c3ab95f2c59">view</a> &bull; Fixed #151 - more complete translations
* <a href="http://github.com/snipe/snipe-it/commit/56d619b218794b43d8c6b182c89c75f83af9dcd9">view</a> &bull; Fixed missing language string
* <a href="http://github.com/snipe/snipe-it/commit/69fca5d1eda0e5f26d2085f7eebc08fafd84a3ae">view</a> &bull; Fixed #58
* <a href="http://github.com/snipe/snipe-it/commit/8197e6174e9f12bcfddd94c6bf606ee36e6801bf">view</a> &bull; Fixes #200
* <a href="http://github.com/snipe/snipe-it/commit/cb8369e9de96aaf1cf9108e22f4038fa6486b881">view</a> &bull; Initial QA Patch to fix errors Add License Add Asset Show Deleted Users License Assign
* <a href="http://github.com/snipe/snipe-it/commit/62a3ed671bbd957075563933f49e8a5445042737">view</a> &bull; DB migration, layout and clone fixes
* <a href="http://github.com/snipe/snipe-it/commit/9ccc7d756f9361757eafc1dce9684ee9f5ecc1c3">view</a> &bull; A0.4.2: Added asset, license, user, model cloning and fixed some bugs.
* <a href="http://github.com/snipe/snipe-it/commit/652f1c746c4757dce9d33d7bd90a9357822c4656">view</a> &bull; Fixes Travis CI build
* <a href="http://github.com/snipe/snipe-it/commit/363d084bd623f778cd72ca7d385a4fb2f67ef474">view</a> &bull; Fixed DB name in travis CI
* <a href="http://github.com/snipe/snipe-it/commit/49c5607e1c8a82a3111123ad84b537644546ebb3">view</a> &bull; Fixes #222
* <a href="http://github.com/snipe/snipe-it/commit/96e40d72b9ac3f74b94b0e39b6f296f0f0b2a1f4">view</a> &bull; fix typo: artisand -> artisan
* <a href="http://github.com/snipe/snipe-it/commit/e978038cc044f60c2eca71b6319063009a57886f">view</a> &bull; Merge pull request #234 from burlito/typo-fix
* <a href="http://github.com/snipe/snipe-it/commit/a829fcf52f933d087c3776993b42311ec9a8988e">view</a> &bull; fix: Use the old asset tag when cloning
* <a href="http://github.com/snipe/snipe-it/commit/ee7d9bec7082dc2e60a4797dea84a0b7443c8090">view</a> &bull; Fixes #279 - QR checkbox not staying checked
* <a href="http://github.com/snipe/snipe-it/commit/bf1e07555160ede517d41f649d6b1d0911f89dd2">view</a> &bull; Fixes #87 - made datatables responsive
### v0.3.11-alpha - Released Jul 19, 2014 05:16:53
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/ac4c905c42d920d2cf8455d644f4c65332a81d0d">view</a> &bull; Fixed #140
* <a href="http://github.com/snipe/snipe-it/commit/4a13cded607415d749151cef495332c48c58cf86">view</a> &bull; Fixed path for Travis composer
* <a href="http://github.com/snipe/snipe-it/commit/ddf946ad5eb6dd8480eeaa709f82af75e0c61444">view</a> &bull; Tried to fix failing build caused by travis composer
### v0.3.10-alpha - Released Jul 17, 2014 05:54:10
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/89a8f3c277d2482b2cd04c4c5c582c9e507770f8">view</a> &bull; Fixes #183 - added employee number to users
* <a href="http://github.com/snipe/snipe-it/commit/a7b809cb91f49fb2c8eaecc67822fa155a0f20ce">view</a> &bull; Updated title/header for users, additional fixes for #151
* <a href="http://github.com/snipe/snipe-it/commit/5266d9a30a9663fa80ff764b25ff71726e5c4f4c">view</a> &bull; Fixed title tag for user list view
* <a href="http://github.com/snipe/snipe-it/commit/43485e1a51330ef585a65b469d7eb85ae7bde13a">view</a> &bull; Fixed language definition
* <a href="http://github.com/snipe/snipe-it/commit/6b5a552d95ff6f6d5567275b50fcd487fb793820">view</a> &bull; Fixed #186 - Bug with select box not retaining selections on edit screen
* <a href="http://github.com/snipe/snipe-it/commit/90eb883b671919ea360122c2bf44433995a12144">view</a> &bull; Fixed #187 - replaced dummy site name with site name from admin panel preferences
* <a href="http://github.com/snipe/snipe-it/commit/f22f23f21dc11d393854849580f0c1229cec9827">view</a> &bull; Fixed button label on forgotten password page
* <a href="http://github.com/snipe/snipe-it/commit/2b9494ed8271254f8f84e016f238d518e6109842">view</a> &bull; Fixed inconsistent password requirements across admin user creation, user profile and forgotten password
* <a href="http://github.com/snipe/snipe-it/commit/9dc40a3a9c161267fd8c88aacc1fbf536c1ec2de">view</a> &bull; Fixed a few CSS quirks
* <a href="http://github.com/snipe/snipe-it/commit/99003e126a8fd0ad9aaea5a550e0af5e306fc1bb">view</a> &bull; Fixed bug with edit (supplier_id), added requestable field for assets
* <a href="http://github.com/snipe/snipe-it/commit/21251b462308f2d783b83be1a331a0faf0813d16">view</a> &bull; Fixed #190, where depreciation on licenses would not be saved
* <a href="http://github.com/snipe/snipe-it/commit/89000440f344f766a31adc69718d7f66df235abf">view</a> &bull; Added admin ability to unsuspend locked accounts #changelog
* <a href="http://github.com/snipe/snipe-it/commit/e203fc516fadf6218f5ce87c070d0cdba9fca188">view</a> &bull; Fixes #194
### v0.3.9-alpha - Released Jul 03, 2014 02:08:38
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/2825bcb4310899e89ccbc73dfa8b59ade28018da">view</a> &bull; Fixed #179 - account page to show assets checked out to the logged in user
* <a href="http://github.com/snipe/snipe-it/commit/c782f29f5aae752a6246be9778d418df172dd4f6">view</a> &bull; Pulled account sub-nav into top menu to free up UI space. #changelog
* <a href="http://github.com/snipe/snipe-it/commit/fad2ec66fa9abe0b34baf16cce453a7e9fc9e4d4">view</a> &bull; More README fixes and refinement
### v0.3.8-alpha - Released Jun 27, 2014 09:28:22
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/3e7a9e45d1954531bbc3b73f2ed59dd16d59c657">view</a> &bull; Fixes PR #168 and #167
* <a href="http://github.com/snipe/snipe-it/commit/945cb3253f1349e30aafa4e7cb57bb507a2ea0cc">view</a> &bull; Fix #117
* <a href="http://github.com/snipe/snipe-it/commit/6f42b3045e2a65cdf98ec1038284cc2ecf01fa5d">view</a> &bull; Resolves #155
* <a href="http://github.com/snipe/snipe-it/commit/380417953b6b67a506f0cc395fd2dfb744cf26cd">view</a> &bull; Fixed missing string
* <a href="http://github.com/snipe/snipe-it/commit/b09aec67f1ab76ff0435a2e1e49b179c3d73bc12">view</a> &bull; Fixes #155
* <a href="http://github.com/snipe/snipe-it/commit/124acc89c4027e346daf54c62cb38b85586de870">view</a> &bull; Resolves #174
* <a href="http://github.com/snipe/snipe-it/commit/afabd2814205fdcd83b7bf9ce9095aa367b08311">view</a> &bull; Additional fixes for #167
* <a href="http://github.com/snipe/snipe-it/commit/a170efae2796314896d548d86b79d89d859c37d2">view</a> &bull; fix for #167
* <a href="http://github.com/snipe/snipe-it/commit/ec9eab81bfb3b35bfe7ad06008b26f19009e6638">view</a> &bull; Closes #177 - change log file added, needs refining
### v.0.3.5-alpha - Released Jun 20, 2014 02:23:09
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/d1499deecdff3c7a4235ffdfad5adbddc1a5098f">view</a> &bull; Provisions for strict mode, fixed license deletion bug
### v.0.3.4-alpha - Released Jun 19, 2014 10:27:59
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/08d05b481f6366ad48fcdce72a8df53755f5dee8">view</a> &bull; Fixes #158
### v.0.3.3-alpha - Released Jun 18, 2014 08:04:46
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/3518ab9f55f7992d6d4f886389d67b062a02f2d8">view</a> &bull; Fixes #139
* <a href="http://github.com/snipe/snipe-it/commit/ad9b1fc446ab15157502fe7c52ddbda455e3ea8f">view</a> &bull; Fixes #139
* <a href="http://github.com/snipe/snipe-it/commit/dfa5d1c64205b23c4c3d16190d1478da8aa0edbb">view</a> &bull; Fixes #136
* <a href="http://github.com/snipe/snipe-it/commit/9714178ce1fa1e2814389e4b6b82eb596a650084">view</a> &bull; Fixes #141
* <a href="http://github.com/snipe/snipe-it/commit/607484b29407718238571eb5269c9c27830338f7">view</a> &bull; Fixes #141 for mysql with strict mode enabled
* <a href="http://github.com/snipe/snipe-it/commit/cbc945299e7f67f5f10dfdc6a875e5c53ac8440a">view</a> &bull; Fixes #148
* <a href="http://github.com/snipe/snipe-it/commit/dc256583d410cb09253e30b075149c79fb4864e5">view</a> &bull; Fixes #144
* <a href="http://github.com/snipe/snipe-it/commit/610c405aa50118163ad5d26d43e4bf6c2456f624">view</a> &bull; More fixes for #151
### v.0.3.2-alpha - Released May 06, 2014 08:26:21
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/f4c5da57d0397877d90aa398267e59fcffee7b47">view</a> &bull; Fixed auth error when user isn't logged in
### v.0.3.1-alpha - Released May 06, 2014 04:48:09
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/efad5db082d1461ea26556fad2f1671a49dc1480">view</a> &bull; Fix for broken datatables in upgrading to v2.2
* <a href="http://github.com/snipe/snipe-it/commit/5690e9ce1f178486179ce6cdfb00ca417acd87dd">view</a> &bull; Fixed code indentation for Apache virtualhost example
* <a href="http://github.com/snipe/snipe-it/commit/67aba76d3f83c5f374c05fe07d52eebd81d13278">view</a> &bull; Fixing this change from #109 that somehow got reverted
* <a href="http://github.com/snipe/snipe-it/commit/06067fc19c0127fdcbb78541e7cdc8e77f5444ac">view</a> &bull; Fixed CSV rexport
### v0.3.0-alpha - Released Jan 29, 2014 01:28:31
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/d91d75abe4df19dd6cd54705ba9967295973ced0">view</a> &bull; Fixing JSON parse errors from #97
* <a href="http://github.com/snipe/snipe-it/commit/baab758377e117a86c3273dd1d6e03e66b02dc8b">view</a> &bull; Fixed language support
* <a href="http://github.com/snipe/snipe-it/commit/a678ec41714f5d7f77418b5933bc112aa67eae9c">view</a> &bull; Fixed Gravatar Hash
* <a href="http://github.com/snipe/snipe-it/commit/c08d672e4923b12972eae76d0699e6a301f911cd">view</a> &bull; Fixes mail config documentation
* <a href="http://github.com/snipe/snipe-it/commit/fba853a6a7a2f8166265cac2e99a526fd9b88c7b">view</a> &bull; Fixes composer formatting issues
* <a href="http://github.com/snipe/snipe-it/commit/c11510fdcac2b2de09ae9e241c846426bdf0b187">view</a> &bull; Removes sorting from actions column. Fixes #56
* <a href="http://github.com/snipe/snipe-it/commit/ae05f2701559a841cb8d9bc8767e6d7a56dcd93c">view</a> &bull; Fixed depreciation bug
* <a href="http://github.com/snipe/snipe-it/commit/d591c1ea0495de091eb0f71daa8572929096c897">view</a> &bull; Fixed bug where RTD = 1 instead of 0
* <a href="http://github.com/snipe/snipe-it/commit/e49420f62d26405b2fa0385f0ae7b3009b3beeaf">view</a> &bull; Fixed bug in depreciation display
* <a href="http://github.com/snipe/snipe-it/commit/1dc7fd1bdf8852fed6d886938f632c8ea69d4798">view</a> &bull; Fixed depreciation display on asset view
* <a href="http://github.com/snipe/snipe-it/commit/211a527ae176df667f6f8fbc95901c53a368d8e2">view</a> &bull; Fixed FK
* <a href="http://github.com/snipe/snipe-it/commit/aaebaa8d36158aafc61988a6370239e85f2e75e5">view</a> &bull; Bugfix to put custom title in the page title
* <a href="http://github.com/snipe/snipe-it/commit/194150bbf0493512018390d011e8d88d144e9763">view</a> &bull; Hacky shit to fix pagination weirdness - will come back to this
* <a href="http://github.com/snipe/snipe-it/commit/5fb649b59e76aa9db6e433ad8c9fc75b4b8d5fc2">view</a> &bull; Fixed error message layout
* <a href="http://github.com/snipe/snipe-it/commit/7bc9a53b133895206a3df230baca50d3879abbe5">view</a> &bull; Fixed error when user is invalid
* <a href="http://github.com/snipe/snipe-it/commit/82de635a75aba376209b741ce1ed4497b91e65e0">view</a> &bull; Fixed invalid seed data
* <a href="http://github.com/snipe/snipe-it/commit/b697c77cd85416ca502ec3bb28c990099c7faae3">view</a> &bull; Fixing error when no depreciation
* <a href="http://github.com/snipe/snipe-it/commit/432f44118f46381bdaa44daf61dfa35677aba0ee">view</a> &bull; Fixed FK in licenses model
* <a href="http://github.com/snipe/snipe-it/commit/4468cbd096e9990300240205baf7139eafbc423c">view</a> &bull; Fixes #112
* <a href="http://github.com/snipe/snipe-it/commit/cd62be0de03da071f344c8f66730cbc9e1475fb7">view</a> &bull; Minor div fix
* <a href="http://github.com/snipe/snipe-it/commit/9c369d72e1e7e29f3c205586c916b2f42b250047">view</a> &bull; Fixed typo
* <a href="http://github.com/snipe/snipe-it/commit/60d94eea04d1ebb93704e6a0d20621ff18708272">view</a> &bull; Changes serial to text area, allow new lines. Fixes #118
* <a href="http://github.com/snipe/snipe-it/commit/580590b4c0b4a2f76ef8edfe02de986267fc7395">view</a> &bull; Fixed === to == (PHP is loosely typed)
### v0.2.0-alpha - Released Nov 29, 2013 10:09:07
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/06cbd0723e415d81ea12aa48f2c37188741b2dbc">view</a> &bull; Fixes #93
* <a href="http://github.com/snipe/snipe-it/commit/e305099b45401c871f16fec23c200080830fcd5d">view</a> &bull; Fixed model view
* <a href="http://github.com/snipe/snipe-it/commit/7003eff42ca0e0ea2b9070187c0afd8ba1a8d8b0">view</a> &bull; More fixes for #88
* <a href="http://github.com/snipe/snipe-it/commit/567f395afaa19335671f366ce889630426f33273">view</a> &bull; More fixes for #88
* <a href="http://github.com/snipe/snipe-it/commit/02f1977d301cd449aa806e02e8a829516d436ef7">view</a> &bull; Fixed error message
* <a href="http://github.com/snipe/snipe-it/commit/11c657ffa4c3bdd01cd04b81d01980b2d795107c">view</a> &bull; Fixes #36
### v0.1.2-alpha - Released Nov 29, 2013 01:59:05
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/b1aa67ddb72158513f0a706996c8c1f5b3609aa5">view</a> &bull; Fixes #69
* <a href="http://github.com/snipe/snipe-it/commit/aa38d83ba6369215451d905e599492214e9f4b8b">view</a> &bull; Fixes #63 and #70
* <a href="http://github.com/snipe/snipe-it/commit/65cd28aad3903e11879e01faba382454dec7e75c">view</a> &bull; Fixes #14
* <a href="http://github.com/snipe/snipe-it/commit/6552401fd869fd285aef5e217438f4f055b53ab6">view</a> &bull; Fixes #77
* <a href="http://github.com/snipe/snipe-it/commit/012a889444fa25b174a5fb2c67e89c8c5fcf09aa">view</a> &bull; Corrected license seat count associated with license. Fixes #75
* <a href="http://github.com/snipe/snipe-it/commit/9e189aa309750c221c27352f35e2ca1f5fab9173">view</a> &bull; Fixes #80, also prevents the user from checking out an asset that is undeployable, etc
* <a href="http://github.com/snipe/snipe-it/commit/8680d98bc36bffa08f87020300b315d26be09928">view</a> &bull; Fixed status display bug
* <a href="http://github.com/snipe/snipe-it/commit/4fcdf72ab6505625f244a6e6ee8683204fb81ccd">view</a> &bull; Responsive design bug fixes, placeholder code for #64 (commented out)
* <a href="http://github.com/snipe/snipe-it/commit/d9a37f38228d55bb6265efc626e3ab7919013362">view</a> &bull; Fixes #88
* <a href="http://github.com/snipe/snipe-it/commit/5858bfb99b2144fbc805ec62cc90e6aaaf019f77">view</a> &bull; Fixed language file paths caused by #88
* <a href="http://github.com/snipe/snipe-it/commit/9f7cbc8a4f01ddf2596e831f658c9a8c321b2f46">view</a> &bull; Another fix related to #88, also accounts for users with invalid location_ids
* <a href="http://github.com/snipe/snipe-it/commit/1b56c31a22afd39a875677714b9c07e96876b8df">view</a> &bull; Fixed relationship between users/locations because fuck you ORM
* <a href="http://github.com/snipe/snipe-it/commit/f0f07b578b2439a627e5e946df0849d88d8526cf">view</a> &bull; Fixing pivot for users
* <a href="http://github.com/snipe/snipe-it/commit/9fa826b2e77ce22d25a8b3094bc825802d25be85">view</a> &bull; Fixed assigned to id
* <a href="http://github.com/snipe/snipe-it/commit/839d01a3af08d780be4152bc02b33bff1a7d7f20">view</a> &bull; Fixed relationship method because fuck you ORM
### v0.1.1-alpha - Released Nov 26, 2013 11:11:00
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/1527b88fd671a20c879c9b2061ae95f3b386ba27">view</a> &bull; Fixes #61
* <a href="http://github.com/snipe/snipe-it/commit/4c0af1eae1031c2630f6e29cc73f8a9dfac1081c">view</a> &bull; Fixed typo causing failure with mysql strict
* <a href="http://github.com/snipe/snipe-it/commit/0e3bcfecd05a39bfea6fc7ba11cb24d9d49e6d86">view</a> &bull; Should be final fixes for mysql strict mode
### Initial alpha - Released Nov 26, 2013 09:01:13
#### This is a pre-release
* <a href="http://github.com/snipe/snipe-it/commit/14972f894bb0e761e2d0170a2c93c9aac4edfca6">view</a> &bull; fixed typo
* <a href="http://github.com/snipe/snipe-it/commit/79da13dff767d57f6f4fbb12f195e25f409ee5ec">view</a> &bull; Fixed layout for category view
* <a href="http://github.com/snipe/snipe-it/commit/0e4e26206bdcc164e5df586368ed45edd45b9a74">view</a> &bull; fixed broken redirect route
* <a href="http://github.com/snipe/snipe-it/commit/9364c46c2df79c0de07dc5dbb724a3c44485b739">view</a> &bull; Fixed licensed link
* <a href="http://github.com/snipe/snipe-it/commit/4f0a1789db01f12ebc4bd47e7e0594bf67b64daf">view</a> &bull; Fixed license blades
* <a href="http://github.com/snipe/snipe-it/commit/27514f85c3ba9056c4609774a482a58605f4d8cf">view</a> &bull; Fixed typo in variable name
* <a href="http://github.com/snipe/snipe-it/commit/ffe786f659e3e8c5b91c27cd33cb77b6d24d3545">view</a> &bull; Fixed routes.
* <a href="http://github.com/snipe/snipe-it/commit/cf59ec8c67e142c0612fb7797448057b19af5cfa">view</a> &bull; Fixed pagination bug
* <a href="http://github.com/snipe/snipe-it/commit/76ac39115347dabac8ada21d981494a3edf7a29c">view</a> &bull; Fixed edit bug where new Category was invoked twice
* <a href="http://github.com/snipe/snipe-it/commit/940281dd4dd0a69b900a052c0bb84e24dde89666">view</a> &bull; Fixed bug on validation display
* <a href="http://github.com/snipe/snipe-it/commit/31d323fb71fcbe94f17f0a2b38183709e8c77151">view</a> &bull; Cosmetic fix on form
* <a href="http://github.com/snipe/snipe-it/commit/8f86ec9dae58cef27387b028f70c8116d649b425">view</a> &bull; Change depreciation to be by month; formatting fixes.
* <a href="http://github.com/snipe/snipe-it/commit/4da2bccbf78a0b630267c6ed75f3a1bd8100e20d">view</a> &bull; Merge pull request #27 from snipe/depreciation_fix
* <a href="http://github.com/snipe/snipe-it/commit/7bc7ffb72813cd176658d536bbdd6be0faa9a45c">view</a> &bull; Fixed some error reporting/required fields around checkout/checkin
* <a href="http://github.com/snipe/snipe-it/commit/48e180c6910d30732559b0cc077fce679344f56e">view</a> &bull; Fixed asset bug caused by unique validation in assets model
* <a href="http://github.com/snipe/snipe-it/commit/57107dceeee81e8618198da33af79fe66bde0d9e">view</a> &bull; Fixed checkin text
* <a href="http://github.com/snipe/snipe-it/commit/7b59f42fb03ee5017c11cf32abc8a23c3f5d2141">view</a> &bull; Fixed pagination bug, styled buttons
* <a href="http://github.com/snipe/snipe-it/commit/ffdb4b1aa8dde9942f7c3b110e9a68006779998b">view</a> &bull; Fixed wonky layout issue
* <a href="http://github.com/snipe/snipe-it/commit/3401fee672d796f4aca8dafced6917c7caf87866">view</a> &bull; Another table fix
* <a href="http://github.com/snipe/snipe-it/commit/ddfd69e5100b19bb05d6b76cb9fad3dbadad1921">view</a> &bull; More table fixes
* <a href="http://github.com/snipe/snipe-it/commit/5c43caf0579a26c1604fc81f5cbdf8f0d43c157d">view</a> &bull; More table fixes
* <a href="http://github.com/snipe/snipe-it/commit/44ba90f9f0a968999a743c35dd2bf153c2edbf1d">view</a> &bull; Fixed icon set to white in main nav
* <a href="http://github.com/snipe/snipe-it/commit/719c36d482cc67f1513351672eda057519f7f735">view</a> &bull; migration to fix a few fields in the log table
* <a href="http://github.com/snipe/snipe-it/commit/5b27a34f050646467a178ff780efc80036aadf1b">view</a> &bull; Fixed fieldname in userlog
* <a href="http://github.com/snipe/snipe-it/commit/76da83cbe1c4334380bb996a40cc374ab4ec12e9">view</a> &bull; Fixed links to edit/checkout
* <a href="http://github.com/snipe/snipe-it/commit/d6a718e99766829e700760c2266b62ad37cdb66b">view</a> &bull; Fixed bug in views for created_at date
* <a href="http://github.com/snipe/snipe-it/commit/0ee08e47e9f2b144084703142ae5eb099090476d">view</a> &bull; Fixed fatal error if admin who created the asset was deleted. (This should never happen, but...)
* <a href="http://github.com/snipe/snipe-it/commit/61cceb5f0b44e7f2a82ccca44be2bc42e8825156">view</a> &bull; Fixed open tag in CSS
* <a href="http://github.com/snipe/snipe-it/commit/c93469c465c0ce8a291d595ac8ccbafbfc68c71a">view</a> &bull; Fixed query for asset/license header boxes
* <a href="http://github.com/snipe/snipe-it/commit/d46556a83d5335913b8b7fe8558f416540290343">view</a> &bull; Fixed phone display if no value set
* <a href="http://github.com/snipe/snipe-it/commit/fc8c3c086ca70a513cd3fff39275fcf035cead5a">view</a> &bull; fixed #37
* <a href="http://github.com/snipe/snipe-it/commit/52020d09f034c9de78b798725c0f3700d0253beb">view</a> &bull; Fixed bomb when asset is unassigned
* <a href="http://github.com/snipe/snipe-it/commit/1b1efec858e8e0cfc5cfafc63bfb537e45191cd1">view</a> &bull; Fixed foreign key in assets
* <a href="http://github.com/snipe/snipe-it/commit/b7251d9992401c04a5da1771af0fcddad2754362">view</a> &bull; Fixed potential bug in blade
* <a href="http://github.com/snipe/snipe-it/commit/9249786a25bc166bf5341f59a11d644cf4f6425c">view</a> &bull; Fixed status display
* <a href="http://github.com/snipe/snipe-it/commit/c32d45e90142b41b567a350af6d399af67994a02">view</a> &bull; Fixed ID
* <a href="http://github.com/snipe/snipe-it/commit/2203789f4c5310599a94799bb3ead9ff9c1ce802">view</a> &bull; Fixes #41
* <a href="http://github.com/snipe/snipe-it/commit/03edbc008c671c868fced94a7761683309d9ab8b">view</a> &bull; Fixes #43
* <a href="http://github.com/snipe/snipe-it/commit/2b088a3fb2ea7711f66e4a887882b17f401ba4bc">view</a> &bull; Fixes #17
* <a href="http://github.com/snipe/snipe-it/commit/ad680a337e50638b4b9865e42e4ea7e453c4151b">view</a> &bull; Fixed column name spelling in DB
* <a href="http://github.com/snipe/snipe-it/commit/718236b2d4bedeaaad80cc601e3e91a3229e81fa">view</a> &bull; Fixed #9
* <a href="http://github.com/snipe/snipe-it/commit/3cc0ed87438b632679ce8b61a7ea0fae48999438">view</a> &bull; Fixed sorting weirdness
* <a href="http://github.com/snipe/snipe-it/commit/57d0f3643518190f0b4a6fe33ac6d83eda647119">view</a> &bull; Fixes #4
* <a href="http://github.com/snipe/snipe-it/commit/3ea0ad87c8733ce1bac0e9c10c2cdaf3254743d5">view</a> &bull; Fixes #22
* <a href="http://github.com/snipe/snipe-it/commit/e87e5b9cae19bfd06b815cf01be309d665409828">view</a> &bull; Fixes #46
* <a href="http://github.com/snipe/snipe-it/commit/7a5b133c5875959c5d7bebf16ea82bac7ceb3989">view</a> &bull; Fixed bug linking to assets when they should be pointing to licenses
* <a href="http://github.com/snipe/snipe-it/commit/e13578d97d6b1ef1990f171beda9b35868fda47c">view</a> &bull; Fixed error when no category is given
* <a href="http://github.com/snipe/snipe-it/commit/45c04b1cdebc0b19243a519d2580af921c82b47b">view</a> &bull; Fixed display bug
* <a href="http://github.com/snipe/snipe-it/commit/4d1dd1c446f76e5530d6fb7f932a77a8da2d0b73">view</a> &bull; Fixed #50 - MOAR VALIDATION!
* <a href="http://github.com/snipe/snipe-it/commit/12958553022d4b7b2184ebf837d4113b23f5cf27">view</a> &bull; Fixes #51
* <a href="http://github.com/snipe/snipe-it/commit/d0cbb9f3a7e4a1fc05ddcabd19e201434c14370b">view</a> &bull; Fixes #52
* <a href="http://github.com/snipe/snipe-it/commit/5bf38b1d02e743af22a51393f1e477100b1f76b7">view</a> &bull; Fixes #50
* <a href="http://github.com/snipe/snipe-it/commit/e6fc1956fc93132d04bf5ac4c0d108c1ae9eee0b">view</a> &bull; Fixes #55
* <a href="http://github.com/snipe/snipe-it/commit/ee3fb2785e719c938b6036f6ea74527c1217aba8">view</a> &bull; Fixed XSS on modal
* <a href="http://github.com/snipe/snipe-it/commit/dc5ad876863a1e17babce06adfb365f38c30ca59">view</a> &bull; Fixes #59
* <a href="http://github.com/snipe/snipe-it/commit/d7a8cac1dc6158983673688c2e48e9a48217b6f2">view</a> &bull; Fixed validation
* <a href="http://github.com/snipe/snipe-it/commit/196710dac807d48f6c73b02c2444a5b8d301b8a6">view</a> &bull; Fixed bug in error validation display
-74
View File
@@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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 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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
Regular → Executable
+18 -3
View File
@@ -1,6 +1,21 @@
### Contributing
# Contribution Guidelines
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please submit all issues and pull requests to the [snipe/snipe-it](http://github.com/snipe/snipe-it) repository in the `develop` branch!
**As you're working on bug-fixes or features, please break them out into their own feature branches and open the pull request against your feature branch**. It makes it _much_ easier to decipher down the road, as you open multiple pull requests over time, and makes it much easier for me to approve pull requests quickly.
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.
If you don't have a feature in mind, but would like to contribute back to the project, check out the [open issues](https://github.com/snipe/snipe-it/issues?state=open) and see if there are any you can tackle.
We use Waffle.io to help better communicate our roadmap with users. Our [project page there](http://waffle.io/snipe/snipe-it) will show you the backlog, what's ready to be worked on, what's in progress, and what's completed.
[![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready&title=Ready)](http://waffle.io/snipe/snipe-it)
The labels we use in GitHub Issues and Waffle.io indicate whether we've confirmed an issue as a bug, whether we're considering the issue as a potential feature, and whether it's ready for someone to work on it. We also provide labels such as "n00b", "intermediate" and "advanced" for the experience level we think it requires for contributors who want to help.
-----
## Translations!
If you're not a coder but want to give back to the project and you're fluent in other languages, that's okay too. We use [CrowdIn](https://crowdin.com) to manage translations, and it makes it super-simple for you to add translations (or validate proposed translations) to the project without messing with code. __Check out [the Snipe-IT CrowdIn translation project here](https://crowdin.com/project/snipe-it/)__.
Thanks!
-75
View File
@@ -1,75 +0,0 @@
Thanks goes to all of these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)) who have helped Snipe-IT get this far:
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=ghost "Code") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/5547470?v=3" width="110px;"/><br /><sub>Hereward</sub>](https://github.com/thehereward)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thehereward "Code") | [<img src="https://avatars0.githubusercontent.com/u/5802977?v=3" width="110px;"/><br /><sub>swoopdk</sub>](https://github.com/swoopdk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=swoopdk "Code") | [<img src="https://avatars1.githubusercontent.com/u/3470403?v=3" width="110px;"/><br /><sub>Abdullah Alansari</sub>](https://linkedin.com/in/ahimta)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Ahimta "Code") | [<img src="https://avatars0.githubusercontent.com/u/796443?v=3" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MicaelRodrigues "Code") | [<img src="https://avatars0.githubusercontent.com/u/614564?v=3" width="110px;"/><br /><sub>Patrick Gallagher</sub>](http://macadmincorner.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=patgmac "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/7165922?v=3" width="110px;"/><br /><sub>Miliamber</sub>](https://github.com/Miliamber)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Miliamber "Code") | [<img src="https://avatars3.githubusercontent.com/u/861766?v=3" width="110px;"/><br /><sub>hawk554</sub>](https://github.com/hawk554)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hawk554 "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1695622?v=3" width="110px;"/><br /><sub>Justin Kerr</sub>](http://jbirdkerr.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbirdkerr "Code") | [<img src="https://avatars3.githubusercontent.com/u/11426176?v=3" width="110px;"/><br /><sub>Ira W. Snyder</sub>](http://www.irasnyder.com/devel/)<br />[📖](https://github.com/snipe/snipe-it/commits?author=irasnyd "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2475759?v=3" width="110px;"/><br /><sub>Aladin Alaily</sub>](https://github.com/aalaily)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aalaily "Code") | [<img src="https://avatars0.githubusercontent.com/u/10247644?v=3" width="110px;"/><br /><sub>Chase Hansen</sub>](https://github.com/kobie-chasehansen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kobie-chasehansen "Code") [💬](#question-kobie-chasehansen "Answering Questions") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Akobie-chasehansen "Bug reports") | [<img src="https://avatars2.githubusercontent.com/u/13545400?v=3" width="110px;"/><br /><sub>IDM Helpdesk</sub>](https://github.com/IDM-Helpdesk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=IDM-Helpdesk "Code") | [<img src="https://avatars2.githubusercontent.com/u/614439?v=3" width="110px;"/><br /><sub>Kai</sub>](http://balticer.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balticer "Code") | [<img src="https://avatars1.githubusercontent.com/u/8762511?v=3" width="110px;"/><br /><sub>Michael Daniels</sub>](http://www.michaeldaniels.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mdaniels5757 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/1532660?v=3" width="110px;"/><br /><sub>Tom Castleman</sub>](http://tomcastleman.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tomcastleman "Code") | [<img src="https://avatars3.githubusercontent.com/u/10723243?v=3" width="110px;"/><br /><sub>Daniel Nemanic</sub>](https://github.com/DanielNemanic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielNemanic "Code") | [<img src="https://avatars0.githubusercontent.com/u/150648?v=3" width="110px;"/><br /><sub>SouthWolf</sub>](https://github.com/southwolf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=southwolf "Code") | [<img src="https://avatars2.githubusercontent.com/u/131616?v=3" width="110px;"/><br /><sub>Ivar Nesje</sub>](https://github.com/ivarne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ivarne "Code") | [<img src="https://avatars1.githubusercontent.com/u/62333?v=3" width="110px;"/><br /><sub>Jérémy Benoist</sub>](http://www.j0k3r.net)<br />[📖](https://github.com/snipe/snipe-it/commits?author=j0k3r "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/724344?v=3" width="110px;"/><br /><sub>Chris Leathley</sub>](https://github.com/cleathley)<br />[🚇](#infra-cleathley "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars0.githubusercontent.com/u/972498?v=3" width="110px;"/><br /><sub>splaer</sub>](https://github.com/splaer)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asplaer "Bug reports") [💻](https://github.com/snipe/snipe-it/commits?author=splaer "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/967362?v=3" width="110px;"/><br /><sub>Joe Ferguson</sub>](http://www.joeferguson.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=svpernova09 "Code") | [<img src="https://avatars3.githubusercontent.com/u/6108682?v=3" width="110px;"/><br /><sub>diwanicki</sub>](https://github.com/diwanicki)<br />[💻](https://github.com/snipe/snipe-it/commits?author=diwanicki "Code") [📖](https://github.com/snipe/snipe-it/commits?author=diwanicki "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/2527115?v=3" width="110px;"/><br /><sub>Lee Thoong Ching</sub>](https://github.com/pakkua80)<br />[📖](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Documentation") [💻](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Code") | [<img src="https://avatars1.githubusercontent.com/u/461491?v=3" width="110px;"/><br /><sub>Marek Šuppa</sub>](http://shu.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrshu "Code") | [<img src="https://avatars1.githubusercontent.com/u/8693762?v=3" width="110px;"/><br /><sub>Juan J. Martinez</sub>](https://github.com/mizar1616)<br />[🌍](#translation-mizar1616 "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1458388?v=3" width="110px;"/><br /><sub>R Ryan Dial</sub>](https://github.com/rrdial)<br />[🌍](#translation-rrdial "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2871745?v=3" width="110px;"/><br /><sub>Andrej Manduch</sub>](https://github.com/burlito)<br />[📖](https://github.com/snipe/snipe-it/commits?author=burlito "Documentation") |
| [<img src="https://avatars0.githubusercontent.com/u/8341172?v=3" width="110px;"/><br /><sub>Jay Richards</sub>](http://www.cordeos.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=technogenus "Code") | [<img src="https://avatars2.githubusercontent.com/u/7295127?v=3" width="110px;"/><br /><sub>Alexander Innes</sub>](https://necurity.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leostat "Code") | [<img src="https://avatars2.githubusercontent.com/u/334485?v=3" width="110px;"/><br /><sub>Danny Garcia</sub>](https://buzzedword.codes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=buzzedword "Code") | [<img src="https://avatars2.githubusercontent.com/u/366855?v=3" width="110px;"/><br /><sub>archpoint</sub>](https://github.com/archpoint)<br />[💻](https://github.com/snipe/snipe-it/commits?author=archpoint "Code") | [<img src="https://avatars1.githubusercontent.com/u/67991?v=3" width="110px;"/><br /><sub>Jake McGraw</sub>](http://www.jakemcgraw.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jakemcgraw "Code") | [<img src="https://avatars1.githubusercontent.com/u/1714374?v=3" width="110px;"/><br /><sub>FleischKarussel</sub>](https://github.com/FleischKarussel)<br />[📖](https://github.com/snipe/snipe-it/commits?author=FleischKarussel "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/319644?v=3" width="110px;"/><br /><sub>Dylan Yi</sub>](https://github.com/feeva)<br />[💻](https://github.com/snipe/snipe-it/commits?author=feeva "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/857740?v=3" width="110px;"/><br /><sub>Gil Rutkowski</sub>](http://FlashingCursor.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [<img src="https://avatars3.githubusercontent.com/u/129360?v=3" width="110px;"/><br /><sub>Desmond Morris</sub>](http://www.desmondmorris.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [<img src="https://avatars2.githubusercontent.com/u/52936?v=3" width="110px;"/><br /><sub>Nick Peelman</sub>](http://peelman.us)<br />[💻](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [<img src="https://avatars0.githubusercontent.com/u/53161?v=3" width="110px;"/><br /><sub>Abraham Vegh</sub>](https://abrahamvegh.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [<img src="https://avatars0.githubusercontent.com/u/2818680?v=3" width="110px;"/><br /><sub>Mohamed Rashid</sub>](https://github.com/rashivkp)<br />[📖](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1509456?v=3" width="110px;"/><br /><sub>Kasey</sub>](http://hinchk.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [<img src="https://avatars2.githubusercontent.com/u/10522541?v=3" width="110px;"/><br /><sub>Brett</sub>](https://github.com/BrettFagerlund)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") |
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/4930051?v=4" width="110px;"/><br /><sub>Wes Hulette</sub>](http://macfoo.wordpress.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jwhulette "Code") | [<img src="https://avatars0.githubusercontent.com/u/8134591?v=4" width="110px;"/><br /><sub>patrict</sub>](https://github.com/patrict)<br />[💻](https://github.com/snipe/snipe-it/commits?author=patrict "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/2611616?v=4" width="110px;"/><br /><sub>Dmitriy Minaev</sub>](https://github.com/VELIKII-DIVAN)<br />[💻](https://github.com/snipe/snipe-it/commits?author=VELIKII-DIVAN "Code") | [<img src="https://avatars0.githubusercontent.com/u/5132245?v=4" width="110px;"/><br /><sub>liquidhorse</sub>](https://github.com/liquidhorse)<br />[💻](https://github.com/snipe/snipe-it/commits?author=liquidhorse "Code") | [<img src="https://avatars1.githubusercontent.com/u/183678?v=4" width="110px;"/><br /><sub>Jordi Boggiano</sub>](https://seld.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Seldaek "Code") | [<img src="https://avatars0.githubusercontent.com/u/653557?v=4" width="110px;"/><br /><sub>Ivan Nieto</sub>](https://github.com/inietov)<br />[💻](https://github.com/snipe/snipe-it/commits?author=inietov "Code") | [<img src="https://avatars2.githubusercontent.com/u/6764151?v=4" width="110px;"/><br /><sub>Ben RUBSON</sub>](https://github.com/benrubson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benrubson "Code") | [<img src="https://avatars2.githubusercontent.com/u/8554558?v=4" width="110px;"/><br /><sub>NMathar</sub>](https://github.com/NMathar)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NMathar "Code") | [<img src="https://avatars1.githubusercontent.com/u/139566?v=4" width="110px;"/><br /><sub>Steffen</sub>](https://github.com/smb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smb "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/6609453?v=4" width="110px;"/><br /><sub>Sxderp</sub>](https://github.com/Sxderp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Sxderp "Code") | [<img src="https://avatars1.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>fanta8897</sub>](https://github.com/fanta8897)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fanta8897 "Code") | [<img src="https://avatars2.githubusercontent.com/u/2576509?v=4" width="110px;"/><br /><sub>Andrey Bolonin</sub>](https://andreybolonin.com/phpconsulting/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreybolonin "Code") | [<img src="https://avatars3.githubusercontent.com/u/2173307?v=4" width="110px;"/><br /><sub>shinayoshi</sub>](http://www.shinayoshi.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=shinayoshi "Code") | [<img src="https://avatars3.githubusercontent.com/u/2130159?v=4" width="110px;"/><br /><sub>Hubert</sub>](https://github.com/reuser)<br />[💻](https://github.com/snipe/snipe-it/commits?author=reuser "Code") | [<img src="https://avatars0.githubusercontent.com/u/6865789?v=4" width="110px;"/><br /><sub>KeenRivals</sub>](https://brashear.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KeenRivals "Code") | [<img src="https://avatars3.githubusercontent.com/u/2902513?v=4" width="110px;"/><br /><sub>omyno</sub>](https://github.com/omyno)<br />[💻](https://github.com/snipe/snipe-it/commits?author=omyno "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/6271335?v=4" width="110px;"/><br /><sub>Evgeny</sub>](https://github.com/jackka)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jackka "Code") | [<img src="https://avatars2.githubusercontent.com/u/1169963?v=4" width="110px;"/><br /><sub>Colin Campbell</sub>](https://digitalist.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=colin-campbell "Code") | [<img src="https://avatars3.githubusercontent.com/u/2872098?v=4" width="110px;"/><br /><sub>Ľubomír Kučera</sub>](https://github.com/lubo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lubo "Code") | [<img src="https://avatars3.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://www.sourceguru.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mezzle "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") | [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars1.githubusercontent.com/u/63399474?v=4" width="110px;"/><br /><sub>johnson-yi</sub>](https://github.com/johnson-yi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=johnson-yi "Code") | [<img src="https://avatars1.githubusercontent.com/u/1862720?v=4" width="110px;"/><br /><sub>Sanjay Govind</sub>](https://tangentmc.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sanjay900 "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") | [<img src="https://avatars1.githubusercontent.com/u/1733625?v=4" width="110px;"/><br /><sub>Dampfklon</sub>](https://github.com/dampfklon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dampfklon "Code") | [<img src="https://avatars2.githubusercontent.com/u/52973156?v=4" width="110px;"/><br /><sub>Charles Hamilton</sub>](https://communityclosing.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chamilton-ccn "Code") | [<img src="https://avatars.githubusercontent.com/u/551789?v=4" width="110px;"/><br /><sub>Giuseppe Iannello</sub>](https://github.com/giannello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=giannello "Code") | [<img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="110px;"/><br /><sub>Peter Dave Hello</sub>](https://www.peterdavehello.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterDaveHello "Code") | [<img src="https://avatars.githubusercontent.com/u/6106332?v=4" width="110px;"/><br /><sub>sigmoidal</sub>](https://github.com/sigmoidal)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sigmoidal "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2082554?v=4" width="110px;"/><br /><sub>Vincent Lainé</sub>](https://github.com/phenixdotnet)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phenixdotnet "Code") | [<img src="https://avatars.githubusercontent.com/u/1943040?v=4" width="110px;"/><br /><sub>Lucas Pleß</sub>](http://www.lucas-pless.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derlucas "Code") | [<img src="https://avatars.githubusercontent.com/u/472804?v=4" width="110px;"/><br /><sub>Ian Littman</sub>](http://twitter.com/iansltx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=iansltx "Code") | [<img src="https://avatars.githubusercontent.com/u/3519029?v=4" width="110px;"/><br /><sub>João Paulo</sub>](https://github.com/PauloLuna)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PauloLuna "Code") | [<img src="https://avatars.githubusercontent.com/u/70443365?v=4" width="110px;"/><br /><sub>ThoBur</sub>](https://github.com/ThoBur)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ThoBur "Code") | [<img src="https://avatars.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") | [<img src="https://avatars.githubusercontent.com/u/438332?v=4" width="110px;"/><br /><sub>Anthony Winstanley</sub>](https://github.com/winstan)<br />[💻](https://github.com/snipe/snipe-it/commits?author=winstan "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3075214?v=4" width="110px;"/><br /><sub>Folke</sub>](https://github.com/fashberg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fashberg "Code") | [<img src="https://avatars.githubusercontent.com/u/1351571?v=4" width="110px;"/><br /><sub>Bennett Blodinger</sub>](https://github.com/benwa)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benwa "Code") | [<img src="https://avatars.githubusercontent.com/u/2974631?v=4" width="110px;"/><br /><sub>NMC</sub>](https://nmc.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ncareau "Code") | [<img src="https://avatars.githubusercontent.com/u/52182449?v=4" width="110px;"/><br /><sub>andres-baller</sub>](https://github.com/andres-baller)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andres-baller "Code") | [<img src="https://avatars.githubusercontent.com/u/67109348?v=4" width="110px;"/><br /><sub>sean-borg</sub>](https://github.com/sean-borg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sean-borg "Code") | [<img src="https://avatars.githubusercontent.com/u/32170051?v=4" width="110px;"/><br /><sub>EDVLeer</sub>](https://github.com/EDVLeer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EDVLeer "Code") | [<img src="https://avatars.githubusercontent.com/u/23075196?v=4" width="110px;"/><br /><sub>Kurokat</sub>](https://github.com/Kurokat)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Kurokat "Code") |
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") | [<img src="https://avatars.githubusercontent.com/u/70129?v=4" width="110px;"/><br /><sub>Joel Pittet</sub>](https://pittet.ca)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joelpittet "Code") | [<img src="https://avatars.githubusercontent.com/u/792695?v=4" width="110px;"/><br /><sub>Eli Young</sub>](https://elyscape.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=elyscape "Code") | [<img src="https://avatars.githubusercontent.com/u/317015?v=4" width="110px;"/><br /><sub>Raell Dottin</sub>](https://github.com/raelldottin)<br />[💻](https://github.com/snipe/snipe-it/commits?author=raelldottin "Code") | [<img src="https://avatars.githubusercontent.com/u/1446856?v=4" width="110px;"/><br /><sub>Tom Misilo</sub>](https://github.com/misilot)<br />[💻](https://github.com/snipe/snipe-it/commits?author=misilot "Code") | [<img src="https://avatars.githubusercontent.com/u/4496300?v=4" width="110px;"/><br /><sub>David Davenne</sub>](http://david.davenne.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JuustoMestari "Code") |
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/qveensi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qveensi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") | [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") |
| [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") | [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") | [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") |
| [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") |
| [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") |
| [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") [💻](https://github.com/snipe/snipe-it/commits?author=swift2512 "Code") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") | [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") |
| [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") | [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/16699443?v=4" width="110px;"/><br /><sub>fvollmer</sub>](https://github.com/fvollmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fvollmer "Code") | [<img src="https://avatars.githubusercontent.com/u/109086466?v=4" width="110px;"/><br /><sub>36864</sub>](https://github.com/36864)<br />[💻](https://github.com/snipe/snipe-it/commits?author=36864 "Code") | [<img src="https://avatars.githubusercontent.com/u/365751?v=4" width="110px;"/><br /><sub>Daniel O'Connor</sub>](http://clockwerx.blogspot.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CloCkWeRX "Code") | [<img src="https://avatars.githubusercontent.com/u/102852568?v=4" width="110px;"/><br /><sub>BeatSpark</sub>](https://github.com/BeatSpark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BeatSpark "Code") | [<img src="https://avatars.githubusercontent.com/u/59203607?v=4" width="110px;"/><br /><sub>mrdahbi</sub>](https://github.com/mrdahbi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrdahbi "Code") | [<img src="https://avatars.githubusercontent.com/u/6661332?v=4" width="110px;"/><br /><sub>Fabian Schmid</sub>](http://sr.solutions)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chfsx "Code") | [<img src="https://avatars.githubusercontent.com/u/1288116?v=4" width="110px;"/><br /><sub>Chris Olin</sub>](https://www.chrisolin.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=realchrisolin "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3803132?v=4" width="110px;"/><br /><sub>Dan</sub>](https://github.com/mnemonicly)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mnemonicly "Code") | [<img src="https://avatars.githubusercontent.com/u/43917728?v=4" width="110px;"/><br /><sub>Nebel</sub>](https://github.com/NebelKreis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NebelKreis "Code") | [<img src="https://avatars.githubusercontent.com/u/132433803?v=4" width="110px;"/><br /><sub>test1337ahp</sub>](https://github.com/test1337ahp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=test1337ahp "Code") | [<img src="https://avatars.githubusercontent.com/u/1916566?v=4" width="110px;"/><br /><sub>Jonathon Reinhart</sub>](https://github.com/JonathonReinhart)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JonathonReinhart "Code") | [<img src="https://avatars.githubusercontent.com/u/484742?v=4" width="110px;"/><br /><sub>aranar-pro</sub>](https://github.com/aranar-pro)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aranar-pro "Code") | [<img src="https://avatars.githubusercontent.com/u/27019397?v=4" width="110px;"/><br /><sub>Phil</sub>](https://github.com/phil-flip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phil-flip "Code") | [<img src="https://avatars.githubusercontent.com/u/6473460?v=4" width="110px;"/><br /><sub>Steffy Fort</sub>](https://fe80.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fe80 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3302372?v=4" width="110px;"/><br /><sub>Jared Busch</sub>](https://github.com/sorvani)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sorvani "Code") | [<img src="https://avatars.githubusercontent.com/u/111956991?v=4" width="110px;"/><br /><sub>seanborg-codethink</sub>](https://github.com/seanborg-codethink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanborg-codethink "Code") | [<img src="https://avatars.githubusercontent.com/u/160669961?v=4" width="110px;"/><br /><sub>dkaatz</sub>](https://github.com/dkaatz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkaatz "Code") | [<img src="https://avatars.githubusercontent.com/u/827205?v=4" width="110px;"/><br /><sub>Daniel Ruf</sub>](https://threema.id/74SF7MW6?text=)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielRuf "Code") | [<img src="https://avatars.githubusercontent.com/u/38883201?v=4" width="110px;"/><br /><sub>ahpaleus</sub>](https://github.com/ahpaleus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ahpaleus "Code") | [<img src="https://avatars.githubusercontent.com/u/22906055?v=4" width="110px;"/><br /><sub>Anh DAO-DUY</sub>](https://github.com/mink-adao-duy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mink-adao-duy "Code") | [<img src="https://avatars.githubusercontent.com/u/4723453?v=4" width="110px;"/><br /><sub>Andres Gutierrez</sub>](https://github.com/Serdnad)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Serdnad "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111083379?v=4" width="110px;"/><br /><sub>Warren White</sub>](https://github.com/wewhite)<br />[💻](https://github.com/snipe/snipe-it/commits?author=wewhite "Code") | [<img src="https://avatars.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://robintemme.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=robintemme "Code") | [<img src="https://avatars.githubusercontent.com/u/47008367?v=4" width="110px;"/><br /><sub>herroworrd</sub>](https://github.com/herroworrd)<br />[💻](https://github.com/snipe/snipe-it/commits?author=herroworrd "Code") | [<img src="https://avatars.githubusercontent.com/u/28558609?v=4" width="110px;"/><br /><sub>vicleos</sub>](https://mubiu.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vicleos "Code") | [<img src="https://avatars.githubusercontent.com/u/1016780?v=4" width="110px;"/><br /><sub>Bob Clough</sub>](http://thinkl33t.co.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thinkl33t "Code") | [<img src="https://avatars.githubusercontent.com/u/10648463?v=4" width="110px;"/><br /><sub>Brandon Daniel Bailey</sub>](https://github.com/brandon-bailey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brandon-bailey "Code") | [<img src="https://avatars.githubusercontent.com/u/23556080?v=4" width="110px;"/><br /><sub>Marc Bartelt</sub>](https://github.com/marcquark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcquark "Code") |
| [<img src="https://avatars.githubusercontent.com/u/18286893?v=4" width="110px;"/><br /><sub>manu-crealytics</sub>](https://github.com/manu-crealytics)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manu-crealytics "Code") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") | [<img src="https://avatars.githubusercontent.com/u/685167?v=4" width="110px;"/><br /><sub>Deloz</sub>](https://deloz.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deloz "Code") | [<img src="https://avatars.githubusercontent.com/u/2682426?v=4" width="110px;"/><br /><sub>Martin Berg</sub>](https://github.com/mbrrg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mbrrg "Code") | [<img src="https://avatars.githubusercontent.com/u/3694534?v=4" width="110px;"/><br /><sub>Richard Schwab</sub>](https://github.com/Nothing4You)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nothing4You "Code") | [<img src="https://avatars.githubusercontent.com/u/8959676?v=4" width="110px;"/><br /><sub>Rick Heil</sub>](https://rickheil.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rickheil "Code") | [<img src="https://avatars.githubusercontent.com/u/397106?v=4" width="110px;"/><br /><sub>Ross Crawford-d'Heureuse</sub>](https://github.com/rosscdh)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rosscdh "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1621107?v=4" width="110px;"/><br /><sub>Ryan McGuire</sub>](https://github.com/McG800)<br />[💻](https://github.com/snipe/snipe-it/commits?author=McG800 "Code") | [<img src="https://avatars.githubusercontent.com/u/77835667?v=4" width="110px;"/><br /><sub>SBrown2021</sub>](https://github.com/SBrown2021)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SBrown2021 "Code") | [<img src="https://avatars.githubusercontent.com/u/8780913?v=4" width="110px;"/><br /><sub>Serkan</sub>](https://github.com/serkanerip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=serkanerip "Code") | [<img src="https://avatars.githubusercontent.com/u/63188620?v=4" width="110px;"/><br /><sub>Shanks</sub>](https://www.yudelei.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Shankschn "Code") | [<img src="https://avatars.githubusercontent.com/u/198525698?v=4" width="110px;"/><br /><sub>cendai-mis</sub>](https://github.com/cendai-mis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cendai-mis "Code") | [<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="110px;"/><br /><sub>Shaun McPeck</sub>](https://smcpeck.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smcpeck "Code") | [<img src="https://avatars.githubusercontent.com/u/1378836?v=4" width="110px;"/><br /><sub>Stephen</sub>](https://github.com/snazy2000)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snazy2000 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/4462739?v=4" width="110px;"/><br /><sub>Steven</sub>](http://nevets82.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nevets82 "Code") | [<img src="https://avatars.githubusercontent.com/u/29017267?v=4" width="110px;"/><br /><sub>Mateus Villar</sub>](https://mateusvillar.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mateus-Romera "Code") | [<img src="https://avatars.githubusercontent.com/u/12749393?v=4" width="110px;"/><br /><sub>Matthew Zackschewski</sub>](https://github.com/mzack5020)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mzack5020 "Code") | [<img src="https://avatars.githubusercontent.com/u/12660103?v=4" width="110px;"/><br /><sub>Matthias Frei</sub>](https://www.frei.media/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=firefrei "Code") | [<img src="https://avatars.githubusercontent.com/u/824840?v=4" width="110px;"/><br /><sub>Nenad Ticaric</sub>](https://github.com/nticaric)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nticaric "Code") | [<img src="https://avatars.githubusercontent.com/u/706439?v=4" width="110px;"/><br /><sub>Nikolay Didenko</sub>](https://github.com/Scorcher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scorcher "Code") | [<img src="https://avatars.githubusercontent.com/u/5457236?v=4" width="110px;"/><br /><sub>Nuno Maduro</sub>](https://nunomaduro.com/sponsorships)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nunomaduro "Code") |
| [<img src="https://avatars.githubusercontent.com/u/8883074?v=4" width="110px;"/><br /><sub>Oliver Walerys</sub>](https://tektikhq.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=owalerys "Code") | [<img src="https://avatars.githubusercontent.com/u/3102039?v=4" width="110px;"/><br /><sub>R. Christian McDonald</sub>](https://keybase.io/rcmcdonald91)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rcmcdonald91 "Code") | [<img src="https://avatars.githubusercontent.com/u/1525581?v=4" width="110px;"/><br /><sub>nix</sub>](https://nnix.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nixn "Code") | [<img src="https://avatars.githubusercontent.com/u/55462380?v=4" width="110px;"/><br /><sub>octobunny</sub>](https://github.com/octobunny)<br />[💻](https://github.com/snipe/snipe-it/commits?author=octobunny "Code") | [<img src="https://avatars.githubusercontent.com/u/8558670?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/sreyemnayr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sreyemnayr "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>p3nj</sub>](https://benji.ltd/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=p3nj "Code") | [<img src="https://avatars.githubusercontent.com/u/6201617?v=4" width="110px;"/><br /><sub>Tim White</sub>](https://github.com/timwsuqld)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timwsuqld "Code") |
| [<img src="https://avatars.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>yannikp</sub>](https://github.com/yannikp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=yannikp "Code") | [<img src="https://avatars.githubusercontent.com/u/20525448?v=4" width="110px;"/><br /><sub>victoria</sub>](https://github.com/viclou)<br />[💻](https://github.com/snipe/snipe-it/commits?author=viclou "Code") | [<img src="https://avatars.githubusercontent.com/u/40685314?v=4" width="110px;"/><br /><sub>Valentyn Tulub</sub>](https://github.com/valentyntu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=valentyntu "Code") | [<img src="https://avatars.githubusercontent.com/u/864520?v=4" width="110px;"/><br /><sub>Wouter van Os</sub>](http://wouter0100.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Wouter0100 "Code") | [<img src="https://avatars.githubusercontent.com/u/3946540?v=4" width="110px;"/><br /><sub>Wyatt Teeter</sub>](https://www.linkedin.com/in/wyatt-teeter)<br />[💻](https://github.com/snipe/snipe-it/commits?author=xWyatt "Code") | [<img src="https://avatars.githubusercontent.com/u/1596124?v=4" width="110px;"/><br /><sub>Yorick Terweijden</sub>](https://github.com/terwey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=terwey "Code") | [<img src="https://avatars.githubusercontent.com/u/69298836?v=4" width="110px;"/><br /><sub>bmkalle</sub>](https://github.com/bmkalle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bmkalle "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28403467?v=4" width="110px;"/><br /><sub>bricelabelle</sub>](https://github.com/bricelabelle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bricelabelle "Code") | [<img src="https://avatars.githubusercontent.com/u/97770090?v=4" width="110px;"/><br /><sub>corydlamb</sub>](https://github.com/corydlamb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=corydlamb "Code") | [<img src="https://avatars.githubusercontent.com/u/1154133?v=4" width="110px;"/><br /><sub>Diogenes S. Jesus</sub>](http://twitter.com/splash)<br />[💻](https://github.com/snipe/snipe-it/commits?author=splashx "Code") | [<img src="https://avatars.githubusercontent.com/u/5826629?v=4" width="110px;"/><br /><sub>D M</sub>](https://github.com/dkmansion)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkmansion "Code") | [<img src="https://avatars.githubusercontent.com/u/14837699?v=4" width="110px;"/><br /><sub>Dustin B</sub>](https://github.com/Jarli01)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jarli01 "Code") | [<img src="https://avatars.githubusercontent.com/u/348344?v=4" width="110px;"/><br /><sub>Fabian Grutschus</sub>](https://github.com/fabiang)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fabiang "Code") | [<img src="https://avatars.githubusercontent.com/u/1491053?v=4" width="110px;"/><br /><sub>MelonSmasher</sub>](https://github.com/MelonSmasher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MelonSmasher "Code") |
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") | [<img src="https://avatars.githubusercontent.com/u/2880129?v=4" width="110px;"/><br /><sub>Sebastian Marsching</sub>](http://sebastian.marsching.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smarsching "Code") | [<img src="https://avatars.githubusercontent.com/u/40658372?v=4" width="110px;"/><br /><sub>Mo</sub>](https://github.com/mohammad-ahmadi1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mohammad-ahmadi1 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/20994684?v=4" width="110px;"/><br /><sub>Owen V. Hayes</sub>](https://github.com/MarvelousAnything)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MarvelousAnything "Code") | [<img src="https://avatars.githubusercontent.com/u/75509373?v=4" width="110px;"/><br /><sub>Peter Gallwas</sub>](https://www.husky.nz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Husky-Devel "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
-138
View File
@@ -1,138 +0,0 @@
FROM ubuntu:24.04
LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
# No need to add `apt-get clean` here, reference:
# - https://github.com/grokability/snipe-it/pull/9201
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
RUN export DEBIAN_FRONTEND=noninteractive; \
export DEBCONF_NONINTERACTIVE_SEEN=true; \
echo 'tzdata tzdata/Areas select Etc' | debconf-set-selections; \
echo 'tzdata tzdata/Zones/Etc select UTC' | debconf-set-selections; \
apt-get update -qqy \
&& apt-get install -qqy --no-install-recommends \
apt-utils \
apache2 \
apache2-bin \
libapache2-mod-php8.3 \
php8.3-curl \
php8.3-ldap \
php8.3-mysql \
php8.3-gd \
php8.3-xml \
php8.3-mbstring \
php8.3-zip \
php8.3-bcmath \
php8.3-redis \
php-memcached \
patch \
curl \
wget \
vim \
git \
cron \
mysql-client \
supervisor \
cron \
gcc \
make \
autoconf \
libc-dev \
libldap-common \
pkg-config \
php8.3-dev \
ca-certificates \
unzip \
dnsutils \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
RUN php go-pear.phar
RUN phpenmod gd
RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/cli/php.ini
RUN useradd -m --uid 10000 --gid 50 docker
RUN echo export APACHE_RUN_USER=docker >> /etc/apache2/envvars
RUN echo export APACHE_RUN_GROUP=staff >> /etc/apache2/envvars
COPY docker/000-default.conf /etc/apache2/sites-enabled/000-default.conf
#SSL
RUN mkdir -p /var/lib/snipeit/ssl
#COPY docker/001-default-ssl.conf /etc/apache2/sites-enabled/001-default-ssl.conf
COPY docker/001-default-ssl.conf /etc/apache2/sites-available/001-default-ssl.conf
RUN a2enmod ssl
RUN a2ensite 001-default-ssl.conf
COPY . /var/www/html
RUN a2enmod rewrite
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
############ INITIAL APPLICATION SETUP #####################
WORKDIR /var/www/html
#Append to bootstrap file (less brittle than 'patch')
# RUN sed -i 's/return $app;/$env="production";\nreturn $app;/' bootstrap/start.php
#copy all configuration files
# COPY docker/*.php /var/www/html/app/config/production/
COPY docker/docker.env /var/www/html/.env
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" \
&& mkdir -p "/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" \
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.cert" "/var/www/html/storage/ldap_client_tls.cert" \
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.key" "/var/www/html/storage/ldap_client_tls.key" \
&& chown docker "/var/lib/snipeit/keys/" \
&& chown -Rh docker "/var/www/html/storage/" \
&& chmod +x /var/www/html/artisan \
&& echo "Finished setting up application in /var/www/html"
############## DEPENDENCIES via COMPOSER ###################
#global install of composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Get dependencies
USER docker
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html && rm -rf /var/www/html/vendor/*/*/.git
USER root
############### APPLICATION INSTALL/INIT #################
#RUN php artisan app:install
# too interactive! Try something else
#COPY docker/app_install.exp /tmp/app_install.exp
#RUN chmod +x /tmp/app_install.exp
#RUN /tmp/app_install.exp
############### DATA VOLUME #################
VOLUME ["/var/lib/snipeit"]
##### START SERVER
COPY docker/startup.sh docker/supervisord.conf /
COPY docker/supervisor-exit-event-listener /usr/bin/supervisor-exit-event-listener
RUN chmod +x /startup.sh /usr/bin/supervisor-exit-event-listener
CMD ["/startup.sh"]
EXPOSE 80
EXPOSE 443
-90
View File
@@ -1,90 +0,0 @@
FROM alpine:3.23
# Apache + PHP
RUN apk add --no-cache \
apache2 \
php84 \
php84-common \
php84-apache2 \
php84-curl \
php84-ldap \
php84-mysqli \
php84-gd \
php84-xml \
php84-mbstring \
php84-zip \
php84-ctype \
php84-tokenizer \
php84-pdo_mysql \
php84-openssl \
php84-bcmath \
php84-phar \
php84-json \
php84-iconv \
php84-fileinfo \
php84-simplexml \
php84-session \
php84-dom \
php84-xmlwriter \
php84-xmlreader \
php84-sodium \
php84-redis \
php84-pecl-memcached \
php84-exif \
curl \
wget \
vim \
git \
mysql-client \
tini
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
# Where apache's PID lives
RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php84/php.ini
COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf
# Enable mod_rewrite
RUN sed -i '/LoadModule rewrite_module/s/^#//g' /etc/apache2/httpd.conf
COPY . /var/www/html
WORKDIR /var/www/html
COPY docker/docker.env /var/www/html/.env
RUN chown -R apache:apache /var/www/html
RUN \
rm -r "/var/www/html/storage/private_uploads" \
&& mkdir -p "/var/lib/snipeit/data/private_uploads" && ln -fs "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" \
&& rm -rf "/var/www/html/public/uploads" \
&& mkdir -p "/var/lib/snipeit/data/uploads" && ln -fs "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" \
&& mkdir -p "/var/lib/snipeit/dumps" && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
&& mkdir -p "/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 -hR apache "/var/www/html/storage/" \
&& chown -R apache "/var/lib/snipeit"
# Install composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN mkdir -p /var/www/.composer && chown apache /var/www/.composer
# Install dependencies
USER apache
RUN COMPOSER_CACHE_DIR=/dev/null composer install --working-dir=/var/www/html
USER root
VOLUME ["/var/lib/snipeit"]
# Startup script
COPY docker/startup_alpine.sh /startup.sh
RUN chmod +x /startup.sh
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/startup.sh"]
EXPOSE 80
-103
View File
@@ -1,103 +0,0 @@
ARG ENVIRONMENT=production
ARG SNIPEIT_RELEASE=6.1.0
ARG PHP_VERSION=8.2
ARG PHP_ALPINE_VERSION=3.17
ARG COMPOSER_VERSION=2
# Cannot use arguments with 'COPY --from' workaround
# https://github.com/moby/moby/issues/34482#issuecomment-454716952
FROM composer:${COMPOSER_VERSION} AS composer
# Final stage
FROM php:${PHP_VERSION}-fpm-alpine${PHP_ALPINE_VERSION} AS source
LABEL maintainer="Mateus Villar <mromeravillar@gmail.com>"
ARG PACKAGES="\
mysql-client \
"
ARG DEV_PACKAGES="\
git \
"
ARG ENVIRONMENT
ENV ENVIRONMENT ${ENVIRONMENT}
ARG SNIPEIT_RELEASE
ENV SNIPEIT_RELEASE ${SNIPEIT_RELEASE}
# Cribbed from wordpress-fpm-alpine image
# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN set -eux; \
docker-php-ext-enable opcache; \
{ \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=4000'; \
echo 'opcache.revalidate_freq=2'; \
echo 'opcache.fast_shutdown=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging
RUN { \
# https://www.php.net/manual/en/errorfunc.constants.php
# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670
echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \
echo 'display_errors = Off'; \
echo 'display_startup_errors = Off'; \
echo 'log_errors = On'; \
echo 'error_log = /dev/stderr'; \
echo 'log_errors_max_len = 1024'; \
echo 'ignore_repeated_errors = On'; \
echo 'ignore_repeated_source = Off'; \
echo 'html_errors = Off'; \
} > /usr/local/etc/php/conf.d/error-logging.ini
# Install php extensions inside docker containers easily
# https://github.com/mlocati/docker-php-extension-installer
COPY --from=mlocati/php-extension-installer:2.1.15 /usr/bin/install-php-extensions /usr/local/bin/
RUN set -eux; \
install-php-extensions \
bcmath \
gd \
ldap \
mysqli \
pdo_mysql \
zip; \
rm -f /usr/local/bin/install-php-extensions; \
# Install prerequisites packages
apk add --no-cache \
${PACKAGES};
COPY --from=composer /usr/bin/composer /usr/local/bin
ARG COMPOSER_ALLOW_SUPERUSER=1
RUN set -eux; \
# Download and extract snipeit tarball
curl -o snipeit.tar.gz -fL "https://github.com/grokability/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \
rm snipeit.tar.gz; \
# Install composer php dependencies
if [ "$ENVIRONMENT" = "production" ]; then \
echo "production environment detected!"; \
composer update \
--no-cache \
--no-dev \
--optimize-autoloader \
--working-dir=/var/www/html; \
else \
echo "development environment detected!"; \
apk add --no-cache \
${DEV_PACKAGES}; \
composer update \
--no-cache \
--prefer-source \
--optimize-autoloader \
--working-dir=/var/www/html; \
fi; \
rm -f /usr/local/bin/composer; \
chown -R www-data:www-data /var/www/html;
VOLUME [ "/var/lib/snipeit" ]
COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env
COPY --chmod=655 docker/startup_alpine_fpm.sh /startup.sh
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
ENTRYPOINT [ "/startup.sh" ]
CMD [ "/startup.sh", "php-fpm" ]
-1
View File
@@ -1 +0,0 @@
web: php heroku/startup.php && heroku-php-apache2 public/
Regular → Executable
+45 -128
View File
@@ -1,150 +1,67 @@
![snipe-it-by-grok](https://github.com/grokability/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
[![Click here to lend your support to: Snipe IT - Free Open Source Asset Management System and make a donation at pledgie.com](https://pledgie.com/campaigns/22899.png?skin_name=chrome)](https://pledgie.com/campaigns/22899) [![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&title=Ready)](http://waffle.io/snipe/snipe-it) [![ProjectStatus](http://stillmaintained.com/snipe/snipe-it.png)](http://stillmaintained.com/snipe/snipe-it) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.png)](https://crowdin.com/project/snipe-it)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Tests in MySQL](https://github.com/grokability/snipe-it/actions/workflows/tests-mysql.yml/badge.svg)](https://github.com/grokability/snipe-it/actions/workflows/tests-mysql.yml)
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributing) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
## Snipe-IT - Open Source Asset Management System
## Snipe-IT - Asset Management For the Rest of Us
[![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)
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
It is built on [Laravel 11](http://laravel.com).
It is built on [Laravel 4.1](http://laravel.com) and uses the [Sentry 2](https://github.com/cartalyst/sentry) package.
Snipe-IT is actively developed and we [release quite frequently](https://github.com/grokability/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
This project is being actively developed and we're [releasing quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](http://snipeitapp.com/demo.php).)
> [!TIP]
> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
__This is web-based software__. This means there there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, flavor of Linux, as well as Windows.
-----
### Documentation & Installation
### Table of Contents
* [Installation](#installation)
* [User's Manual](#users-manual)
* [Bug Reports & Feature Requests](#bug-reports--feature-requests)
* [Security](#security)
* [Upgrading](#upgrading)
* [Translations!](#translations-)
* [Libraries, Modules & Related Projects](#libraries-modules--related-projects)
* [Join the Community!](#join-the-community)
* [Contributing](#contributing)
* [Announcement List](#announcement-list)
__Installation and configuration documentation for this project has been moved to http://docs.snipeitapp.com.__ This provides a more easily navigated, organized view of the documentation, and is based off of the [documentation branch](https://github.com/snipe/snipe-it/tree/documentation) in this repo. Contributions and bugfixes to the documentation are always welcome!
We'll be adding a long-overdue user's manual soon as well.
-----
### Installation
For instructions on installing and configuring Snipe-IT on your server, check out the [installation manual](https://snipe-it.readme.io/docs). (Please see the [requirements documentation](https://snipe-it.readme.io/docs/requirements) for full requirements.)
If you're having trouble with the installation, please check the [Common Issues](https://snipe-it.readme.io/docs/common-issues) and [Getting Help](https://snipe-it.readme.io/docs/getting-help) documentation, and search this repository's open *and* closed issues for help.
-----
### User's Manual
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
__To deploy on Ubuntu using Ansible and Vagrant, be sure to check out the [Snipe-IT Installation scripts](https://github.com/GR360RY/snipeit-ansible) created by [@GR360RY](https://github.com/GR360RY/).__
-----
### Bug Reports & Feature Requests
Feel free to check out the [GitHub Issues for this project](https://github.com/grokability/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open and closed) to see if your question hasn't already been answered before opening a new issue.
> [!IMPORTANT]
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
We use Waffle.io to help better communicate our roadmap with users. Our [project page there](http://waffle.io/snipe/snipe-it) will show you the backlog, what's ready to be worked on, what's in progress, and what's completed.
[![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready&title=Ready)](http://waffle.io/snipe/snipe-it)
-----
### Security
> [!IMPORTANT]
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**
-----
### Upgrading
Please see the [upgrading documentation](https://snipe-it.readme.io/docs/upgrading) for instructions on upgrading Snipe-IT.
------
### Translations!
Please see the [translations documentation](https://snipe-it.readme.io/docs/translations) for information about available languages and how to add translations to Snipe-IT.
-----
### 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.
> [!NOTE]
> 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. :)
#### Libraries & Modules
- [SnipeScheduler](https://github.com/JSY-Ben/SnipeScheduler) by [@JSY-Ben](https://github.com/JSY-Ben) - An Asset Reservation/Checkout System for Snipe-IT
- [Snipe-IT MCP Server](https://github.com/jameshgordy/snipeit-mcp) by [@jameshgordy](https://github.com/jameshgordy) - A Model Context Protocol (MCP) server for managing Snipe-IT inventory systems
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/grokability/jamf2snipe) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
- [jamf-snipe-rename](https://macblog.org/jamf-snipe-rename/) - Python script to rename computers in Jamf from Snipe-IT
- [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
- [Rudder2Snipe](https://github.com/norbertoaquino/rudder2snipe) by [@norbertoaquino](https://github.com/norbertoaquino) - Rudder.io integration for Snipe-IT
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-IT.
- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT.
- [WWW::SnipeIT](https://github.com/SEDC/perl-www-snipeit) by [@SEDC](https://github.com/SEDC) - perl module for accessing the API
- [UniFi to Snipe-IT](https://www.edtechirl.com/p/snipe-it-and-azure-asset-management) originally by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT.
- [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT.
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by [@ReticentRobot](https://github.com/ReticentRobot) - Windows agent for Snipe-IT.
- [Gate Pass Generator](https://github.com/cha7uraAE/snipe-it-gate-pass-system) by [@cha7uraAE](https://github.com/cha7uraAE) - A Streamlit application for generating gate passes based on hardware data from a Snipe-IT API.
- [InQRy (archived)](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
- [Marksman (archived)](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
- [Python Module (archived)](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
We also have a handful of [Google Apps scripts](https://github.com/grokability/google-apps-scripts-for-snipe-it) to help with various tasks.
#### Mobile Apps
We're currently working on our own mobile app, but in the meantime, check out these third-party apps that work with Snipe-IT:
- [SnipeMate](https://snipemate.app/) (iOS, Google Play, Huawei AppGallery) by Mars Technology
- [Snipe-Scan](https://apps.apple.com/do/app/snipe-scan/id6744179400?uo=2) (iOS) by Nicolas Maton
- [Snipe-IT Assets Management](https://play.google.com/store/apps/details?id=com.diegogarciadev.assetsmanager.snipeit&hl=en&pli=1) (Google Play) by DiegoGarciaDEV
- [AssetX](https://apps.apple.com/my/app/assetx-for-snipe-it/id6741996196?uo=2) (iOS) for Snipe-IT by Rishi Gupta
-----
### Join the Community!
- **[Join our Discord](https://discord.gg/yZFtShAcKk)!** Its full of great people. We even wrote about it [here](https://grokstar.dev/culture/2024/06/the-unlikely-rise-of-discord-as-a-support-channel/)!
- **Follow us on Bluesky** at [@snipeitapp.com](https://bsky.app/profile/snipeitapp.com)
- **Follow us on Mastodon** at [hachyderm.io/@grokability](https://hachyderm.io/@grokability)
- **Follow our blog** at [Grokstar.Dev](https://grokstar.dev)
- **Subscribe here** on Github for notifications about new releases. (We recommend selecting "Releases" only for most users - this repo can get noisy.)
-----
### Contributing
**Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.** Please see our [AI Contribution Policy](https://snipe-it.readme.io/docs/contributing-overview#ai-usage-policy) for more information.
Contributions should follow from a human-to-human discussion in the form of an issue for the best chances of being merged into the core project. (Sometimes we might already be working on that feature, sometimes we've decided against )
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
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.
The ERD is available [online here](https://drawsql.app/templates/snipe-it).
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
-----
### Star History
[![Star History Chart](https://api.star-history.com/svg?repos=grokability/snipe-it&type=Date)](https://www.star-history.com/#grokability/snipe-it&Date)
------
### Announcement List
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
We also usually make smaller announcements on our social accounts, our Discord, and our blog, so be sure to subscribe to those if you're looking for more granular announcements.
### Translations!
If you're not a coder but want to give back to the project and you're fluent in other languages, consider helping out with the translations. We use [CrowdIn](https://crowdin.com) to manage translations, and it makes it super-simple for you to add translations to the project without messing with code. Check out [the Snipe-IT CrowdIn translation project here](https://crowdin.com/project/snipe-it/).
We currently have English, Chinese, Finnish, Malay, Romanian and Spanish completed, and are looking for lots more!
-----
## Requirements
- PHP 5.4 or later
- MCrypt PHP Extension
- Fileinfo Extension
And _one_ of the following image libraries (for QR codes, asset model image and user avatar image uploads).
- GD Library (>=2.0) … or …
- Imagick PHP extension (>=6.3.8)
-----
## Important Notes on Updating
Whenever you pull down a new version from master or develop, when you grab the [latest official release](https://github.com/snipe/snipe-it/releases), make sure to run the following commands via command line:
php composer.phar dump-autoload
php artisan migrate
Forgetting to do this can mean your DB might end up out of sync with the new files you just pulled, or you may have some funky cached autoloader values. It's a good idea to get into the habit of running these every time you pull anything new down. If there are no database changes to migrate, it won't hurt anything to run migrations anyway.
-41
View File
@@ -1,41 +0,0 @@
# Security Policy
We take security issues very seriously, and will always attempt to address any
vulnerabilities as quickly as possible.
## Supported Versions
We try to make a reasonable effort to support older versions of Snipe-IT,
however there are times when library dependencies and/or PHP/MySQL dependencies
make it impossible to backport security fixes on older versions.
| Version | Supported |
|---------|--------------------|
| 8.x | :white_check_mark: |
| 7.x | :x: |
| 6.x | :x: |
| 5.1.x | :x: |
| 5.0.x | :x: |
| 4.0.x | :x: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Security vulnerabilities should be sent to security@snipeitapp.com. You can typically expect a
response within two business days, and we typically have fixes out in under a week from the initial disclosure.
This obviously varies based on the severity of the security issue and the difficulty in remediation, but those have
historically been the timelines we work around.
We do ask that you do not disclose the vulnerability publicly until we have had a chance to address it and tag a release
so that we can protect our users, and we will work
with you to coordinate a public disclosure once we have a fix out. We will also work with you to ensure that you receive
appropriate credit for the discovery of the vulnerability, if you would like to be credited. (Please provide a GitHub
username or other information if you would like to be credited, and please let us know if you would like to remain
anonymous.)
For responsible disclosure, we ask that you give us at least __90 days__ to address the issue before disclosing it
publicly,
but we will work with you if you need to disclose it sooner than that.
For a full breakdown of our security policies, please see https://snipeitapp.com/security.
-65
View File
@@ -1,65 +0,0 @@
# Running the Test Suite
This document is targeted at developers looking to make modifications to this application's code base and want to run the existing test suite.
Before starting, follow the [instructions](README.md#installation) for installing the application locally and ensure you can load it in a browser properly.
## Unit and Feature Tests
Before attempting to run the test suite copy the example environment file for tests and update the values to match your environment:
`cp .env.testing.example .env.testing`
The following should work for running tests in memory with sqlite:
```
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=testing
APP_DEBUG=true
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en-US
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=sqlite_testing
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=null
#DB_USERNAME=null
#DB_PASSWORD=null
```
To use MySQL you should update the `DB_` variables to match your local test database:
```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE={}
DB_USERNAME={}
DB_PASSWORD={}
```
Now you are ready to run the entire test suite from your terminal:
```shell
php artisan test
````
To run individual test files, you can pass the path to the test that you want to run:
```shell
php artisan test tests/Unit/AccessoryTest.php
```
Some tests, like ones concerning LDAP, are marked with the `@group` annotation. Those groups can be run, or excluded, using the `--group` or `--exclude-group` flags:
```shell
php artisan test --group=ldap
php artisan test --exclude-group=ldap
```
This can be helpful if a set of tests are failing because you don't have an extension, like LDAP, installed.
Vendored
-111
View File
@@ -1,111 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/grokability/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 "forwarded_port", guest: 80, host: 8080
bionic.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
bionic.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "xenial" do |xenial|
xenial.vm.box = "ubuntu/xenial64"
xenial.vm.hostname = 'xenial'
xenial.vm.network "forwarded_port", guest: 80, host: 8080
xenial.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
xenial.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty32"
trusty.vm.hostname = 'trusty'
trusty.vm.network "forwarded_port", guest: 80, host: 8080
trusty.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
trusty.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
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
config.vm.define "freebsd" do |freebsd|
freebsd.vm.box = "freebsd/FreeBSD-11.2-RELEASE"
freebsd.vm.hostname = 'freebsd12'
freebsd.vm.network "forwarded_port", guest: 80, host: 8080
freebsd.vm.network "forwarded_port", guest:3306, host:3306 # mysql
freebsd.vm.network "private_network", type: "dhcp"
freebsd.ssh.shell = "sh"
freebsd.vm.base_mac = "080027D14C66"
freebsd.vm.synced_folder ".", "/vagrant", :nfs => true, id: "vagrant-root",
:mount_options => ['rw', 'vers=3', 'tcp', 'actimeo=2']
freebsd.vm.provision "shell", inline: <<-SHELL
pkg install -y python27;
SHELL
freebsd.vm.provision "ansible" do |ansible|
ansible.playbook = "ansible/freebsd/vagrant_playbook.yml"
end
end
end
-260
View File
@@ -1,260 +0,0 @@
---
- name: Set up local server
hosts: all
remote_user: vagrant
become_user: root
become_method: sudo
vars:
- ansible_python_interpreter: /usr/local/bin/python2.7
gather_facts: no
# Tasks
tasks:
#
# Update the PKG database
#
- name: Upgrade PKG database
raw: sudo pkg upgrade -y
#
# Mount the shared folders
#
- name: Update Vagrant Shared Folders
command: "{{ item }}"
with_items:
- sysrc rpc_lockd_enable=YES
- sysrc rpc_statd_enable=YES
become: true
#
# Install required utilities
#
- name: Install Utilities
pkgng:
name: "{{ item }}"
state: present
with_items:
- openssl
- node
- npm
- git
- nano
- wget
- bash
become: true
#
# Install php and php dependancies
#
- name: Install PHP dependancies
pkgng:
name: "{{ item }}"
state: present
with_items:
- php72
- php72-zip
- php72-zlib
- php72-extensions
- php72-mbstring
- php72-openssl
# - php72-mysqli
- php72-curl
- php72-soap
- php72-pdo_mysql
# - php72-pdo_pgsql
- php72-ldap
- php72-curl
- php72-fileinfo
- php72-bcmath
- php72-gd
become: true
#
# Create a php.ini file
#
- name: PHP INI check
stat:
path: /usr/local/etc/php.ini
register: php_ini_exits
- name: Create PHP ini
command: cp /usr/local/etc/php.ini-development /usr/local/etc/php.ini
become: true
when: not php_ini_exits.stat.exists
- name: Enable PHP-FPM auto-start
command: sysrc php_fpm_enable=YES
become: true
- name: Start PHP-FPM service
service:
name: php-fpm
state: started
become: true
#
# Install the lastest version of composer
#
- name: Composer check
stat:
path: /usr/local/bin/composer
register: composer_exits
- name: Install Composer
shell: |
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
mv composer.phar /usr/local/bin/composer
exit $RESULT
when: not composer_exits.stat.exists
become: true
#
# Install MySQL Server
- name: Install MySQL 5.7
pkgng:
name: mysql57-server
state: present
become: true
register: sql_server
- name: Start MySQL server
service:
name: mysql-server
state: started
become: true
- name: MySQL 5.7 auto-start
command: sysrc mysql_enable=YES
become: true
when: sql_server.changed == true
- name: Get MySQL root password
command: tail -1 /root/.mysql_secret
register: myql_root_pwd
become: true
when: sql_server.changed == true
- name: Change MySQL root password
command: mysqladmin -u root -p'{{myql_root_pwd.stdout}}' password vagrant
when: sql_server.changed == true
- name: Enable remote mysql
replace:
path: /usr/local/etc/mysql/my.cnf
regexp: "127.0.0.1"
replace: "0.0.0.0"
become: true
when: sql_server.changed == true
- name: Grant user vagrant privelages
shell: mysql -u root -pvagrant -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'vagrant' WITH GRANT OPTION; FLUSH PRIVILEGES;"
become: true
when: sql_server.changed == true
ignore_errors: true
- name: Restart MySQL server
service:
name: mysql-server
state: restarted
become: true
#
# Install Apache Web Server
#
- name: Install Apache 2.4
pkgng:
name: apache24
state: present
become: true
register: apache24_server
- name: Apache 2.4 auto-start
command: sysrc apache24_enable=YES
become: true
when: apache24_server.changed == true
- name: Enable Apache modules
replace:
path: /usr/local/etc/apache24/httpd.conf
regexp: "#{{ item }}"
replace: "{{ item }}"
become: true
with_items:
- LoadModule rewrite_module libexec/apache24/mod_rewrite.so
- LoadModule vhost_alias_module libexec/apache24/mod_vhost_alias.so
- LoadModule deflate_module libexec/apache24/mod_deflate.so
- LoadModule expires_module libexec/apache24/mod_expires.so
- LoadModule mpm_worker_module libexec/apache24/mod_mpm_worker.so
- LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so
- LoadModule proxy_module libexec/apache24/mod_proxy.so
- Include etc/apache24/extra/httpd-vhosts.conf
when: apache24_server.changed == true
- name: Disable Apache modules
replace:
path: /usr/local/etc/apache24/httpd.conf
regexp: "{{ item }}"
replace: "#{{ item }}"
become: true
with_items:
- LoadModule mpm_prefork_module libexec/apache24/mod_mpm_prefork.so
when: apache24_server.changed == true
- name: Backup vhosts
command: cp /usr/local/etc/apache24/extra/httpd-vhosts.conf /usr/local/etc/apache24/extra/httpd-vhosts.conf.bak
become: true
when: apache24_server.changed == true
- name: Truncate vhosts
command: truncate -s 0 /usr/local/etc/apache24/extra/httpd-vhosts.conf
become: true
when: apache24_server.changed == true
- name: Set up vhost
blockinfile:
path: "/usr/local/etc/apache24/extra/httpd-vhosts.conf"
block: |
<VirtualHost *>
DocumentRoot /usr/local/www/apache24/data/public
ServerName vagrant.app
ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/usr/local/www/apache24/data/public/$1
DirectoryIndex /index.php index.php
<Directory /usr/local/www/apache24/data/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
become: true
when: apache24_server.changed == true
- name: Map apache dir to local folder
shell: |
if ! [ -L /var/www ]; then
rm -rf /usr/local/www/apache24/data;
ln -fs /vagrant /usr/local/www/apache24/data;
fi
become: true
when: apache24_server.changed == true
- name: Start Apache 2.4 server
service:
name: apache24
state: started
become: true
-10
View File
@@ -1,10 +0,0 @@
<VirtualHost *:80>
<Directory {{ app_path }}/public>
Allow From All
AllowOverride All
Options -Indexes
</Directory>
DocumentRoot {{ app_path }}/public
ServerName {{ fqdn }}
</VirtualHost>
-226
View File
@@ -1,226 +0,0 @@
---
- name: Set up local server
hosts: all
remote_user: vagrant
become_user: root
become_method: sudo
vars:
app_path: "/var/www/snipeit"
fqdn: "localhost"
tasks:
- name: Update and upgrade existing apt packages
become: true
apt:
upgrade: yes
update_cache: yes
- name: Install Utilities
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- nano
- vim
- name: Installing Apache httpd, PHP, MariaDB and other requirements.
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- mariadb-client
- php
- php-curl
- php-mysql
- php-gd
- php-ldap
- php-zip
- php-mbstring
- php-xml
- php-bcmath
- curl
- git
- unzip
- python-pymysql
#
# Install the lastest version of composer
#
- name: Composer check
stat:
path: /usr/local/bin/composer
register: composer_exits
- name: Install Composer
shell: |
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
mv composer.phar /usr/local/bin/composer
exit $RESULT
when: not composer_exits.stat.exists
args:
creates: /usr/local/bin/composer
become: true
#
# Install and Configure MariaDB
#
- name: Install MariaDB
become: true
apt:
name: mariadb-server
state: present
register: sql_server
- name: Start and Enable MySQL server
become: true
systemd:
state: started
enabled: yes
name: mariadb
- name: Create Vagrant mysql password
become: true
mysql_user:
name: vagrant
password: vagrant
login_unix_socket: /var/run/mysqld/mysqld.sock
priv: "*.*:ALL"
state: present
- name: Enable remote mysql
replace:
path: /etc/mysql/mariadb.conf.d/50-server.cnf
regexp: "127.0.0.1"
replace: "0.0.0.0"
become: true
notify:
- restart mysql
- name: Create snipeit database
become: true
mysql_db:
name: snipeit
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
#
# Install Apache Web Server
#
- name: Install Apache 2.4
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- apache2
- libapache2-mod-php
become: true
register: apache2_server
- name: Start and Enable Apache2 Server
become: true
systemd:
name: apache2
state: started
enabled: yes
#- name: Disable Apache modules
# become: true
# apache2_module:
# state: absent
# name: "{{ item }}"
# with_items:
# #- mpm_prefork
# notify:
# - restart apache2
- name: Enable Apache modules
become: true
apache2_module:
state: present
name: "{{ item }}"
with_items:
- rewrite
- vhost_alias
- deflate
- expires
- proxy_fcgi
- proxy
notify:
- restart apache2
- name: Install Apache VirtualHost File
become: true
template:
src: apachevirtualhost.conf.j2
dest: "/etc/apache2/sites-available/snipeit.conf"
- name: Enable VirtualHost
become: true
command: a2ensite snipeit
args:
creates: /etc/apache2/sites-enabled/snipeit.conf
notify:
- restart apache2
- name: Map apache dir to local folder
become: true
file:
src: /vagrant
dest: "{{ app_path }}"
state: link
notify:
- restart apache2
#
# Install dependencies from composer
#
- name: Install dependencies from composer
composer:
command: install
working_dir: "{{ app_path }}"
notify:
- restart apache2
#
# Configure .env file
#
- name: Copy .env file
copy:
src: "{{ app_path }}/.env.example"
dest: "{{ app_path }}/.env"
- name: Configure .env file
lineinfile:
path: "{{ app_path }}/.env"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^DB_HOST=', line: 'DB_HOST=127.0.0.1'}
- { regexp: '^DB_DATABASE=', line: 'DB_DATABASE=snipeit' }
- { regexp: '^DB_USERNAME=', line: 'DB_USERNAME=vagrant' }
- { regexp: '^DB_PASSWORD=', line: 'DB_PASSWORD=vagrant' }
- { regexp: '^APP_URL=', line: "APP_URL=http://{{ fqdn }}" }
- { regexp: '^APP_ENV=', line: "APP_ENV=development" }
- { regexp: '^APP_DEBUG=', line: "APP_DEBUG=true" }
- name: Generate application key
shell: "php {{ app_path }}/artisan key:generate --force"
- name: Artisan Migrate
shell: "php {{ app_path }}/artisan migrate --force"
#
# Create Cron Job
#
- name: Create scheduler cron job
become: true
cron:
name: "Snipe-IT Artisan Scheduler"
job: "/usr/bin/php {{ app_path }}/artisan schedule:run"
handlers:
- name: restart apache2
become: true
systemd:
name: apache2
state: restarted
- name: restart mysql
become: true
systemd:
name: mysql
state: restarted
-154
View File
@@ -1,154 +0,0 @@
{
"name": "Snipe-IT",
"description": "Open source asset management.",
"keywords": [
"asset management",
"it asset"
],
"website": "https://snipeitapp.com/",
"repository": "https://github.com/grokability/snipe-it",
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
"success_url": "/setup",
"env": {
"APP_ENV": {
"description": "Laravel environment mode. Unless developing the application, this should be production.",
"value": "production"
},
"APP_DEBUG": {
"description": "Laravel debug mode. Unless developing the application or actively debugging a problem, this should be set to false.",
"value": "false"
},
"APP_KEY": {
"description": "A secret key for verifying the integrity of signed cookies. (See either https://snipe-it.readme.io/docs/generate-your-app-key or generate at https://coderstoolbox.online/toolbox/generate-symfony-secret)",
"value": ""
},
"APP_URL": {
"description": "URL where your Snipe-IT install will be available at.",
"value": "https://your-app-name.herokuapp.com"
},
"APP_TIMEZONE": {
"description": "Which timezone do you want to use for your install? (http://php.net/manual/en/timezones.php)",
"value": "UTC"
},
"APP_LOCALE": {
"description": "Which language do you want to use for your install? (https://snipe-it.readme.io/docs/configuration#setting-a-language)",
"value": "en"
},
"MAX_RESULTS": {
"description": "The maximum number of search results that can be returned at one time.",
"value": "500"
},
"MAIL_MAILER": {
"description": "Mail driver - Generally SMTP on Heroku - https://snipe-it.readme.io/docs/configuration#required-outgoing-mail-settings",
"value": "smtp"
},
"MAIL_HOST": {
"description": "SMTP Server Hostname",
"value": "smtp.your.domain.name"
},
"MAIL_PORT": {
"description": "SMTP Server Port",
"value": "25"
},
"MAIL_USERNAME": {
"description": "SMTP Server Username",
"value": "YOURUSERNAME"
},
"MAIL_PASSWORD": {
"description": "SMTP Server Password",
"value": "YOURPASSWORD"
},
"MAIL_TLS_VERIFY_PEER": {
"description": "Ensure validity of TLS certificate on remote mail server",
"value": true
},
"MAIL_FROM_ADDR": {
"description": "Email from address",
"value": "no-reply@domain.name"
},
"MAIL_FROM_NAME": {
"description": "Email from Name",
"value": "Snipe-IT"
},
"MAIL_REPLYTO_ADDR": {
"description": "Email Reply-To address",
"value": "your@domain.name"
},
"MAIL_REPLYTO_NAME": {
"description": "Email Reply-To Name",
"value": "Snipe-IT"
},
"MAIL_AUTO_EMBED": {
"description": "Whether or not to embed images in emails (via CID or base64) versus linking to them.",
"value": "true"
},
"MAIL_AUTO_EMBED_METHOD": {
"description": "Method that should be used for attaching inline images.",
"value": "base64"
},
"SESSION_LIFETIME": {
"description": "Specify the time in minutes that the session should remain valid.",
"value": "12000"
},
"EXPIRE_ON_CLOSE": {
"description": "Specify whether or not the logged in session should be expired when the user closes their browser window.",
"value": "false"
},
"ENCRYPT": {
"description": "Specify whether you wish to use encrypted cookies for your Snipe-IT sessions.",
"value": "true"
},
"COOKIE_NAME": {
"description": "The name of the cookie set by Snipe-IT for session management.",
"value": "snipeit_session"
},
"COOKIE_DOMAIN": {
"description": "The domain name that the session cookie should be sent for.",
"value": "your-app-name.herokuapp.com"
},
"SECURE_COOKIES": {
"description": "Should cookies only be sent for HTTPS connections? Generally true on Heroku.",
"value": "true"
},
"LOGIN_MAX_ATTEMPTS": {
"description": "The maximum number of failed attempts allowed before the user is throttled.",
"value": "5"
},
"LOGIN_LOCKOUT_DURATION": {
"description": "The duration (in seconds) that the user should be blocked from attempting to authenticate again.",
"value": "60"
},
"LOG_CHANNEL": {
"description": "Driver to send logs to. (errorlog for stderr)",
"value": "errorlog"
},
"ALLOW_IFRAMING": {
"description": "Allow Snipe-IT to be loaded using an iFrame?",
"value": "false"
},
"GOOGLE_MAPS_API": {
"description": "Include your Google Maps API key here if you'd like Snipe-IT to load maps from Google on your locations and suppliers pages.",
"required": false
},
"BACKUP_ENV": {
"description": "Set this to true if you wish to backup your .env file in your Admin > Backups process.",
"value": "true"
},
"ENABLE_HSTS": {
"description": "Whether or not to send the HSTS security policy header.",
"value": "false"
}
},
"formation": {
"web": {
"quantity": 1,
"size": "free"
}
},
"image": "heroku/php",
"addons": [
"cleardb:ignite",
"heroku-redis:mini",
"papertrail:choklad"
]
}
@@ -1,109 +0,0 @@
<?php
namespace App\Actions\Breadcrumbs;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Consumable;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
use Tabuna\Breadcrumbs\Trail;
final class BuildAcceptanceBreadcrumbs
{
public static function forAcceptance(Trail $trail, CheckoutAcceptance|int|string $acceptance): void
{
$acceptance = self::resolveAcceptance($acceptance);
$trail->parent('home');
if (! $acceptance instanceof CheckoutAcceptance) {
self::appendProfileContext($trail);
return;
}
if (! self::isSignInPlaceFlow($acceptance)) {
self::appendProfileContext($trail);
$trail->push(trans('general.accept_item'), route('account.accept.item', $acceptance));
return;
}
self::appendCheckoutFlowContext($trail, $acceptance);
$trail->push(self::buildSignInPlaceLabel($acceptance));
}
private static function resolveAcceptance(CheckoutAcceptance|int|string $acceptance): ?CheckoutAcceptance
{
if ($acceptance instanceof CheckoutAcceptance) {
return $acceptance;
}
if (is_numeric($acceptance)) {
return CheckoutAcceptance::find((int) $acceptance);
}
return null;
}
private static function isSignInPlaceFlow(CheckoutAcceptance $acceptance): bool
{
return (int) session('sign_in_place_acceptance_id') === (int) $acceptance->id;
}
private static function appendProfileContext(Trail $trail): void
{
$trail->push(trans('general.profile'), route('account'));
$trail->push(trans('general.accept_items'), route('account.accept'));
}
private static function appendCheckoutFlowContext(Trail $trail, CheckoutAcceptance $acceptance): void
{
$checkoutable = $acceptance->checkoutable;
if ($checkoutable instanceof Asset) {
$trail->push(trans('general.assets'), route('hardware.index'));
$trail->push($checkoutable->display_name ?? trans('general.asset'), route('hardware.show', $checkoutable));
$trail->push(trans('general.checkout'));
return;
}
if ($checkoutable instanceof LicenseSeat) {
$license = $checkoutable->license;
if ($license instanceof License) {
$trail->push(trans('general.licenses'), route('licenses.index'));
$trail->push($license->display_name ?? trans('general.license'), route('licenses.show', $license));
$trail->push(trans('general.checkout'));
}
return;
}
if ($checkoutable instanceof Consumable) {
$trail->push(trans('general.consumables'), route('consumables.index'));
$trail->push($checkoutable->display_name ?? trans('general.consumable'), route('consumables.show', $checkoutable));
$trail->push(trans('general.checkout'));
return;
}
if ($checkoutable instanceof Accessory) {
$trail->push(trans('general.accessories'), route('accessories.index'));
$trail->push($checkoutable->display_name ?? trans('general.accessory'), route('accessories.show', $checkoutable));
$trail->push(trans('general.checkout'));
}
}
private static function buildSignInPlaceLabel(CheckoutAcceptance $acceptance): string
{
if ($acceptance->assignedTo instanceof User) {
return sprintf('%s for %s', trans('general.sign_in_place'), $acceptance->assignedTo->display_name);
}
return trans('general.sign_in_place');
}
}
@@ -1,59 +0,0 @@
<?php
namespace App\Actions\Categories;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssetModels;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Category;
use Illuminate\Support\Facades\Storage;
class DestroyCategoryAction
{
/**
* @throws ItemStillHasAssets
* @throws ItemStillHasAssetModels
* @throws ItemStillHasComponents
* @throws ItemStillHasAccessories
* @throws ItemStillHasLicenses
* @throws ItemStillHasConsumables
*/
public static function run(Category $category): bool
{
$category->loadCount([
'assets as assets_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
'licenses as licenses_count',
'models as models_count',
]);
if ($category->assets_count > 0) {
throw new ItemStillHasAssets($category);
}
if ($category->accessories_count > 0) {
throw new ItemStillHasAccessories($category);
}
if ($category->consumables_count > 0) {
throw new ItemStillHasConsumables($category);
}
if ($category->components_count > 0) {
throw new ItemStillHasComponents($category);
}
if ($category->licenses_count > 0) {
throw new ItemStillHasLicenses($category);
}
if ($category->models_count > 0) {
throw new ItemStillHasAssetModels($category);
}
Storage::disk('public')->delete('categories'.'/'.$category->image);
$category->delete();
return true;
}
}
@@ -1,47 +0,0 @@
<?php
namespace App\Actions\CheckoutRequests;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetCancelation;
use Illuminate\Auth\Access\AuthorizationException;
class CancelCheckoutRequestAction
{
public static function run(Asset $asset, User $user)
{
if (! Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException;
}
$asset->cancelRequest();
$asset->decrement('requests_counter', 1);
$data['item'] = $asset;
$data['target'] = $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');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('request canceled');
try {
$settings->notify(new RequestAssetCancelation($data));
} catch (\Exception $e) {
\Log::warning($e);
}
return true;
}
}
@@ -1,54 +0,0 @@
<?php
namespace App\Actions\CheckoutRequests;
use App\Exceptions\AssetNotRequestable;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetNotification;
use Illuminate\Auth\Access\AuthorizationException;
use Log;
class CreateCheckoutRequestAction
{
/**
* @throws AssetNotRequestable
* @throws AuthorizationException
*/
public static function run(Asset $asset, User $user): string
{
if (is_null(Asset::RequestableAssets()->find($asset->id))) {
throw new AssetNotRequestable($asset);
}
if (! Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException;
}
$data['item'] = $asset;
$data['target'] = $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');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('requested');
$asset->request();
$asset->increment('requests_counter', 1);
try {
$settings->notify((new RequestAssetNotification($data))->locale($settings->locale));
} catch (\Exception $e) {
Log::warning($e);
}
return true;
}
}
@@ -1,62 +0,0 @@
<?php
namespace App\Actions\Manufacturers;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Manufacturer;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DeleteManufacturerAction
{
/**
* @throws ItemStillHasAssets
* @throws ItemStillHasComponents
* @throws ItemStillHasAccessories
* @throws ItemStillHasLicenses
* @throws ItemStillHasConsumables
*/
public static function run(Manufacturer $manufacturer): bool
{
$manufacturer->loadCount([
'assets as assets_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
'licenses as licenses_count',
]);
if ($manufacturer->assets_count > 0) {
throw new ItemStillHasAssets($manufacturer);
}
if ($manufacturer->accessories_count > 0) {
throw new ItemStillHasAccessories($manufacturer);
}
if ($manufacturer->consumables_count > 0) {
throw new ItemStillHasConsumables($manufacturer);
}
if ($manufacturer->components_count > 0) {
throw new ItemStillHasComponents($manufacturer);
}
if ($manufacturer->licenses_count > 0) {
throw new ItemStillHasLicenses($manufacturer);
}
if ($manufacturer->image) {
try {
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
} catch (\Exception $e) {
Log::info($e);
}
}
$manufacturer->delete();
// dd($manufacturer);
return true;
}
}
@@ -1,30 +0,0 @@
<?php
namespace App\Actions\Permissions;
final class NormalizePermissionsPayloadAction
{
/**
* Normalize permissions payloads from request/model to a consistent associative array.
*
* @return array<string, mixed>
*/
public static function run(mixed $permissions): array
{
if (is_string($permissions)) {
$decoded = json_decode($permissions, true);
return is_array($decoded) ? $decoded : [];
}
if (is_array($permissions)) {
return $permissions;
}
if ($permissions instanceof \stdClass) {
return (array) $permissions;
}
return [];
}
}
@@ -1,36 +0,0 @@
<?php
namespace App\Actions\Permissions;
use App\Models\User;
final class PreserveUnauthorizedPrivilegedPermissionsAction
{
/**
* Preserve privileged permission keys unless the authenticated user may manage them.
*
* @param array<string, mixed> $requestedPermissions
* @param array<string, mixed> $originalPermissions
* @return array<string, mixed>
*/
public static function run(array $requestedPermissions, User $authenticatedUser, array $originalPermissions = []): array
{
if (! $authenticatedUser->isSuperUser()) {
if (array_key_exists('superuser', $originalPermissions)) {
$requestedPermissions['superuser'] = $originalPermissions['superuser'];
} else {
unset($requestedPermissions['superuser']);
}
}
if ((! $authenticatedUser->isAdmin()) && (! $authenticatedUser->isSuperUser())) {
if (array_key_exists('admin', $originalPermissions)) {
$requestedPermissions['admin'] = $originalPermissions['admin'];
} else {
unset($requestedPermissions['admin']);
}
}
return $requestedPermissions;
}
}
@@ -1,71 +0,0 @@
<?php
namespace App\Actions\Suppliers;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Exceptions\ItemStillHasMaintenances;
use App\Models\Supplier;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DestroySupplierAction
{
/**
* @throws ItemStillHasLicenses
* @throws ItemStillHasAssets
* @throws ItemStillHasMaintenances
* @throws ItemStillHasAccessories
* @throws ItemStillHasConsumables
* @throws ItemStillHasComponents
*/
public static function run(Supplier $supplier): bool
{
$supplier->loadCount([
'maintenances as maintenances_count',
'assets as assets_count',
'licenses as licenses_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
]);
if ($supplier->assets_count > 0) {
throw new ItemStillHasAssets($supplier);
}
if ($supplier->maintenances_count > 0) {
throw new ItemStillHasMaintenances($supplier);
}
if ($supplier->licenses_count > 0) {
throw new ItemStillHasLicenses($supplier);
}
if ($supplier->accessories_count > 0) {
throw new ItemStillHasAccessories($supplier);
}
if ($supplier->consumables_count > 0) {
throw new ItemStillHasConsumables($supplier);
}
if ($supplier->components_count > 0) {
throw new ItemStillHasComponents($supplier);
}
if ($supplier->image) {
try {
Storage::disk('public')->delete('suppliers/'.$supplier->image);
} catch (\Exception $e) {
Log::info($e->getMessage());
}
}
$supplier->delete();
return true;
}
}
@@ -1,86 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\License;
use App\Models\LicenseSeat;
use Illuminate\Console\Command;
class CheckinLicensesFromAllUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:checkin-from-all {--license_id=} {--notify}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Checks in licenses from all users';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$license_id = $this->option('license_id');
$notify = $this->option('notify');
if (! $license_id) {
$this->error('ERROR: License ID is required.');
return false;
}
if (! $license = License::where('id', '=', $license_id)->first()) {
$this->error('Invalid license ID');
return false;
}
$this->info('Checking in ALL seats for '.$license->name);
$licenseSeats = LicenseSeat::where('license_id', '=', $license_id)
->whereNotNull('assigned_to')
->with('user')
->get();
$this->info(' There are '.$licenseSeats->count().' seats checked out: ');
if (! $notify) {
$this->info('No mail will be sent.');
}
foreach ($licenseSeats as $seat) {
$this->info($seat->user->username.' has a license seat for '.$license->name);
$seat->assigned_to = null;
if ($seat->save()) {
// Override the email address so we don't notify on checkin
if (! $notify) {
$seat->user->email = null;
}
// Log the checkin
$seat->logCheckin($seat->user, 'Checked in via cli tool');
}
}
}
}
@@ -1,106 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\License;
use App\Models\User;
use Illuminate\Console\Command;
class CheckoutLicenseToAllUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:checkout-to-all {--license_id=} {--notify}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Checks out licenses to all users';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$license_id = $this->option('license_id');
$notify = $this->option('notify');
if (! $license_id) {
$this->error('ERROR: License ID is required.');
return false;
}
if (! $license = License::where('id', '=', $license_id)->with('assignedusers')->first()) {
$this->error('Invalid license ID');
return false;
}
$users = User::whereNull('deleted_at')->where('autoassign_licenses', '=', 1)->with('licenses')->get();
if ($users->count() > $license->getAvailSeatsCountAttribute()) {
$this->info('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
}
$this->info('Checking out '.$users->count().' of '.$license->getAvailSeatsCountAttribute().' seats for '.$license->name);
if (! $notify) {
$this->info('No mail will be sent.');
}
foreach ($users as $user) {
// Check to make sure this user doesn't already have this license checked out
// to them
if ($user->licenses->where('id', '=', $license_id)->count()) {
$this->info($user->username.' already has this license checked out to them. Skipping... ');
continue;
}
// If the license is valid, check that there is an available seat
if ($license->availCount()->count() < 1) {
$this->error('ERROR: No available seats');
return false;
}
$this->info($license->availCount()->count().' seats left');
// Get the seat ID
$licenseSeat = $license->freeSeat();
// Update the seat with checkout info,
$licenseSeat->assigned_to = $user->id;
if ($licenseSeat->save()) {
// Temporarily null the user's email address so we don't send mail if we're not supposed to
if (! $notify) {
$user->email = null;
}
// Log the checkout
$licenseSeat->logCheckout('Checked out via cli tool', $user);
$this->info('License '.$license_id.' seat '.$licenseSeat->id.' checked out to '.$user->username);
}
}
}
}
@@ -1,70 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Console\Command;
class CleanIncorrectCheckoutAcceptances extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-checkout-acceptances';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete checkout acceptances for checkouts to non-users';
/**
* Execute the console command.
*/
public function handle()
{
$deletions = 0;
$skips = 0;
// This walks *every* checkoutacceptance. That's gnarly. But necessary
$this->withProgressBar(CheckoutAcceptance::all(), function ($checkoutAcceptance) use (&$deletions, &$skips) {
$item = $checkoutAcceptance->checkoutable;
$checkout_to_id = $checkoutAcceptance->assigned_to_id;
if (is_null($item)) {
$this->info("'Checkoutable' Item is null, going to next record");
return; // 'false' allegedly breaks execution entirely, so 'true' maybe doesn't? hrm. just straight return maybe?
}
if (get_class($item) == LicenseSeat::class) {
$item = $item->license;
}
foreach ($item->assetlog()->where('action_type', 'checkout')->get() as $assetlog) {
if ($assetlog->target_id == $checkout_to_id && $assetlog->target_type != User::class) {
// We have a checkout-to an ID for a non-User, which matches to an ID in the checkout_acceptances table
// now, let's compare the _times_ - are they close?
// I'm picking `created_at` over `action_date` because I'm more interested in when the actionlogs
// were _created_, not when they were alleged to have happened - those created_at times need to be within 'X' seconds of
// each other (currently 5)
if ($assetlog->created_at->diffInSeconds($checkoutAcceptance->created_at, true) <= 5) { // we're allowing for five _ish_ seconds of slop
$deletions++;
$checkoutAcceptance->forceDelete(); // HARD delete this record; it should have never been
return;
} else {
// $this->info("The two records are too far apart");
}
} else {
// $this->info("No match! checkout to id: " . $checkout_to_id." target_id: ".$assetlog->target_id." target_type: ".$assetlog->target_type);
}
}
$skips++;
});
$this->error("Final deletion count: $deletions, and skip count: $skips");
}
}
@@ -1,77 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutRequest;
use Illuminate\Console\Command;
class CleanOldCheckoutRequests extends Command
{
private int $deletions = 0;
private int $skips = 0;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-old-checkout-requests';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes checkout requests that reference deleted assets or users.';
/**
* Execute the console command.
*/
public function handle()
{
$requests = CheckoutRequest::with([
'user' => function ($query) {
$query->withTrashed();
},
'requestedItem' => function ($query) {
$query->withTrashed();
},
])->get();
$this->info("Processing {$requests->count()} checkout requests");
$this->withProgressBar($requests, function ($request) {
if ($this->shouldForceDelete($request)) {
$request->forceDelete();
$this->deletions++;
return;
}
if ($this->shouldSoftDelete($request)) {
$request->delete();
$this->deletions++;
return;
}
$this->skips++;
});
$this->info("Final deletion count: $this->deletions, and skip count: $this->skips");
return 0;
}
private function shouldForceDelete(CheckoutRequest $request)
{
// check if the requestable or user relationship is null
return ! $request->requestable || ! $request->user;
}
private function shouldSoftDelete(CheckoutRequest $request)
{
return $request->requestable->trashed() || $request->user->trashed();
}
}
-89
View File
@@ -1,89 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
class CreateAdmin extends Command
{
/** @mixin User **/
/**
* App\Console\CreateAdmin
*
* @property mixed $first_name
* @property string $last_name
* @property string $username
* @property string $email
* @property string $permissions
* @property string $password
* @property bool $activated
* @property bool $show_in_list
* @property bool $autoassign_licenses
* @property Carbon|null $created_at
* @property mixed $created_by
*/
protected $signature = 'snipeit:create-admin {--first_name=} {--last_name=} {--email=} {--username=} {--password=} {show_in_list?} {autoassign_licenses?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create an admin user via command line.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
public function handle()
{
$first_name = $this->option('first_name');
$last_name = $this->option('last_name');
$username = $this->option('username');
$email = $this->option('email');
$password = $this->option('password');
$show_in_list = $this->argument('show_in_list');
$autoassign_licenses = $this->argument('autoassign_licenses');
if (($first_name == '') || ($last_name == '') || ($username == '') || ($email == '') || ($password == '')) {
$this->info('ERROR: All fields are required.');
} else {
$user = new User;
$user->first_name = $first_name;
$user->last_name = $last_name;
$user->username = $username;
$user->email = $email;
$user->permissions = '{"admin":1,"user":1,"superuser":1,"reports.view":1, "licenses.keys":1}';
$user->password = bcrypt($password);
$user->activated = 1;
if ($show_in_list == 'false') {
$user->show_in_list = 0;
}
if ($autoassign_licenses == 'false') {
$user->autoassign_licenses = 0;
}
if ($user->save()) {
$this->info('New user created');
$user->groups()->attach(1);
} else {
$this->info('Admin user was not created');
$errors = $user->getErrors();
foreach ($errors->all() as $error) {
$this->info('ERROR:'.$error);
}
}
}
}
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
class DisableLDAP extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:ldap-disable';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This is a rescue command that can be used to turn off LDAP settings in the event that you managed to lock yourself out using bad LDAP settings.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nThis will disable LDAP support. You will not be able \nto login with an account that does not exist \nlocally in the Snipe-IT local database. \n****************************************************\n\nDo you wish to continue? [y|N]")) {
$setting = Setting::getSettings();
$setting->ldap_enabled = 0;
if ($setting->save()) {
$this->info('LDAP has been set to disabled.');
} else {
$this->info('Unable to disable LDAP.');
}
} else {
$this->info('Canceled. No actions taken.');
}
}
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
class DisableSAML extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:saml-disable';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This is a rescue command that can be used to turn off SAML settings in the event that you managed to lock yourself out using bad SAML settings.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nThis will disable SAML support. You will not be able \nto login with an account that does not exist \nlocally in the Snipe-IT local database. \n****************************************************\n\nDo you wish to continue? [y|N]")) {
$setting = Setting::getSettings();
$setting->saml_enabled = 0;
if ($setting->save()) {
$this->info('SAML has been set to disabled.');
} else {
$this->info('Unable to disable SAML.');
}
} else {
$this->info('Canceled. No actions taken.');
}
}
}
@@ -1,153 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Actionlog;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Model;
class FixBulkAccessoryCheckinActionLogEntries extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:fix-bulk-accessory-action-log-entries {--dry-run : Run the sync process but don\'t update the database} {--skip-backup : Skip pre-execution backup}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This script attempts to fix timestamps and missing created_by values for bulk checkin entries in the log table';
private bool $dryrun = false;
private bool $skipBackup = false;
/**
* Execute the console command.
*/
public function handle()
{
$this->skipBackup = $this->option('skip-backup');
$this->dryrun = $this->option('dry-run');
if ($this->dryrun) {
$this->info('This is a DRY RUN - no changes will be saved.');
$this->newLine();
}
$logs = Actionlog::query()
// only look for accessory checkin logs
->where('item_type', Accessory::class)
// that were part of a bulk checkin
->where('note', 'Bulk checkin items')
// logs that were improperly timestamped should have created_at in the 1970s
->whereYear('created_at', '1970')
->get();
if ($logs->isEmpty()) {
$this->info('No logs found with incorrect timestamps.');
return 0;
}
$this->info('Found '.$logs->count().' logs with incorrect timestamps:');
$this->table(
['ID', 'Created By', 'Created At', 'Updated At'],
$logs->map(function ($log) {
return [
$log->id,
$log->created_by,
$log->created_at,
$log->updated_at,
];
})
);
if (! $this->dryrun && ! $this->confirm('Update these logs?')) {
return 0;
}
if (! $this->dryrun && ! $this->skipBackup) {
$this->info('Backing up the database before making changes...');
$this->call('snipeit:backup');
}
if ($this->dryrun) {
$this->newLine();
$this->info('DRY RUN. NOT ACTUALLY UPDATING LOGS.');
}
foreach ($logs as $log) {
$this->newLine();
$this->info('Processing log id:'.$log->id);
// created_by was not being set for accessory bulk checkins
// so let's see if there was another bulk checkin log
// with the same timestamp and a created_by value we can use.
if (is_null($log->created_by)) {
$createdByFromSimilarLog = $this->getCreatedByAttributeFromSimilarLog($log);
if ($createdByFromSimilarLog) {
$this->line(vsprintf('Updating log id:%s created_by to %s', [$log->id, $createdByFromSimilarLog]));
$log->created_by = $createdByFromSimilarLog;
} else {
$this->warn(vsprintf('No created_by found for log id:%s', [$log->id]));
$this->warn('Skipping updating this log since no similar log was found to update created_by from.');
// If we can't find a similar log then let's skip updating it
continue;
}
}
$this->line(vsprintf('Updating log id:%s from %s to %s', [$log->id, $log->created_at, $log->updated_at]));
$log->created_at = $log->updated_at;
if (! $this->dryrun) {
Model::withoutTimestamps(function () use ($log) {
$log->saveQuietly();
});
}
}
$this->newLine();
if ($this->dryrun) {
$this->info('DRY RUN. NO CHANGES WERE ACTUALLY MADE.');
}
return 0;
}
/**
* Hopefully the bulk checkin included other items like assets or licenses
* so we can use one of those logs to get the correct created_by value.
*
* This method attempts to find a bulk check in log that was
* created at the same time as the log passed in.
*/
private function getCreatedByAttributeFromSimilarLog(Actionlog $log): ?int
{
$similarLog = Actionlog::query()
->whereNotNull('created_by')
->where([
'action_type' => 'checkin from',
'note' => 'Bulk checkin items',
'target_id' => $log->target_id,
'target_type' => $log->target_type,
'created_at' => $log->updated_at,
])
->first();
if ($similarLog) {
return $similarLog->created_by;
}
return null;
}
}
-94
View File
@@ -1,94 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\Department;
use App\Models\Depreciation;
use App\Models\Group;
use App\Models\License;
use App\Models\Location;
use App\Models\Manufacturer;
use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Console\Command;
class FixDoubleEscape extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:unescape';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This should be run to fix some double-escaping issues from earlier versions of Snipe-IT.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$tables = [
Asset::class => ['name'],
License::class => ['name'],
Consumable::class => ['name'],
Accessory::class => ['name'],
Component::class => ['name'],
Company::class => ['name'],
Manufacturer::class => ['name'],
Supplier::class => ['name'],
Statuslabel::class => ['name'],
Depreciation::class => ['name'],
AssetModel::class => ['name'],
Group::class => ['name'],
Department::class => ['name'],
Location::class => ['name'],
User::class => ['first_name', 'last_name'],
];
$count = [];
foreach ($tables as $classname => $fields) {
$count[$classname] = [];
$count[$classname]['classname'] = 0;
foreach ($fields as $field) {
$count[$classname]['classname']++;
$count[$classname][$field] = 0;
foreach ($classname::where("$field", 'LIKE', '%&%')->get() as $row) {
$this->info('Updating '.$field.' for '.$classname);
$row->{$field} = html_entity_decode($row->{$field}, ENT_QUOTES);
$row->save();
$count[$classname][$field]++;
}
}
}
$this->info('Update complete');
}
}
@@ -1,100 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\User;
use Illuminate\Console\Command;
class FixMismatchedAssetsAndLogs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:fix-assets-and-logs {--dryrun : Run the sync process but don\'t update the database}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This script attempts to check the log table and check that the assets.assigned_to matches the last checkout.';
/**
* Is dry-run?
*
* @var bool
*/
private $dryrun = false;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->option('dryrun')) {
$this->dryrun = true;
}
if ($this->dryrun) {
$this->info('This is a DRY RUN - no changes will be saved.');
}
$mismatch_count = 0;
$assets = Asset::whereNotNull('assigned_to')
->where('assigned_type', '=', User::class)
->orderBy('id', 'ASC')->get();
foreach ($assets as $asset) {
// get the last checkout of the asset
if ($checkout_log = Actionlog::where('target_type', '=', User::class)
->where('action_type', '=', 'checkout')
->where('item_id', '=', $asset->id)
->orderBy('created_at', 'DESC')
->first()) {
// Now check for a subsequent checkin log - we want to ignore those
if (! $checkin_log = Actionlog::where('target_type', '=', User::class)
->where('action_type', '=', 'checkin from')
->where('item_id', '=', $asset->id)
->whereDate('created_at', '>', $checkout_log->created_at)
->orderBy('created_at', 'DESC')
->first()) {
// print_r($asset);
if ($checkout_log->target_id != $asset->assigned_to) {
$this->error('Log ID: '.$checkout_log->id.' -- Asset ID '.$checkout_log->item_id.' SHOULD BE checked out to User '.$checkout_log->target_id.' but its assigned_to is '.$asset->assigned_to);
if (! $this->dryrun) {
$asset->assigned_to = $checkout_log->target_id;
if ($asset->save()) {
$this->info('Asset record updated.');
} else {
$this->error('Error updating asset: '.$asset->getErrors());
}
}
$mismatch_count++;
}
} else {
// $this->info('Asset ID '.$asset->id.': There is a checkin '.$checkin_log->created_at.' after this checkout '.$checkout_log->created_at);
}
}
}
$this->info($mismatch_count.' mismatched assets.');
}
}
@@ -1,32 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class FixUpAssignedTypeWithoutAssignedTo extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-type-fixup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
/**
* Execute the console command.
*/
public function handle()
{
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
$this->info('Assets with an assigned_type but no assigned_to are fixed');
}
}
@@ -1,68 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use Illuminate\Console\Command;
class FixupAssignedToWithoutAssignedType extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-to-fixup
{--debug : Display debugging output}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_to but no assigned_type';
/**
* Execute the console command.
*/
public function handle()
{
$assets = Asset::whereNull('assigned_type')->whereNotNull('assigned_to')->withTrashed();
$this->withProgressBar($assets->get(), function (Asset $asset) {
// now check each action log, from the most recent backwards, to find the last checkin or checkout
foreach ($asset->log()->orderBy('id', 'desc')->get() as $action_log) {
if ($this->option('debug')) {
$this->info('Asset id: '.$asset->id.' action log, action type is: '.$action_log->action_type);
}
switch ($action_log->action_type) {
case 'checkin from':
if ($this->option('debug')) {
$this->info('Doing a checkin for '.$asset->id);
}
$asset->assigned_to = null;
// if you have a required custom field, we still want to save, and we *don't* want an action_log
$asset->saveQuietly();
return;
case 'checkout':
if ($this->option('debug')) {
$this->info('Doing a checkout for '.$asset->id.' picking target type: '.$action_log->target_type);
}
if ($asset->assigned_to != $action_log->target_id) {
$this->error("Asset's assigned_to does *NOT* match Action Log's target_id. \$asset->assigned_to=".$asset->assigned_to.' vs. $action_log->target_id='.$action_log->target_id);
// FIXME - do we abort here? Do we try to keep looking? I don't know, this means your data is *really* messed up...
}
$asset->assigned_type = $action_log->target_type;
$asset->saveQuietly(); // see above
return;
}
}
$asset->assigned_to = null; // asset was never checked in or out in its lifetime - it stays 'checked in'
$asset->saveQuietly(); // see above
});
$this->newLine();
$this->info('Assets assigned_type are fixed');
}
}
@@ -1,88 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Laravel\Passport\TokenRepository;
class GeneratePersonalAccessToken extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:make-api-key
{--user_id= : The ID of the user to create the token for.}
{--name= : The name of the new API token}
{--key-only : Only return the value of the API key}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This console command allows you to generate Personal API tokens to be used with the Snipe-IT JSON REST API on behalf of a user.';
/**
* The token repository implementation.
*
* @var TokenRepository
*/
protected $tokenRepository;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(TokenRepository $tokenRepository)
{
$this->tokenRepository = $tokenRepository;
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$accessTokenName = $this->option('name');
if ($accessTokenName == '') {
$accessTokenName = 'CLI Auth Token';
}
if ($this->option('user_id') == '') {
return $this->error('ERROR: user_id cannot be blank.');
}
if ($user = User::find($this->option('user_id'))) {
$createAccessToken = $user->createToken($accessTokenName)->accessToken;
if ($this->option('key-only')) {
$this->info($createAccessToken);
} else {
$this->warn('Your API Token has been created. Be sure to copy this token now, as it WILL NOT be accessible again.');
if ($token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name', '=', $accessTokenName)->orderBy('created_at', 'desc')->first()) {
$this->info('API Token ID: '.$token->id);
}
$this->info('API Token User: '.$user->present()->fullName.' ('.$user->username.')');
$this->info('API Token Name: '.$accessTokenName);
$this->info('API Token: '.$createAccessToken);
}
} else {
return $this->error('ERROR: Invalid user. API key was not created.');
}
}
}
-150
View File
@@ -1,150 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Location;
use Illuminate\Console\Command;
use League\Csv\Reader;
class ImportLocations extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:import-locations {filename}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import locations and their parents';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (! ini_get('auto_detect_line_endings')) {
ini_set('auto_detect_line_endings', '1');
}
$filename = $this->argument('filename');
$csv = Reader::createFromPath(storage_path('private_uploads/imports/').$filename, 'r');
$this->info('Attempting to process: '.storage_path('private_uploads/imports/').$filename);
$csv->setHeaderOffset(0); // because we don't want to insert the header
$results = $csv->getRecords();
// Import parent location names first if they don't exist
foreach ($results as $parent_index => $parent_row) {
if (array_key_exists('Parent Name', $parent_row)) {
$parent_name = trim($parent_row['Parent Name']);
if (array_key_exists('Name', $parent_row)) {
$this->info('- Parent: '.$parent_name.' in row as: '.trim($parent_row['Parent Name']));
}
// Save parent location name
// This creates a sort of name-stub that we'll update later on in this script
$parent_location = Location::firstOrCreate(['name' => $parent_name]);
if (array_key_exists('Name', $parent_row)) {
$this->info('Parent for '.$parent_row['Name'].' is '.$parent_name.'. Attempting to save '.$parent_name.'.');
}
// Check if the record was updated or created.
// This is mostly for clearer debugging.
if ($parent_location->exists) {
$this->info('- Parent location '.$parent_name.' already exists.');
} else {
$this->info('- Parent location '.$parent_name.' was created.');
}
} else {
$this->info('- No Parent Name provided, so no parent location will be created.');
}
}
$this->info('----- Parents Created.... backfilling additional details... --------');
// Loop through ALL records and add/update them if there are additional fields
// besides name
foreach ($results as $index => $row) {
if (array_key_exists('Parent Name', $row)) {
$parent_name = trim($row['Parent Name']);
} else {
$parent_name = null;
}
// Set the location attributes to save
if (array_key_exists('Name', $row)) {
$location = Location::firstOrCreate(['name' => trim($row['Name'])]);
$location->name = trim($row['Name']);
$this->info('Checking location: '.$location->name);
} else {
$this->error('Location name is required and is missing from at least one row in this dataset. Check your CSV for extra trailing rows and try again.');
return false;
}
if (array_key_exists('Currency', $row)) {
$location->currency = trim($row['Currency']);
}
if (array_key_exists('Address 1', $row)) {
$location->address = trim($row['Address 1']);
}
if (array_key_exists('Address 2', $row)) {
$location->address2 = trim($row['Address 2']);
}
if (array_key_exists('City', $row)) {
$location->city = trim($row['City']);
}
if (array_key_exists('State', $row)) {
$location->state = trim($row['State']);
}
if (array_key_exists('Zip', $row)) {
$location->zip = trim($row['Zip']);
}
if (array_key_exists('Country', $row)) {
$location->country = trim($row['Country']);
}
if (array_key_exists('OU', $row)) {
$location->ldap_ou = trim($row['OU']);
}
// If a parent name is provided, we created it earlier in the script,
// so let's grab that ID
if ($parent_name) {
$this->info('-- Searching for Parent Name: '.$parent_name);
$parent = Location::where('name', '=', $parent_name)->first();
$location->parent_id = $parent->id;
$this->info('Parent: '.$parent_name.' - ID: '.$parent->id);
}
// Make sure the more advanced (non-name) fields pass validation
if (($location->isValid()) && ($location->save())) {
// Check if the record was updated or created.
// This is mostly for clearer debugging.
if ($location->exists) {
$this->info('Location '.$location->name.' already exists. Updating...');
} else {
$this->info('- Location '.$location->name.' was created. ');
}
// If there's a validation error, display that
} else {
$this->error('- Non-parent Location '.$location->name.' could not be created: '.$location->getErrors());
}
}
}
}
-60
View File
@@ -1,60 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class KillAllSessions extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:global-logout {--force : Skip the danger prompt; assuming you enter "y"} ';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command will destroy all web sessions on disk and will force a re-login for all users.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (! $this->option('force') && ! $this->confirm("****************************************************\nTHIS WILL FORCE A LOGIN FOR ALL LOGGED IN USERS.\n\nAre you SURE you wish to continue? ")) {
return $this->error('Session loss not confirmed');
}
$session_files = glob(storage_path('framework/sessions/*'));
$count = 0;
foreach ($session_files as $file) {
if (is_file($file)) {
unlink($file);
}
$count++;
}
\DB::table('users')->update(['remember_token' => null]);
$this->info($count.' sessions cleared!');
}
}
-569
View File
@@ -1,569 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\Department;
use App\Models\Group;
use App\Models\Ldap;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class LdapSync extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=*} {--base_dn=} {--filter=} {--delete} {--summary} {--json_summary}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command line LDAP sync';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// If LDAP enabled isn't set to 1 (ldap_enabled!=1) then we should cut this short immediately without going any further
if (Setting::getSettings()->ldap_enabled != '1') {
$this->error('LDAP is not enabled. Aborting. See Settings > LDAP to enable it.');
exit();
}
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); // 600 seconds = 10 minutes
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
// Map the LDAP attributes to the Snipe-IT user fields.
$ldap_map = [
'username' => Setting::getSettings()->ldap_username_field,
'last_name' => Setting::getSettings()->ldap_lname_field,
'first_name' => Setting::getSettings()->ldap_fname_field,
'active_flag' => Setting::getSettings()->ldap_active_flag,
'emp_num' => Setting::getSettings()->ldap_emp_num,
'email' => Setting::getSettings()->ldap_email,
'phone' => Setting::getSettings()->ldap_phone_field,
'mobile' => Setting::getSettings()->ldap_mobile,
'jobtitle' => Setting::getSettings()->ldap_jobtitle,
'address' => Setting::getSettings()->ldap_address,
'city' => Setting::getSettings()->ldap_city,
'state' => Setting::getSettings()->ldap_state,
'zip' => Setting::getSettings()->ldap_zip,
'country' => Setting::getSettings()->ldap_country,
'location' => Setting::getSettings()->ldap_location,
'dept' => Setting::getSettings()->ldap_dept,
'manager' => Setting::getSettings()->ldap_manager,
'display_name' => Setting::getSettings()->ldap_display_name,
];
$ldap_default_group = Setting::getSettings()->ldap_default_group;
$search_base = Setting::getSettings()->ldap_base_dn;
try {
$ldapconn = Ldap::connectToLdap();
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::info($e);
return [];
}
$summary = [];
$seen_ldap_usernames = [];
try {
/**
* if a location ID has been specified, use that OU
*/
if ($this->option('location_id')) {
foreach ($this->option('location_id') as $location_id) {
$location_ou = Location::where('id', '=', $location_id)->value('ldap_ou');
$search_base = $location_ou;
Log::debug('Importing users from specified location OU: \"'.$search_base.'\".');
}
}
/**
* if a manual base DN has been specified, use that. Allow the Base DN to override
* even if there's a location-based DN - if you picked it, you must have picked it for a reason.
*/
if ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
Log::debug('Importing users from specified base DN: \"'.$search_base.'\".');
}
/**
* If a filter has been specified, use that, otherwise default to null
*/
if ($this->option('filter') != '') {
$filter = $this->option('filter');
} else {
$filter = null;
}
/**
* We only need to request the LDAP attributes that we process
*/
$attributes = array_values(array_filter($ldap_map));
if (Setting::getSettings()->is_ad === 1 && is_null($ldap_map['active_flag'])) {
$attributes[] = 'useraccountcontrol';
}
$results = Ldap::findLdapUsers($search_base, -1, $filter, $attributes);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = ['error' => true, 'error_message' => $e->getMessage(), 'summary' => []];
$this->info(json_encode($json_summary));
}
Log::info($e);
return [];
}
/* Determine which location to assign users to by default. */
$default_location = null;
if ($this->option('location') != '') {
if ($default_location = Location::where('name', '=', $this->option('location'))->first()) {
Log::debug('Location name '.$this->option('location').' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
}
} elseif ($this->option('location_id')) {
// TODO - figure out how or why this is an array?
foreach ($this->option('location_id') as $location_id) {
if ($default_location = Location::where('id', '=', $location_id)->first()) {
Log::debug('Location ID '.$location_id.' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
}
}
}
if (! isset($default_location)) {
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
/* Process locations with explicitly defined OUs, if doing a full import. */
if ($this->option('base_dn') == '' && $this->option('filter') == '') {
// 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 = [];
foreach ($ldap_ou_locations as $ou_loc) {
$ldap_ou_lengths[] = strlen($ou_loc['ldap_ou']);
}
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
if (count($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'] = null;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
try {
$location_users = Ldap::findLdapUsers($ldap_loc['ldap_ou']);
} catch (\Exception $e) { // TODO: this is stolen from line 77 or so above
if ($this->option('json_summary')) {
$json_summary = ['error' => true, 'error_message' => trans('admin/users/message.error.ldap_could_not_search').' Location: '.$ldap_loc['name'].' (ID: '.$ldap_loc['id'].') cannot connect to "'.$ldap_loc['ldap_ou'].'" - '.$e->getMessage(), 'summary' => []];
$this->info(json_encode($json_summary));
}
Log::info($e);
return [];
}
$usernames = [];
for ($i = 0; $i < $location_users['count']; $i++) {
if (array_key_exists($ldap_map['username'], $location_users[$i])) {
$location_users[$i]['ldap_location_override'] = true;
$location_users[$i]['location_id'] = $ldap_loc['id'];
$usernames[] = $location_users[$i][$ldap_map['username']][0];
}
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_map['username'], $generic_entry))) {
if (in_array($generic_entry[$ldap_map['username']][0], $usernames)) {
unset($results[$key]);
}
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
}
$manager_cache = [];
if ($ldap_default_group != null) {
$default = Group::find($ldap_default_group);
if (! $default) {
$ldap_default_group = null; // un-set the default group if that group doesn't exist
}
}
// Assign the mapped LDAP attributes for each user to the Snipe-IT user fields
for ($i = 0; $i < $results['count']; $i++) {
$item = [];
$item['username'] = $results[$i][$ldap_map['username']][0] ?? null;
$item['display_name'] = $results[$i][$ldap_map['display_name']][0] ?? null;
$item['employee_number'] = $results[$i][$ldap_map['emp_num']][0] ?? null;
$item['lastname'] = $results[$i][$ldap_map['last_name']][0] ?? null;
$item['firstname'] = $results[$i][$ldap_map['first_name']][0] ?? null;
$item['email'] = $results[$i][$ldap_map['email']][0] ?? null;
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? null;
$item['location_id'] = $results[$i]['location_id'] ?? null;
$item['telephone'] = $results[$i][$ldap_map['phone']][0] ?? null;
$item['mobile'] = $results[$i][$ldap_map['mobile']][0] ?? null;
$item['jobtitle'] = $results[$i][$ldap_map['jobtitle']][0] ?? null;
$item['address'] = $results[$i][$ldap_map['address']][0] ?? null;
$item['city'] = $results[$i][$ldap_map['city']][0] ?? null;
$item['state'] = $results[$i][$ldap_map['state']][0] ?? null;
$item['country'] = $results[$i][$ldap_map['country']][0] ?? null;
$item['zip'] = $results[$i][$ldap_map['zip']][0] ?? null;
$item['department'] = $results[$i][$ldap_map['dept']][0] ?? null;
$item['manager'] = $results[$i][$ldap_map['manager']][0] ?? null;
$item['location'] = $results[$i][$ldap_map['location']][0] ?? null;
$location = $default_location; // initially, set '$location' to the default_location (which may just be null)
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
if ($ldap_map['location'] && $item['location']) {
$location = Location::firstOrCreate([
'name' => $item['location'],
]);
}
$department = Department::firstOrCreate([
'name' => $item['department'],
]);
$user = User::withTrashed()->where('username', $item['username'])->first();
if (! empty($item['username'])) {
$seen_ldap_usernames[] = $item['username'];
}
if ($user) {
if ($user->trashed()) {
$user->restore();
}
// Updating an existing user.
$item['createorupdate'] = 'updated';
} else {
// Creating a new user.
$user = new User;
$user->password = $user->noPassword();
$user->locale = app()->getLocale();
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
$item['createorupdate'] = 'created';
}
// If a sync option is not filled in on the LDAP settings don't populate the user field
if ($ldap_map['username'] != null) {
$user->username = $item['username'];
}
if ($ldap_map['display_name'] != null) {
$user->display_name = $item['display_name'];
}
if ($ldap_map['last_name'] != null) {
$user->last_name = $item['lastname'];
}
if ($ldap_map['first_name'] != null) {
$user->first_name = $item['firstname'];
}
if ($ldap_map['emp_num'] != null) {
$user->employee_num = e($item['employee_number']);
}
if ($ldap_map['email'] != null) {
$user->email = $item['email'];
}
if ($ldap_map['phone'] != null) {
$user->phone = $item['telephone'];
}
if ($ldap_map['mobile'] != null) {
$user->mobile = $item['mobile'];
}
if ($ldap_map['jobtitle'] != null) {
$user->jobtitle = $item['jobtitle'];
}
if ($ldap_map['address'] != null) {
$user->address = $item['address'];
}
if ($ldap_map['city'] != null) {
$user->city = $item['city'];
}
if ($ldap_map['state'] != null) {
$user->state = $item['state'];
}
if ($ldap_map['country'] != null) {
$user->country = $item['country'];
}
if ($ldap_map['zip'] != null) {
$user->zip = $item['zip'];
}
if ($ldap_map['dept'] != null) {
$user->department_id = $department->id;
}
if ($ldap_map['location'] != null) {
$user->location_id = $location?->id;
}
if ($ldap_map['manager'] != null) {
if ($item['manager'] != null) {
// Check Cache first
if (isset($manager_cache[$item['manager']])) {
// found in cache; use that and avoid extra lookups
$user->manager_id = $manager_cache[$item['manager']];
} else {
// Get the LDAP Manager
try {
$ldap_manager = Ldap::findLdapUsers($item['manager'], -1, $this->option('filter'));
} catch (\Exception $e) {
Log::warning('Manager lookup caused an exception: '.$e->getMessage().'. Falling back to direct username lookup');
// Hail-mary for Okta manager 'shortnames' - will only work if
// Okta configuration is using full email-address-style usernames
$ldap_manager = [
'count' => 1,
0 => [
$ldap_map['username'] => [$item['manager']],
],
];
}
$add_manager_to_cache = true;
if ($ldap_manager['count'] > 0) {
try {
// Get the Manager's username
// PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array.
$ldapManagerUsername = $ldap_manager[0][$ldap_map['username']][0];
// Get User from Manager username.
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
if ($ldap_manager && isset($ldap_manager->id)) {
// Link user to manager id.
$user->manager_id = $ldap_manager->id;
}
} catch (\Exception $e) {
$add_manager_to_cache = false;
\Log::warning('Handling ldap manager '.$item['manager'].' caused an exception: '.$e->getMessage().'. Continuing synchronization.');
}
}
if ($add_manager_to_cache) {
$manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed'
}
}
}
}
// Sync activated state for Active Directory.
if (! empty($ldap_map['active_flag'])) { // IF we have an 'active' flag set....
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
// (Specifically, we don't handle a value of '0.0' correctly)
$raw_value = @$results[$i][$ldap_map['active_flag']][0];
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$boolean_cast = (bool) $raw_value;
if (Setting::getSettings()->ldap_invert_active_flag === 1) {
// Because ldap_active_flag is set, if filter_var is true or boolean_cast is true, then user is suspended
$user->activated = ! ($filter_var ?? $boolean_cast);
} else {
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
}
} elseif (array_key_exists('useraccountcontrol', $results[$i])) {
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
/* The following is _probably_ the correct logic, but we can't use it because
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
'512', // 0x200 NORMAL_ACCOUNT
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'4194816', // 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH
'4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
'1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
'1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED,
];
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
// If we're not using AD, and there isn't an activated flag set, activate all users
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
already-existing accounts will be however the administrator has set them */
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (! empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id; // THIS is the magic line, this should do it.
}
}
// TODO - should we be NULLING locations if $location is really `null`, and that's what we came up with?
// will that conflict with any overriding setting that the user set? Like, if they moved someone from
// the 'null' location to somewhere, we wouldn't want to try to override that, right?
$location = null;
$user->ldap_import = 1;
$errors = '';
if ($user->save()) {
$item['id'] = $user->id;
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
// Check if the relationship already exists
if (! $user->groups()->where('group_id', $ldap_default_group)->exists()) {
$user->groups()->attach($ldap_default_group);
}
}
// updates assets location based on user's location
if ($user->wasChanged('location_id')) {
foreach ($user->assets as $asset) {
$asset->location_id = $user->location_id;
// TODO: somehow add note? "Asset Location Changed because of thing"
$asset->save();
}
}
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
}
$item['note'] = $errors;
$item['status'] = 'error';
}
array_push($summary, $item);
}
// Optionally soft-delete LDAP-imported users that are no longer present in LDAP.
// users with assests etc. are not deletable and skipped
if ($this->option('delete')) {
$missing_ldap_users = User::where('ldap_import', 1);
$missing_ldap_users = $missing_ldap_users->whereNotIn('username', $seen_ldap_usernames);
$missing_ldap_users = $missing_ldap_users->get();
foreach ($missing_ldap_users as $missing_user) {
$is_deletable = $this->isUserDeletable($missing_user);
$missing_item = [
'id' => $missing_user->id,
'username' => $missing_user->username,
'firstname' => $missing_user->first_name,
'lastname' => $missing_user->last_name,
'email' => $missing_user->email,
'createorupdate' => 'skipped',
'status' => 'info',
'deletable' => $is_deletable,
'note' => $is_deletable ? 'missing from LDAP' : 'missing from LDAP, but not deletable',
];
if ($is_deletable) {
$missing_user->delete();
$missing_item['createorupdate'] = 'deleted';
$missing_item['status'] = 'success';
$missing_item['note'] = 'deleted_missing_from_ldap';
}
$summary[] = $missing_item;
}
}
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']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} elseif ($this->option('json_summary')) {
$json_summary = ['error' => false, 'error_message' => '', 'summary' => $summary]; // hardcoding the error to false and the error_message to blank seems a bit weird
$this->info(json_encode($json_summary));
} else {
return $summary;
}
}
/**
* Checks if the user is deletable without gate check
*
* A user is considered deletable if they have no associated assets, accessories, licenses, consumables, managed users, or managed locations.
*
* @param User $user The user to check
*
* @return bool True if the user is deletable, false otherwise
*/
private function isUserDeletable(User $user): bool
{
return (($user->assets_count ?? $user->assets()->count()) === 0)
&& (($user->accessories_count ?? $user->accessories()->count()) === 0)
&& (($user->licenses_count ?? $user->licenses()->count()) === 0)
&& (($user->consumables_count ?? $user->consumables()->count()) === 0)
&& (($user->manages_users_count ?? $user->managesUsers()->count()) === 0)
&& (($user->manages_locations_count ?? $user->managedLocations()->count()) === 0);
}
}
-650
View File
@@ -1,650 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Ldap;
use App\Models\Setting;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Crypt;
/**
* Check if a given ip is in a network
*
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
* @return bool true if the ip is in this range / false if not.
*/
function ip_in_range($ip, $range)
{
if (strpos($range, '/') == false) {
$range .= '/32';
}
// $range is in IP/CIDR format eg 127.0.0.1/24
[$range, $netmask] = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~$wildcard_decimal;
return ($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal);
}
// NOTE - this function was shamelessly stolen from this gist: https://gist.github.com/tott/7684443
/**
* Ensure LDAP filters are parentheses-wrapped
*/
function parenthesized_filter($filter)
{
if (substr($filter, 0, 1) == '(') {
return $filter;
} else {
return '('.$filter.')';
}
}
class LdapTroubleshooter extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ldap:troubleshoot
{--ldap-search : Output an ldapsearch command-line for testing your LDAP config}
{--force : Skip the interactive yes/no prompt for confirmation}
{--debug : Include debugging output (verbose)}
{--trace : Include extremely verbose LDAP trace output}
{--timeout=15 : Timeout for LDAP Bind operations}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Runs a series of non-destructive LDAP commands to help try and determine correct LDAP settings for your environment.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Output something *only* if debug is enabled
*
* @return void
*/
public function debugout($string)
{
if ($this->option('debug')) {
$this->line($string);
}
}
/**
* Clean the results from ldap_get_entries into something useful
*
* @param array $array
* @return array
*/
public function ldap_results_cleaner($array)
{
$cleaned = [];
for ($i = 0; $i < $array['count']; $i++) {
$row = $array[$i];
$clean_row = [];
foreach ($row as $key => $val) {
$this->debugout('Key is: '.$key);
if ($key == 'count' || is_int($key) || $key == 'dn') {
$this->debugout(" and we're gonna skip it\n");
continue;
}
$this->debugout(" And that seems fine.\n");
if (array_key_exists('count', $val)) {
if ($val['count'] == 1) {
$clean_row[$key] = $val[0];
} else {
unset($val['count']); // these counts are annoying
$elements = [];
foreach ($val as $entry) {
if (isset($ldap_constants[$entry])) {
$elements[] = $ldap_constants[$entry];
} else {
$elements[] = $entry;
}
}
$clean_row[$key] = $elements;
}
} else {
$clean_row[$key] = $val;
}
}
$cleaned[$i] = $clean_row;
}
return $cleaned;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->option('trace')) {
ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7);
}
$settings = Setting::getSettings();
$this->settings = $settings;
if ($this->option('ldap-search')) {
if (! $this->option('force')) {
$confirmation = $this->confirm('WARNING: This command will display your LDAP password on your terminal. Are you sure this is ok?');
if (! $confirmation) {
$this->error('ABORTING');
exit(-1);
}
}
$output = [];
if ($settings->ldap_server_cert_ignore) {
$this->line('# Ignoring server certificate validity');
$output[] = 'LDAPTLS_REQCERT=never';
}
if ($settings->ldap_client_tls_cert && $settings->ldap_client_tls_key) {
$this->line('# Adding LDAP Client Certificate and Key');
$output[] = 'LDAPTLS_CERT=storage/ldap_client_tls.cert';
$output[] = 'LDAPTLS_KEY=storage/ldap_client_tls.key';
}
$output[] = 'ldapsearch';
$output[] = '-H '.$settings->ldap_server;
$output[] = '-x';
$output[] = '-b '.escapeshellarg($settings->ldap_basedn);
$output[] = '-D '.escapeshellarg($settings->ldap_uname);
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (Exception $e) {
$this->warn('Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.');
exit(0);
}
$output[] = '-w '.escapeshellarg($w);
$output[] = escapeshellarg(parenthesized_filter($settings->ldap_filter));
if ($settings->ldap_tls) {
$this->line('# adding STARTTLS option');
$output[] = '-Z';
}
$output[] = '-v';
$this->line("\n");
$this->line(implode(" \\\n", $output));
exit(0);
}
// PHP Version check for warning
$php_version = phpversion();
[$major, $minor, $patch] = explode('.', $php_version);
if (
$major < 8 ||
($major == 8 && $minor < 3) ||
($major == 8 && $minor == 3 && $patch < 21) ||
($major == 8 && $minor == 4 && $patch < 7)
) {
$this->warn("PHP Version: $php_version WARNING - Versions before 8.3.21 or 8.4.7 will return INCONSISTENT results!");
if (! $this->confirm('Are you sure you wish to continue?')) {
$this->warn('ABORTING');
exit(-1);
}
}
if (! $this->option('force')) {
$confirmation = $this->confirm('WARNING: This command will make several attempts to connect to your LDAP server. Are you sure this is ok?');
if (! $confirmation) {
$this->error('ABORTING');
exit(-1);
}
}
// $this->line(print_r($settings,true));
$this->line('STAGE 1: Checking settings');
if (! $settings->ldap_enabled) {
$this->error("WARNING: Snipe-IT's LDAP setting is not turned on. (That may be OK if you're still trying to figure out settings)");
}
$ldap_conn = false;
try {
$ldap_conn = ldap_connect($settings->ldap_server);
} catch (Exception $e) {
$this->error("WARNING: Exception caught when executing 'ldap_connect()' - ".$e->getMessage().'. We will try to guess.');
}
if (! $ldap_conn) {
$this->error('WARNING: LDAP Server setting of: '.$settings->ldap_server.' cannot be parsed. We will try to guess.');
// exit(-1);
}
// since we never use $ldap_conn again, we don't have to ldap_unbind() it (it's not even connected, tbh - that only happens at bind-time)
$parsed = parse_url($settings->ldap_server);
if (@$parsed['scheme'] != 'ldap' && @$parsed['scheme'] != 'ldaps') {
$this->error("WARNING: LDAP URL Scheme of '".@$parsed['scheme']."' is probably incorrect; should usually be ldap or ldaps");
}
if (! @$parsed['host']) {
$this->error('ERROR: Cannot determine hostname or IP from ldap URL: '.$settings->ldap_server.'. ABORTING.');
exit(-1);
} else {
$this->info('Determined LDAP hostname to be: '.$parsed['host']);
}
$raw_ips = [];
if (inet_pton($parsed['host']) !== false) {
$this->line($parsed['host'].' already looks like an address; skipping DNS lookup');
$raw_ips[] = $parsed['host'];
} else {
$this->line('Performing DNS lookup of: '.$parsed['host']);
$ips = dns_get_record($parsed['host']);
// $this->info("Host IP is: ".print_r($ips,true));
if (! $ips || count($ips) == 0) {
$this->error('ERROR: DNS lookup of host: '.$parsed['host'].' has failed. ABORTING.');
exit(-1);
}
$this->debugout("IP's? ".print_r($ips, true));
foreach ($ips as $ip) {
if (! isset($ip['ip'])) {
continue;
}
$raw_ips[] = $ip['ip'];
}
}
foreach ($raw_ips as $ip) {
if ($ip == '127.0.0.1') {
$this->error('WARNING: Using the localhost IP as the LDAP server. This is usually wrong');
}
if (ip_in_range($ip, '10.0.0.0/8') || ip_in_range($ip, '192.168.0.0/16') || ip_in_range($ip, '172.16.0.0/12')) {
$this->error('WARNING: Using an RFC1918 Private address for LDAP server. This may be correct, but it can be a problem if your Snipe-IT instance is not hosted on your private network');
}
}
$this->line('STAGE 2: Checking basic network connectivity');
$ports = [636, 389];
if (@$parsed['port'] && ! in_array($parsed['port'], $ports)) {
$ports[] = $parsed['port'];
}
$open_ports = [];
foreach ($ports as $port) {
$errno = 0;
$errstr = '';
$timeout = 30.0;
$result = '';
$this->line('Attempting to connect to port: '.$port." - may take up to $timeout seconds");
try {
$result = fsockopen($parsed['host'], $port, $errno, $errstr, 30.0);
} catch (Exception $e) {
$this->error('Exception: '.$e->getMessage());
}
if ($result) {
$this->info('Success!');
$open_ports[] = $port;
} else {
$this->error("WARNING: Cannot connect to port: $port - $errstr ($errno)");
}
}
if (count($open_ports) == 0) {
$this->error('ERROR - no open ports. ABORTING.');
exit(-1);
}
$this->line('STAGE 3: Determine encryption algorithm, if any');
$ldap_urls = []; // [url, cert-check?, start_tls?]
$pretty_ldap_urls = [];
foreach ($open_ports as $port) {
$this->line("Trying TLS first for port $port");
$ldap_url = 'ldaps://'.$parsed['host'].":$port";
if ($this->test_anonymous_bind($ldap_url)) {
$this->info("Anonymous bind succesful to $ldap_url!");
$ldap_urls[] = [$ldap_url, true, false];
$pretty_ldap_urls[] = [$ldap_url, 'enabled', 'n/a (no)'];
continue; // TODO - lots of copypasta in these if(test_anonymous_bind()) routines...
} else {
$this->error("WARNING: Failed to bind to $ldap_url - trying without certificate checks.");
}
if ($this->test_anonymous_bind($ldap_url, false)) {
$this->info("Anonymous bind successful to $ldap_url with certificate-checks disabled");
$ldap_urls[] = [$ldap_url, false, false];
$pretty_ldap_urls[] = [$ldap_url, 'DISABLED', 'n/a (no)'];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with certificate checks disabled. Trying unencrypted with STARTTLS");
}
// now switching to ldap:// URL's from ldaps://
$ldap_url = 'ldap://'.$parsed['host'].":$port";
if ($this->test_anonymous_bind($ldap_url, true, true)) {
$this->info("Plain connection to $ldap_url with STARTTLS succesful!");
$ldap_urls[] = [$ldap_url, true, true];
$pretty_ldap_urls[] = [$ldap_url, 'enabled', 'STARTTLS ENABLED'];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled. Trying without certificate checks.");
}
if ($this->test_anonymous_bind($ldap_url, false, true)) {
$this->info("Plain connection to $ldap_url with STARTTLS and cert checks *disabled* successful!");
$ldap_urls[] = [$ldap_url, false, true];
$pretty_ldap_urls[] = [$ldap_url, 'DISABLED', 'STARTTLS ENABLED'];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled, and cert checks disabled. Trying without STARTTLS");
}
if ($this->test_anonymous_bind($ldap_url)) {
$this->info("Plain connection to $ldap_url succesful!");
$ldap_urls[] = [$ldap_url, true, false];
$pretty_ldap_urls[] = [$ldap_url, 'n/a', 'starttls disabled'];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url. Giving up on port $port");
}
}
$this->debugout(print_r($ldap_urls, true));
if (count($ldap_urls) > 0) {
$this->debugout("Found working LDAP URL's: ");
foreach ($ldap_urls as $ldap_url) { // TODO maybe do this as a $this->table() instead?
$this->debugout('LDAP URL: '.$ldap_url[0]);
$this->debugout($ldap_url[0].($ldap_url[1] ? ' certificate checks enabled' : ' certificate checks disabled').($ldap_url[2] ? ' STARTTLS Enabled ' : ' STARTTLS Disabled'));
}
$this->table(['URL', 'Cert Checks?', 'STARTTLS?'], $pretty_ldap_urls);
} else {
$this->error("ERROR - no valid LDAP URL's available - ABORTING");
exit(1);
}
$this->line('STAGE 4: Test Administrative Bind for LDAP Sync');
foreach ($ldap_urls as $ldap_url) {
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (Exception $e) {
$this->warn('Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.');
exit(0);
}
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, $w);
}
$this->line('STAGE 5: Test BaseDN');
// grab all LDAP_ constants and fill up a reversed array mapping from weird LDAP dotted-strings to (Constant Name)
$all_defined_constants = get_defined_constants();
$ldap_constants = [];
foreach ($all_defined_constants as $key => $val) {
if (starts_with($key, 'LDAP_') && is_string($val)) {
$ldap_constants[$val] = $key; // INVERT the meaning here!
}
}
$this->debugout('LDAP constants are: '.print_r($ldap_constants, true));
foreach ($ldap_urls as $ldap_url) {
try {
$w = Crypt::Decrypt($settings->ldap_pword);
} catch (Exception $e) {
$this->warn('Could not decrypt password. This usually means an LDAP password was not set or the APP_KEY was changed since the LDAP pasword was last saved. Aborting.');
exit(0);
}
if ($this->test_informational_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, $w, $settings)) {
$this->info('Success getting informational bind!');
} else {
$this->error('Unable to get information from bind.');
}
}
$this->line('STAGE 6: Test LDAP Login to Snipe-IT');
foreach ($ldap_urls as $ldap_url) {
$this->line('Starting auth to '.$ldap_url[0]);
while (true) {
$with_tls = $ldap_url[1] ? 'with' : 'without';
$with_startssl = $ldap_url[2] ? 'using' : 'not using';
if (! $this->confirm('Do you wish to try to authenticate to this directory: '.$ldap_url[0]." $with_tls TLS and $with_startssl STARTSSL?")) {
break;
}
$username = $this->ask('Username');
$password = $this->secret('Password');
$results = $this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $username, $password); // FIXME - should do some other stuff here, maybe with the concatenating or something? maybe? and/or should put up some results?
if ($results) {
$this->info('Success authenticating with '.$username);
} else {
$this->error('Unable to authenticate with '.$username);
}
}
}
$this->info('LDAP TROUBLESHOOTING COMPLETE!');
}
public function connect_to_ldap($ldap_url, $check_cert, $start_tls)
{
if ($check_cert) {
$this->line('we *ARE* checking certs');
Ldap::ignoreCertificates(false);
} else {
$this->line('we are IGNORING certs');
Ldap::ignoreCertificates(true);
}
$lconn = ldap_connect($ldap_url);
ldap_set_option($lconn, LDAP_OPT_PROTOCOL_VERSION, 3); // should we 'test' different protocol versions here? Does anyone even use anything other than LDAPv3?
// no - it's formally deprecated: https://tools.ietf.org/html/rfc3494
if ($this->settings->ldap_client_tls_cert && $this->settings->ldap_client_tls_key) {
// client-side TLS certificate support for LDAP (Google Secure LDAP)
putenv('LDAPTLS_CERT=storage/ldap_client_tls.cert');
putenv('LDAPTLS_KEY=storage/ldap_client_tls.key');
}
if ($start_tls) {
if (! ldap_start_tls($lconn)) {
$this->error('WARNING: Unable to start TLS');
return false;
}
}
if (! $lconn) {
$this->error('WARNING: Failed to generate connection string - using: '.$ldap_url);
return false;
}
$net = ldap_set_option($lconn, LDAP_OPT_NETWORK_TIMEOUT, $this->option('timeout'));
$time = ldap_set_option($lconn, LDAP_OPT_TIMELIMIT, $this->option('timeout'));
if (! $net || ! $time) {
$this->error('Unable to set timeouts!');
}
return $lconn;
}
public function test_anonymous_bind($ldap_url, $check_cert = true, $start_tls = false)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls) {
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$this->line('Attempting to bind now, this can take a while if we mess it up');
$bind_results = ldap_bind($lconn);
$this->line('Bind results are: '.$bind_results.' which translate into boolean: '.(bool) $bind_results);
ldap_close($lconn);
return (bool) $bind_results;
} catch (Exception $e) {
$this->error('WARNING: Exception caught during bind - '.$e->getMessage());
return false;
}
});
}
public function test_authed_bind($ldap_url, $check_cert, $start_tls, $username, $password)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password) {
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$bind_results = ldap_bind($lconn, $username, $password);
ldap_close($lconn);
if (! $bind_results) {
$this->error("WARNING: Failed to bind to $ldap_url as $username");
return false;
} else {
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
return (bool) $lconn;
}
} catch (Exception $e) {
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
return false;
}
});
}
public function test_informational_bind($ldap_url, $check_cert, $start_tls, $username, $password, $settings)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password, $settings) {
try { // TODO - copypasta'ed from test_authed_bind
$conn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$bind_results = ldap_bind($conn, $username, $password);
if (! $bind_results) {
$this->error("WARNING: Failed to bind to $ldap_url as $username");
return false;
}
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
$cleaned_results = [];
try {
// This _may_ only work for Active Directory?
$result = ldap_read($conn, '', '(objectClass=*)'/* , ['supportedControl'] */);
$results = ldap_get_entries($conn, $result);
$cleaned_results = $this->ldap_results_cleaner($results);
// $this->line(print_r($cleaned_results,true));
$default_naming_contexts = $cleaned_results[0]['namingcontexts'];
$this->info('Default Naming Contexts:');
$this->info(implode(', ', $default_naming_contexts));
// okay, great - now how do we display those results? I have no idea.
} catch (Exception $e) {
$this->error("Unable to get base naming contexts - here's what we *did* get:");
$this->line(print_r($cleaned_results, true));
}
// I don't see why this throws an Exception for Google LDAP, but I guess we ought to try and catch it?
$this->debugout("I guess we're trying to do the ldap search here, but sometimes it takes too long?");
$this->debugout('Base DN is: '.$settings->ldap_basedn.' and filter is: '.parenthesized_filter($settings->ldap_filter));
$search_results = ldap_search($conn, $settings->ldap_basedn, parenthesized_filter($settings->ldap_filter));
$entries = ldap_get_entries($conn, $search_results);
$this->info('Printing first 10 results: ');
$pretty_data = array_slice($this->ldap_results_cleaner($entries), 0, 10);
// print_r($data);
$headers = [];
foreach ($pretty_data as $row) {
// populate headers
foreach ($row as $key => $value) {
// skip objectsid and objectguid because it junks up output
if ($key == 'objectsid' || $key == 'objectguid') {
continue;
}
if (! in_array($key, $headers)) {
$headers[] = $key;
}
}
}
$table = [];
// repeat again to populate table
foreach ($pretty_data as $row) {
$newrow = [];
foreach ($headers as $header) {
if (is_array(@$row[$header])) {
$newrow[] = '['.implode(', ', $row[$header]).']';
} else {
$newrow[] = @$row[$header];
}
}
$table[] = $newrow;
}
$this->table($headers, $table);
} catch (Exception $e) {
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
return false;
} finally {
ldap_close($conn);
}
});
}
/***********************************************
*
* This function executes $function - which is expected to be some kind of executable function -
* with a timeout set. It respects the timeout by forking execution and setting a strict timer
* for which to get back a SIGUSR1 or SIGUSR2 signal from the forked process.
*
***********************************************/
private function timed_boolean_execute($function)
{
if (! (function_exists('pcntl_sigtimedwait') && function_exists('posix_getpid') && function_exists('pcntl_fork') && function_exists('posix_kill') && function_exists('pcntl_wifsignaled'))) {
// POSIX functions needed for forking aren't present, just run the function inline (ignoring timeout)
$this->line('WARNING: Unable to execute POSIX fork() commands, timeout may not be respected');
return $function();
} else {
$parent_pid = posix_getpid();
$pid = pcntl_fork();
switch ($pid) {
case 0:
// we're the 'child'
if ($function()) {
// SUCCESS = SIGUSR1
posix_kill($parent_pid, SIGUSR1);
} else {
// FAILURE = SIGUSR2
posix_kill($parent_pid, SIGUSR2);
}
exit();
break; // yes I know we don't need it.
case -1:
// couldn't fork
$this->error('COULD NOT FORK - assuming failure');
return false;
break; // I still know that we don't need it
default:
// we remain the 'parent', $pid is the PID of the forked process.
$siginfo = [];
$exit_status = pcntl_sigtimedwait([SIGUSR1, SIGUSR2], $siginfo, $this->option('timeout'));
if ($exit_status == SIGUSR1) {
return true;
} else {
posix_kill($pid, SIGKILL); // make sure we don't have processes hanging around that might try and send signals during later executions, confusing us
return false;
}
break; // Yeah I get it already, shush.
}
}
}
}
@@ -1,127 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Events\UserMerged;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
class MergeUsersByUsername extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:merge-users';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows you to merge the history of users. It looks for users without an email address as their username and merges them into the version that does have an email username.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Get the list of users who have an email address as their username
$users = User::where('username', 'LIKE', '%@%')->whereNull('deleted_at')->get();
$this->info($users->count().' total non-deleted users whose usernames contain a @ symbol.');
foreach ($users as $user) {
$parts = explode('@', trim($user->username));
$this->info('Checking against username '.trim($parts[0]).'.');
$bad_users = User::where('username', '=', trim($parts[0]))
->whereNull('deleted_at')
->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations', 'uploads', 'acceptances')
->get();
foreach ($bad_users as $bad_user) {
$this->info($bad_user->username.' ('.$bad_user->id.') will be merged into '.$user->username.' ('.$user->id.') ');
// Walk the list of assets
foreach ($bad_user->assets as $asset) {
$this->info('Updating asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
$asset->assigned_to = $user->id;
if (! $asset->save()) {
$this->error('Could not update assigned_to field on asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
$this->error('Error saving: '.$asset->getErrors());
}
}
// Walk the list of licenses
foreach ($bad_user->licenses as $license) {
$this->info('Updating license '.$license->name.' '.$license->id.' to user '.$user->id);
$bad_user->licenses()->updateExistingPivot($license->id, ['assigned_to' => $user->id]);
}
// Walk the list of consumables
foreach ($bad_user->consumables as $consumable) {
$this->info('Updating consumable '.$consumable->id.' to user '.$user->id);
$bad_user->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $user->id]);
}
// Walk the list of accessories
foreach ($bad_user->accessories as $accessory) {
$this->info('Updating accessory '.$accessory->id.' to user '.$user->id);
$bad_user->accessories()->updateExistingPivot($accessory->id, ['assigned_to' => $user->id]);
}
// Walk the list of logs
foreach ($bad_user->userlog as $log) {
$this->info('Updating action log record '.$log->id.' to user '.$user->id);
$log->target_id = $user->id;
$log->save();
}
// Update any manager IDs
$this->info('Updating managed user records to user '.$user->id);
User::where('manager_id', '=', $bad_user->id)->update(['manager_id' => $user->id]);
// Update location manager IDs
foreach ($bad_user->managedLocations as $managedLocation) {
$this->info('Updating managed location record '.$managedLocation->name.' to manager '.$user->id);
$managedLocation->manager_id = $user->id;
$managedLocation->save();
}
foreach ($bad_user->uploads as $upload) {
$this->info('Updating upload log record '.$upload->id.' to user '.$user->id);
$upload->item_id = $user->id;
$upload->save();
}
foreach ($bad_user->acceptances as $acceptance) {
$this->info('Updating acceptance log record '.$acceptance->id.' to user '.$user->id);
$acceptance->item_id = $user->id;
$acceptance->save();
}
// Mark the user as deleted
$this->info('Marking the user as deleted');
$bad_user->deleted_at = Carbon::now()->timestamp;
$bad_user->save();
event(new UserMerged($bad_user, $user, null));
}
}
}
}
@@ -1,73 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Enums\ActionType;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class MigrateLicenseSeatQuantitiesInActionLogs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:migrate-license-seat-quantities-in-action-logs
{--no-interaction: Do not ask any interactive question}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Updates quantity field in action_logs table for license seats that were added or deleted.';
/**
* Execute the console command.
*/
public function handle()
{
$query = DB::table('action_logs')
->whereIn('action_type', [
ActionType::AddSeats->value,
ActionType::DeleteSeats->value,
])
->where('quantity', '=', 1)
->orderBy('id');
$count = $query->count();
if ($count === 0) {
$this->info('Nothing to update');
return 0;
}
$this->info("{$count} logs to update");
if ($this->option('no-interaction') || $this->confirm('Update quantities in the action log?')) {
$query->chunk(50, function ($logs) {
$logs->each(function ($log) {
$quantityFromNote = Str::between($log->note, 'ed ', ' seats');
if (! is_numeric($quantityFromNote)) {
$this->error('Could not parse quantity from ID: {id}', ['id' => $log->id]);
}
if ($log->quantity !== (int) $quantityFromNote) {
$this->info(vsprintf('Updating id: %s to quantity %s', [
'id' => $log->id,
'new_quantity' => $quantityFromNote,
]));
DB::table('action_logs')->where('id', $log->id)->update(['quantity' => (int) $quantityFromNote]);
}
});
});
}
return 0;
}
}
@@ -1,164 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class MoveUploadsToNewDisk extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:move-uploads {delete_local?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This will move your locally uploaded files to whatever your current disk is.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (config('filesystems.default') == 'local') {
$this->error('Your current disk is set to local so we cannot proceed.');
$this->warn("Please configure your .env settings for S3. \nChange your PUBLIC_FILESYSTEM_DISK value to 's3_public' and your PRIVATE_FILESYSTEM_DISK to s3_private.");
return false;
}
$delete_local = $this->argument('delete_local');
$public_uploads['accessories'] = glob('public/uploads/accessories'.'/*.*');
$public_uploads['assets'] = glob('public/uploads/assets'.'/*.*');
$public_uploads['avatars'] = glob('public/uploads/avatars'.'/*.*');
$public_uploads['categories'] = glob('public/uploads/categories'.'/*.*');
$public_uploads['companies'] = glob('public/uploads/companies'.'/*.*');
$public_uploads['components'] = glob('public/uploads/components'.'/*.*');
$public_uploads['consumables'] = glob('public/uploads/consumables'.'/*.*');
$public_uploads['departments'] = glob('public/uploads/departments'.'/*.*');
$public_uploads['locations'] = glob('public/uploads/locations'.'/*.*');
$public_uploads['manufacturers'] = glob('public/uploads/manufacturers'.'/*.*');
$public_uploads['suppliers'] = glob('public/uploads/suppliers'.'/*.*');
$public_uploads['assetmodels'] = glob('public/uploads/models'.'/*.*');
// iterate files
foreach ($public_uploads as $public_type => $public_upload) {
$type_count = 0;
$this->info('- There are '.count($public_upload).' PUBLIC '.$public_type.' files.');
for ($i = 0; $i < count($public_upload); $i++) {
$type_count++;
$filename = basename($public_upload[$i]);
try {
Storage::disk('public')->put('uploads/'.$public_type.'/'.$filename, file_get_contents($public_upload[$i]));
$new_url = Storage::disk('public')->url('uploads/'.$public_type.'/'.$filename, $filename);
$this->info($type_count.'. PUBLIC: '.$filename.' was copied to '.$new_url);
} catch (\Exception $e) {
Log::debug($e);
$this->error($e);
}
}
}
$logos = glob('public/uploads/setting*.*');
$this->info('- There are '.count($logos).' files that might be logos.');
$type_count = 0;
foreach ($logos as $logo) {
$this->info($logo);
$type_count++;
$filename = basename($logo);
Storage::disk('public')->put('uploads/'.$filename, file_get_contents($logo));
$this->info($type_count.'. LOGO: '.$filename.' was copied to '.env('PUBLIC_AWS_URL').'/uploads/'.$filename);
}
$private_uploads['assets'] = glob('storage/private_uploads/assets'.'/*.*');
$private_uploads['signatures'] = glob('storage/private_uploads/signatures'.'/*.*');
$private_uploads['audits'] = glob('storage/private_uploads/audits'.'/*.*');
$private_uploads['assetmodels'] = glob('storage/private_uploads/models'.'/*.*');
$private_uploads['imports'] = glob('storage/private_uploads/imports'.'/*.*');
$private_uploads['licenses'] = glob('storage/private_uploads/licenses'.'/*.*');
$private_uploads['users'] = glob('storage/private_uploads/users'.'/*.*');
$private_uploads['backups'] = glob('storage/private_uploads/backups'.'/*.*');
foreach ($private_uploads as $private_type => $private_upload) {
$this->info('- There are '.count($private_upload).' PRIVATE '.$private_type.' files.');
$type_count = 0;
for ($x = 0; $x < count($private_upload); $x++) {
$type_count++;
$filename = basename($private_upload[$x]);
try {
Storage::put($private_type.'/'.$filename, file_get_contents($private_upload[$x]));
$new_url = Storage::url($private_type.'/'.$filename, $filename);
$this->info($type_count.'. PRIVATE: '.$filename.' was copied to '.$new_url);
} catch (\Exception $e) {
Log::debug($e);
$this->error($e);
}
}
if ($delete_local == 'true') {
$public_delete_count = 0;
$private_delete_count = 0;
$this->info("\n\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
$this->warn("\nTHIS WILL DELETE ALL OF YOUR LOCAL UPLOADED FILES. \n\nThis cannot be undone, so you should take a backup of your system before you proceed.\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
if ($this->confirm('Do you wish to continue?')) {
foreach ($public_uploads as $public_type => $public_upload) {
for ($i = 0; $i < count($public_upload); $i++) {
$filename = $public_upload[$i];
try {
unlink($filename);
$public_delete_count++;
} catch (\Exception $e) {
Log::debug($e);
$this->error($e);
}
}
}
foreach ($private_uploads as $private_type => $private_upload) {
for ($i = 0; $i < count($private_upload); $i++) {
$filename = $private_upload[$i];
try {
unlink($filename);
$private_delete_count++;
} catch (\Exception $e) {
Log::debug($e);
$this->error($e);
}
}
}
$this->info($public_delete_count.' PUBLIC local files and '.$private_delete_count.' PRIVATE local files were deleted from your filesystem.');
}
}
}
}
}
@@ -1,52 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class NormalizeUserNames extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:normalize-names';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Normalizes weirdly formatted names as first-letter upercased';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$users = User::get();
$this->info($users->count().' users');
foreach ($users as $user) {
$user->first_name = ucwords(strtolower($user->first_name));
$user->last_name = ucwords(strtolower($user->last_name));
$user->email = strtolower($user->email);
$user->save();
}
}
}
@@ -1,158 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Helper\ProgressIndicator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* Class ObjectImportCommand
*/
class ObjectImportCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'snipeit:import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import Items from CSV';
/**
* The progress indicator instance.
*/
protected ProgressIndicator $progressIndicator;
/**
* Logger instance with configurable log path
*/
protected $logger;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); // 600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
$this->progressIndicator = new ProgressIndicator($this->output);
$filename = $this->argument('filename');
$class = ucfirst($this->option('item-type'));
$classString = "App\\Importer\\{$class}Importer";
$importer = new $classString($filename);
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
->setCreatedBy($this->option('user_id'))
->setUpdating($this->option('update'))
->setShouldNotify($this->option('send-welcome'))
->setUsernameFormat($this->option('username_format'));
$this->logger = Log::build([
'driver' => 'single',
'path' => $this->option('logfile'),
]);
$this->progressIndicator->start('======= Importing Items from '.$filename.' =========');
$importer->import();
$this->progressIndicator->finish('Import finished.');
}
public function errorCallback($item, $field, $error)
{
$this->output->write("\x0D\x1B[2K");
$this->warn('Error: Item: '.$item->name.' failed validation: '.json_encode($error));
}
public function progress($importedItemsCount)
{
$this->progressIndicator->advance();
}
/**
* Log a message to file, configurable by the --log-file parameter.
* If a warning message is passed, we'll spit it to the console as well.
*
* @author Daniel Melzter
*
* @since 3.0
*
* @param string $string
* @param string $level
*/
public function log($string, $level = 'info')
{
if ($level === 'warning') {
$this->logger->warning($string);
$this->comment($string);
} else {
$this->logger->Info($string);
if ($this->option('verbose')) {
$this->comment($string);
}
}
}
/**
* Get the console command arguments.
*
* @author Daniel Melzter
*
* @since 3.0
*
* @return array
*/
protected function getArguments()
{
return [
['filename', InputArgument::REQUIRED, 'File for the CSV import.'],
];
}
/**
* Get the console command options.
*
* @author Daniel Melzter
*
* @since 3.0
*
* @return array
*/
protected function getOptions()
{
return [
['email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null],
['username_format', null, InputOption::VALUE_REQUIRED, 'The format of the username that should be generated. Options are firstname.lastname, firstname, filastname, email', null],
['logfile', null, InputOption::VALUE_REQUIRED, 'The path to log output to. storage/logs/importer.log by default', storage_path('logs/importer.log')],
['item-type', null, InputOption::VALUE_REQUIRED, 'Item Type To import. Valid Options are Asset, Consumable, Accessory, License, or User', 'Asset'],
['web-importer', null, InputOption::VALUE_NONE, 'Internal: packages output for use with the web importer'],
['user_id', null, InputOption::VALUE_REQUIRED, 'ID of user creating items', 1],
['update', null, InputOption::VALUE_NONE, 'If a matching item is found, update item information'],
['send-welcome', null, InputOption::VALUE_NONE, 'Whether to send a welcome email to any new users that are created.'],
];
}
}
-93
View File
@@ -1,93 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomField;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class PaveIt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:pave {--force : Skip the interactive yes/no prompt for confirmation}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear the database tables, leaving all migrations, table structure, and the first user in place. (It is primarily a quick tool for developers.) If you want to destroy all tables as well, use php artisan db:wipe.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (! $this->option('force')) {
$confirmation = $this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE DATA IN YOUR DATABASE. \nThere is NO undo. This WILL destroy ALL of your data, \nINCLUDING ANY non-Snipe-IT tables you have in this database. \n****************************************************\n\nDo you wish to continue? No backsies! ");
if (! $confirmation) {
$this->error('ABORTING');
exit(-1);
}
}
// List all the tables in the database so we don't have to worry about missing some as the app grows
$tables = Schema::getTables();
$except_tables = [
'oauth_access_tokens',
'oauth_clients',
'oauth_personal_access_clients',
'migrations',
'settings',
'users',
'telescope_entries',
'telescope_entries_tags',
'telescope_monitoring',
];
// We only need to find out what these are so we can nuke these columns on the assets table.
$custom_fields = CustomField::get();
foreach ($custom_fields as $custom_field) {
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
if (Schema::hasColumn('assets', $custom_field->db_column)) {
Schema::table('assets', function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
}
}
foreach ($tables as $table_obj) {
$table = $table_obj['name'];
if (in_array($table, $except_tables)) {
$this->info($table.' is SKIPPED.');
} else {
\DB::statement('truncate '.$table);
$this->info($table.' is TRUNCATED.');
}
}
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
DB::statement('delete from oauth_clients WHERE id > 2');
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
}
}
-185
View File
@@ -1,185 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Location;
use App\Models\Manufacturer;
use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class Purge extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:purge {--force=false}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Purge all soft-deleted deleted records in the database. This will rewrite history for items that have been edited, or checked in or out. It will also rewrite history for users associated with deleted items.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$force = $this->option('force');
if (($this->confirm("\n****************************************************\nTHIS WILL PURGE ALL SOFT-DELETED ITEMS IN YOUR SYSTEM. \nThere is NO undo. This WILL permanently destroy \nALL of your deleted data. \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) || $force == 'true') {
/**
* Delete assets
*/
$assets = Asset::whereNotNull('deleted_at')->withTrashed()->get();
$assetcount = $assets->count();
$this->info($assets->count().' assets purged.');
$asset_assoc = 0;
$maintenances = 0;
foreach ($assets as $asset) {
$this->info('- Asset "'.$asset->display_name.'" deleted.');
$asset_assoc += $asset->assetlog()->count();
$asset->assetlog()->forceDelete();
$maintenances += $asset->maintenances()->count();
$asset->maintenances()->forceDelete();
$asset->forceDelete();
}
$this->info($asset_assoc.' corresponding log records purged.');
$this->info($maintenances.' corresponding maintenance records purged.');
$locations = Location::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($locations->count().' locations purged.');
foreach ($locations as $location) {
$this->info('- Location "'.$location->name.'" deleted.');
$location->forceDelete();
}
$accessories = Accessory::whereNotNull('deleted_at')->withTrashed()->get();
$accessory_assoc = 0;
$this->info($accessories->count().' accessories purged.');
foreach ($accessories as $accessory) {
$this->info('- Accessory "'.$accessory->name.'" deleted.');
$accessory_assoc += $accessory->assetlog()->count();
$accessory->assetlog()->forceDelete();
$accessory->forceDelete();
}
$this->info($accessory_assoc.' corresponding log records purged.');
$consumables = Consumable::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($consumables->count().' consumables purged.');
foreach ($consumables as $consumable) {
$this->info('- Consumable "'.$consumable->name.'" deleted.');
$consumable->assetlog()->forceDelete();
$consumable->forceDelete();
}
$components = Component::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($components->count().' components purged.');
foreach ($components as $component) {
$this->info('- Component "'.$component->name.'" deleted.');
$component->assetlog()->forceDelete();
$component->forceDelete();
}
$licenses = License::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($licenses->count().' licenses purged.');
foreach ($licenses as $license) {
$this->info('- License "'.$license->name.'" deleted.');
$license->assetlog()->forceDelete();
$license->licenseseats()->forceDelete();
$license->forceDelete();
}
$models = AssetModel::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($models->count().' asset models purged.');
foreach ($models as $model) {
$this->info('- Asset Model "'.$model->name.'" deleted.');
$model->forceDelete();
}
$categories = Category::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($categories->count().' categories purged.');
foreach ($categories as $category) {
$this->info('- Category "'.$category->name.'" deleted.');
$category->forceDelete();
}
$suppliers = Supplier::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($suppliers->count().' suppliers purged.');
foreach ($suppliers as $supplier) {
$this->info('- Supplier "'.$supplier->name.'" deleted.');
$supplier->forceDelete();
}
$users = User::whereNotNull('deleted_at')->where('show_in_list', '!=', '0')->withTrashed()->get();
$this->info($users->count().' users purged.');
$user_assoc = 0;
foreach ($users as $user) {
$rel_path = 'private_uploads/users';
$filenames = Actionlog::where('action_type', 'uploaded')
->where('item_id', $user->id)
->pluck('filename');
foreach ($filenames as $filename) {
try {
if (Storage::exists($rel_path.'/'.$filename)) {
Storage::delete($rel_path.'/'.$filename);
}
} catch (\Exception $e) {
Log::info('An error occurred while deleting files: '.$e->getMessage());
}
}
$this->info('- User "'.$user->username.'" deleted.');
$user_assoc += $user->userlog()->count();
$user->userlog()->forceDelete();
$user->forceDelete();
}
$this->info($user_assoc.' corresponding user log records purged.');
$manufacturers = Manufacturer::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($manufacturers->count().' manufacturers purged.');
foreach ($manufacturers as $manufacturer) {
$this->info('- Manufacturer "'.$manufacturer->name.'" deleted.');
$manufacturer->forceDelete();
}
$status_labels = Statuslabel::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($status_labels->count().' status labels purged.');
foreach ($status_labels as $status_label) {
$this->info('- Status Label "'.$status_label->name.'" deleted.');
$status_label->forceDelete();
}
} else {
$this->info('Action canceled. Nothing was purged.');
}
}
}
-155
View File
@@ -1,155 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class PurgeEulaPDFs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:purge-eula-pdfs
{--older-than-days= : The number of days we should delete before }
{--company-id= : Only purge acceptances for users in this company}
{--only-deleted-users : Only purge acceptances for deleted users, including soft-deleted or missing users}
{--force : Skip the interactive yes/no prompt for confirmation}
{--dryrun : Show the records that would be deleted but don\'t update the database or delete files from disk}
{--with-output : Display the results in a table in your console}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This purges signature files and EULAs from the system if they are older than the date passed with --older-than-days=.';
/**
* Execute the console command.
*/
public function handle()
{
$before = $this->option('older-than-days');
if (($before == '') || (! is_numeric($before))) {
return $this->error('ERROR: You must pass a valid number for --older-than-days (example: snipeit:purge-eula-pdfs --older-than-days=365.)');
}
$interval_date = Carbon::now()->subDays($before);
$signature_path = 'private_uploads/signatures/';
$eula_path = 'private_uploads/eula-pdfs/';
if (! Storage::exists($eula_path)) {
$this->fail('The storage directory "'.$eula_path.'" does not exist. No EULA files will be deleted.');
}
if (! Storage::exists($signature_path)) {
$this->fail('The storage directory "'.$signature_path.'" does not exist. No signature files will be deleted.');
}
if ($this->option('dryrun')) {
$this->info('This script is being run with the --dryrun option. No files or records will be deleted.');
}
$companyId = $this->option('company-id');
$query = CheckoutAcceptance::HasFiles()->where('updated_at', '<', $interval_date)
->with([
'assignedTo' => function ($query) {
$query->withTrashed();
},
]);
if ($this->option('only-deleted-users')) {
$query->where(function ($query) use ($companyId) {
$query->whereHas('assignedTo', function ($q) use ($companyId) {
$q->withTrashed()->whereNotNull('deleted_at');
if ($companyId) {
$q->where('company_id', $companyId);
}
});
$query->orWhereDoesntHave('assignedTo');
});
} else {
if ($companyId) {
$query->whereHas('assignedTo', function ($query) use ($companyId) {
$query->withTrashed()->where('company_id', $companyId);
});
}
}
$acceptances = $query->get();
if (! $this->option('force')) {
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE SIGNATURES AND EULA PDF FILES SINCE $interval_date. \nThere is NO undo! \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
}
}
if ($acceptances->count() == 0) {
return $this->warn('There are no item acceptances with signatures or EULA PDFs from before '.$interval_date);
}
$this->info(number_format($acceptances->count()).' EULA PDFs from before '.$interval_date.' will be purged');
if (! $this->option('with-output')) {
$this->info('Run this command with the --with-output option to see the full list in the console.');
} else {
$this->table(
[
trans('general.user'),
trans('general.type'),
trans('general.item'),
trans('general.category'),
trans('general.accepted_date'),
trans('general.declined_date'),
trans('general.signature'),
trans('general.filename'),
],
$acceptances->map(fn ($acceptance) => [
trans('general.user') => $acceptance->assignedTo->display_name,
trans('general.type') => $acceptance->display_checkoutable_type,
trans('general.item') => $acceptance->checkoutable_type::find($acceptance->checkoutable_id)->display_name,
trans('general.category') => $acceptance->checkoutable_category_name,
trans('general.accepted_date') => $acceptance->accepted_at,
trans('general.declined_date') => $acceptance->declined_at,
trans('general.signature') => $acceptance->signature_filename,
trans('general.filename') => $acceptance->stored_eula_file,
])
);
}
foreach ($acceptances as $acceptance) {
$signature_file = $signature_path.$acceptance->signature_filename;
$eula_file = $eula_path.$acceptance->stored_eula_file;
if (Storage::exists($signature_file)) {
if (! $this->option('dryrun')) {
Storage::delete($signature_file);
}
} else {
$this->error('The file "'.$signature_file.'" does not exist.');
}
if (Storage::exists($eula_file)) {
if (! $this->option('dryrun')) {
Storage::delete($eula_file);
}
} else {
$this->error('The file "'.$eula_file.'" does not exist.');
}
if (! $this->option('dryrun')) {
$acceptance->delete();
}
}
}
}
@@ -1,44 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class PurgeLoginAttempts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:purge-logins';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears the login_attempts table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE YOUR LOGIN ATTEMPT RECORDS. \nThere is NO undo! \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
\DB::statement('delete from login_attempts');
}
}
}
@@ -1,129 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomField;
use Illuminate\Console\Command;
class ReEncodeCustomFieldNames extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:regenerate-fieldnames';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This utility will regenerate the column names for custom fields. It should typically only be needed when a PHP upgrade changed the behavior of the unicode conversion between versions.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* All three of these things must match for the custom fields system to work as expected:
*
* - what the system thinks the output of $field->convertUnicodeDbSlug() is
* - the actual db_column name in the customfields table
* - the physical column name that was created on the assets table
*
* For some people who upgraded their version of PHP, the unicode converter now behaves
* differently in than it did when their custom fields were first created, specifically as it
* relates to handling slashes, ampersands, etc. This can result in the field names no longer
* matching up, as an older version of the PHP extension simply dropped slashes, etc, while the
* newer version of the PHP extension will convert them to underscores.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm('This will regenerate all of the custom field database fieldnames in your database. THIS WILL CHANGE YOUR SCHEMA AND SHOULD NOT BE DONE WITHOUT MAKING A BACKUP FIRST. Do you wish to continue?')) {
/** Get all of the custom fields */
$fields = CustomField::get();
$asset_columns = \DB::getSchemaBuilder()->getColumnListing('assets');
$custom_field_columns = [];
/** Loop through the columns on the assets table */
foreach ($asset_columns as $asset_column) {
/** Add ones that start with _snipeit_ to an array for handling */
if (strpos($asset_column, '_snipeit_') === 0) {
/**
* Get the ID of the custom field based on the fieldname.
* For example, in _snipeit_mac_address_1, we grab the 1 because we know
* that's the ID of the custom field that created the column.
* Then use that ID as the array key for use comparing the actual assets field name
* and the db_column value from the custom fields table.
*/
$last_part = substr(strrchr($asset_column, '_snipeit_'), 1);
$custom_field_columns[$last_part] = $asset_column;
}
}
foreach ($fields as $field) {
$this->info($field->name.' ('.$field->id.') column should be '.$field->convertUnicodeDbSlug());
/** The assets table has the column it should have, all is well */
if ($field->db_column == $field->convertUnicodeDbSlug() && \Schema::hasColumn('assets', $field->convertUnicodeDbSlug())) {
$this->info('-- ✓ This field exists on the assets table and the value for db_column matches in the custom_fields table.');
/**
* There is a mismatch between the fieldname on the assets table and
* what $field->convertUnicodeDbSlug() is *now* expecting.
*/
} else {
if ($field->db_column != $field->convertUnicodeDbSlug()) {
$this->error('-- ✘ Field mismatch: '.$field->name.' value should be '.$field->convertUnicodeDbSlug().' but is '.$field->db_column.' in the custom_fields table');
} else {
$this->error('-- ✘ Field mismatch: '.$field->name.' column should be '.$field->convertUnicodeDbSlug().' but is '.$custom_field_columns[$field->id].' on the assets table.');
}
/** Make sure the custom_field_columns array has the ID */
if (array_key_exists($field->id, $custom_field_columns)) {
/**
* Update the asset schema to the corrected fieldname that will be recognized by the
* system elsewhere that we use $field->convertUnicodeDbSlug()
*/
$this->info('-- ✓ Updating field from '.$field->db_column.' to '.$field->convertUnicodeDbSlug().' in the assets table');
\Schema::table('assets', function ($table) use ($custom_field_columns, $field) {
$table->renameColumn($custom_field_columns[$field->id], $field->convertUnicodeDbSlug());
});
$this->info('-- ✓ Updating field from '.$field->db_column.' to '.$field->convertUnicodeDbSlug().' in the custom fields table');
$field->db_column = $field->convertUnicodeDbSlug();
$field->save();
} else {
$this->warn('-- ✘ WARNING: There is no field on the assets table ending in '.$field->id.'. This may require more in-depth investigation and may mean the schema was altered manually.');
}
}
/** Update the db_column property in the custom fields table, just in case it doesn't match the other
* things.
*/
$field->db_column = $field->convertUnicodeDbSlug();
$field->save();
}
}
}
}
@@ -1,104 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\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) {
$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();
$start_tag++;
if ($bar) {
$bar->advance();
}
}
$settings->next_auto_tag_base = Asset::zerofill($start_tag, $settings->zerofill_count);
$settings->save();
$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);
}
}
}
}
}
@@ -1,60 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\AssetModel;
use Illuminate\Console\Command;
class RemoveExplicitEols extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:remove-explicit-eols {--model_name= : The name of the asset model to update (use "all" to update all models)}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes explicit EOLs on assets with selected model so they may inherit the asset model EOL';
/**
* Execute the console command.
*/
public function handle()
{
$startTime = microtime(true);
if ($this->option('model_name') == 'all') {
$assets = Asset::all();
$this->updateAssets($assets);
} else {
$assetModel = AssetModel::where('name', '=', $this->option('model_name'))->first();
if ($assetModel) {
$assets = Asset::where('model_id', '=', $assetModel->id)->get();
$this->updateAssets($assets);
} else {
$this->error('Asset model not found');
}
}
$endTime = microtime(true);
$executionTime = ($endTime - $startTime);
$this->info('Command executed in '.round($executionTime, 2).' seconds.');
}
private function updateAssets($assets)
{
foreach ($assets as $asset) {
$asset->eol_explicit = 0;
$asset->asset_eol_date = null;
$asset->save();
}
$this->info($assets->count().' Assets updated successfully');
}
}
@@ -1,56 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use Illuminate\Console\Command;
class RemoveInvalidUploadDeleteActionLogItems extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:remove-invalid-upload-delete-action-log-items';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Permanently remove invalid "upload deleted" action log items that have a null filename. This command can potentially result in deleted files being "resurrected" in the UI.';
/**
* Execute the console command.
*/
public function handle()
{
$invalidLogs = Actionlog::query()
->where('action_type', 'upload deleted')
->whereNull('filename')
->withTrashed()
->get();
$this->info("{$invalidLogs->count()} invalid log items found.");
if ($invalidLogs->count() === 0) {
return 0;
}
$this->table(['ID', 'Action Type', 'Item Type', 'Item ID', 'Created At', 'Deleted At'], $invalidLogs->map(fn ($log) => [
$log->id,
$log->action_type,
$log->item_type,
$log->item_id,
$log->created_at,
$log->deleted_at,
])->toArray());
if ($this->confirm("Do you wish to remove {$invalidLogs->count()} log items?")) {
$invalidLogs->each(fn ($log) => $log->forceDelete());
}
return 0;
}
}
@@ -1,92 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Console\Command;
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->login_note = 'Use `admin` / `password` to login to the demo.';
$settings->header_color = '#3c8dbc';
$settings->link_dark_color = '#5fa4cc';
$settings->link_light_color = '#296282;';
$settings->nav_link_color = '#FFFFFF';
$settings->label2_2d_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 2;
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 0;
$settings->label2_1d_type = 'C128';
$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-US';
$settings->version_footer = 'on';
$settings->support_footer = 'on';
$settings->saml_enabled = '0';
$settings->saml_sp_x509cert = null;
$settings->saml_idp_metadata = null;
$settings->saml_attr_mapping_username = null;
$settings->saml_forcelogin = '0';
$settings->saml_slo = null;
$settings->saml_custom_settings = null;
$settings->default_avatar = 'default.png';
$settings->save();
if ($user = User::where('username', '=', 'admin')->first()) {
$user->locale = 'en-US';
$user->enable_confetti = 1;
$user->enable_sounds = 1;
$user->save();
}
\Storage::disk('public')->put('snipe-logo.png', file_get_contents(public_path('img/demo/snipe-logo.png')));
\Storage::disk('public')->put('snipe-logo-lg.png', file_get_contents(public_path('img/demo/snipe-logo-lg.png')));
}
}
@@ -1,105 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\License;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
class RestoreDeletedUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:restore-users {--start_date=} {--end_date=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Restore users, and any associated assets and license checkouts.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$start_date = $this->option('start_date');
$end_date = $this->option('end_date');
$asset_totals = 0;
$license_totals = 0;
$user_count = 0;
if (($start_date == '') || ($end_date == '')) {
$this->info('ERROR: All fields are required.');
return false;
}
$users = User::whereBetween('deleted_at', [$start_date, $end_date])->withTrashed()->get();
$this->info('There are '.$users->count().' users deleted between '.$start_date.' and '.$end_date);
$this->warn('Making a backup!');
Artisan::call('backup:run');
foreach ($users as $user) {
$user_count++;
$user_logs = Actionlog::where('target_id', $user->id)->where('target_type', User::class)
->where('action_type', 'checkout')->with('item')->get();
$this->info($user_count.'. '.$user->username.' ('.$user->id.') was deleted at '.$user->deleted_at.' and has '.$user_logs->count().' checkouts associated.');
foreach ($user_logs as $user_log) {
$this->info(' * '.$user_log->item_type.': '.$user_log->item->name.' - item_id: '.$user_log->item_id);
if ($user_log->item_type == Asset::class) {
$asset_totals++;
DB::table('assets')
->where('id', $user_log->item_id)
->update(['assigned_to' => $user->id, 'assigned_type' => User::class]);
$this->info(' ** Asset '.$user_log->item->id.' ('.$user_log->item->asset_tag.') restored to user '.$user->id.'');
} elseif ($user_log->item_type == License::class) {
$license_totals++;
$avail_seat = DB::table('license_seats')->where('license_id', '=', $user_log->item->id)
->whereNull('assigned_to')->whereNull('asset_id')->whereBetween('updated_at', [$start_date, $end_date])->first();
if ($avail_seat) {
$this->info(' ** Allocating seat '.$avail_seat->id.' for this License');
DB::table('license_seats')
->where('id', $avail_seat->id)
->update(['assigned_to' => $user->id]);
} else {
$this->warn('ERROR: No available seats for '.$user_log->item->name);
}
}
}
$this->warn('Restoring user '.$user->username.'!');
$user->restore();
}
$this->info($asset_totals.' assets affected');
$this->info($license_totals.' licenses affected');
}
}
-589
View File
@@ -1,589 +0,0 @@
<?php
namespace App\Console\Commands;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use ZipArchive;
class SQLStreamer
{
private $input;
private $output;
// embed the prefix here?
public ?string $prefix;
private bool $reading_beginning_of_line = true;
public static $buffer_size = 1024 * 1024; // use a 1MB buffer, ought to work fine for most cases?
public array $tablenames = [];
private bool $should_guess = false;
private bool $statement_is_permitted = false;
public function __construct($input, $output, ?string $prefix = null)
{
$this->input = $input;
$this->output = $output;
$this->prefix = $prefix;
}
public function parse_sql(string $line): string
{
// take into account the 'start of line or not' setting as an instance variable?
// 'continuation' lines for a permitted statement are PERMITTED.
// remove *only* line-feeds & carriage-returns; helpful for regexes against lines from
// Windows dumps
$line = trim($line, "\r\n");
if ($this->statement_is_permitted && $line[0] === ' ') {
return $line."\n"; // re-add the newline
}
$table_regex = '`?([a-zA-Z0-9_]+)`?';
$allowed_statements = [
"/^(DROP TABLE (?:IF EXISTS )?)`$table_regex(.*)$/" => false,
"/^(CREATE TABLE )$table_regex(.*)$/" => true, // sets up 'continuation'
"/^(LOCK TABLES )$table_regex(.*)$/" => false,
"/^(INSERT INTO )$table_regex(.*)$/" => false,
'/^UNLOCK TABLES/' => false,
// "/^\\) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/" => false, // FIXME not sure what to do here?
'/^\\)[a-zA-Z0-9_= ]*;$/' => false,
// ^^^^^^ that bit should *exit* the 'permitted' block
'/^\\(.*\\)[,;]$/' => false, // older MySQL dump style with one set of values per line
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
"<^/\*![0-9]{5} SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, // using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
"<^/\*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' \*/;$>" => false, // same, now handle zero-values
];
foreach ($allowed_statements as $statement => $statechange) {
// $this->info("Checking regex: $statement...\n");
$matches = [];
if (preg_match($statement, $line, $matches)) {
$this->statement_is_permitted = $statechange;
// matches are: 1 => first part of the statement, 2 => tablename, 3 => rest of statement
// (with of course 0 being "the whole match")
if (@$matches[2]) {
// print "Found a tablename! It's: ".$matches[2]."\n";
if ($this->should_guess) {
@$this->tablenames[$matches[2]] += 1;
continue; // oh? FIXME
} else {
$cleaned_tablename = \DB::getTablePrefix().preg_replace('/^'.$this->prefix.'/', '', $matches[2]);
$line = preg_replace($statement, '$1`'.$cleaned_tablename.'`$3', $line);
}
} else {
// no explicit tablename in this one, leave the line alone
}
// how do we *replace* the tablename?
// print "RETURNING LINE: $line";
return $line."\n"; // re-add newline
}
}
// all that is not allowed is denied.
return '';
}
// this is used in exactly *TWO* places, and in both cases should return a prefix I think?
// first - if you do the --sanitize-only one (which is mostly for testing/development)
// next - when you run *without* a guessed prefix, this is run first to figure out the prefix
// I think we have to *duplicate* the call to be able to run it again?
public static function guess_prefix($input): string
{
$parser = new self($input, null);
$parser->should_guess = true;
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; // TODO - move to statics?
// can't use 'users' because the 'accessories_checkout' table?
// can't use 'assets' because 'ver1_components_assets'
foreach ($check_tables as $check_table => $_ignore) {
foreach ($parser->tablenames as $tablename => $_count) {
// print "Comparing $tablename to $check_table\n";
if (str_ends_with($tablename, $check_table)) {
// print "Found one!\n";
$check_tables[$check_table] = substr($tablename, 0, -strlen($check_table));
}
}
}
$guessed_prefix = null;
foreach ($check_tables as $clean_table => $prefix_guess) {
if (is_null($prefix_guess)) {
echo "Couldn't find table $clean_table\n";
exit();
}
if (is_null($guessed_prefix)) {
$guessed_prefix = $prefix_guess;
} else {
if ($guessed_prefix != $prefix_guess) {
echo "Prefix mismatch! Had guessed $guessed_prefix but got $prefix_guess\n";
exit();
}
}
}
return $guessed_prefix;
}
public function line_aware_piping(): int
{
$bytes_read = 0;
if (! $this->input) {
throw new \Exception('No Input available for line_aware_piping');
}
while (($buffer = fgets($this->input, SQLStreamer::$buffer_size)) !== false) {
$bytes_read += strlen($buffer);
if ($this->reading_beginning_of_line) {
// Log::debug("Buffer is: '$buffer'");
$cleaned_buffer = $this->parse_sql($buffer);
if ($this->output) {
$bytes_written = fwrite($this->output, $cleaned_buffer);
if ($bytes_written === false) {
throw new \Exception('Unable to write to pipe');
}
}
}
// if we got a newline at the end of this, then the _next_ read is the beginning of a line
if ($buffer[strlen($buffer) - 1] === "\n") {
$this->reading_beginning_of_line = true;
} else {
$this->reading_beginning_of_line = false;
}
}
return $bytes_read;
}
}
class RestoreFromBackup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
// FIXME - , stripping prefixes and nonstandard SQL statements. Without --prefix, guess and return the correct prefix to strip
protected $signature = 'snipeit:restore
{--force : Skip the danger prompt; assuming you enter "y"}
{filename : The zip file to be migrated}
{--no-progress : Don\'t show a progress bar}
{--sanitize-guess-prefix : Guess and output the table-prefix needed to "sanitize" the SQL}
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}
{--sql-stdout-only : ONLY "Sanitize" the SQL and print it to stdout - useful for debugging - probably requires --sanitize-with-prefix= }';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Restore from a previously created Snipe-IT backup file';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$dir = getcwd();
if ($dir != base_path()) { // usually only the case when running via webserver, not via command-line
Log::debug("Current working directory is: $dir, changing directory to: ".base_path());
chdir(base_path()); // TODO - is this *safe* to change on a running script?!
}
//
$filename = $this->argument('filename');
if (! $filename) {
return $this->error('Missing required filename');
}
if (! $this->option('force') && ! $this->option('sanitize-guess-prefix') && ! $this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
return $this->error('Data loss not confirmed');
}
if (config('database.default') != 'mysql') {
return $this->error('DB_CONNECTION must be MySQL in order to perform a restore. Detected: '.config('database.default'));
}
$za = new ZipArchive;
$errcode = $za->open($filename/* , ZipArchive::RDONLY */); // that constant only exists in PHP 7.4 and higher
if ($errcode !== true) {
$errors = [
ZipArchive::ER_EXISTS => 'File already exists.',
ZipArchive::ER_INCONS => 'Zip archive inconsistent.',
ZipArchive::ER_INVAL => 'Invalid argument.',
ZipArchive::ER_MEMORY => 'Malloc failure.',
ZipArchive::ER_NOENT => 'No such file ('.$filename.') in directory '.$dir.'.',
ZipArchive::ER_NOZIP => 'Not a zip archive.',
ZipArchive::ER_OPEN => "Can't open file.",
ZipArchive::ER_READ => 'Read error.',
ZipArchive::ER_SEEK => 'Seek error.',
];
return $this->error('Could not access file: '.$filename.' - '.array_key_exists($errcode, $errors) ? $errors[$errcode] : " Unknown reason: $errcode");
}
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels' => 'storage/private_uploads/models', // this was changed from assetmodels => models Aug 10 2025
'storage/private_uploads/asset_maintenances' => 'storage/private_uploads/maintenances', // this was changed from asset_maintenances => maintenances Aug 10 2025
'storage/private_uploads/maintenances', // but let 'maintenances' take precedence
'storage/private_uploads/models', // and let 'models' take precedence
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
'storage/private_uploads/consumables',
'storage/private_uploads/eula-pdfs',
'storage/private_uploads/imports',
'storage/private_uploads/locations',
'storage/private_uploads/licenses',
'storage/private_uploads/signatures',
'storage/private_uploads/users',
];
$private_files = [
'storage/oauth-private.key',
'storage/oauth-public.key',
];
$public_dirs = [
'public/uploads/accessories',
// 'public/uploads/assetmodels' => 'public/uploads/models', //according to git, this was _never_ a thing... (see below)
'public/uploads/maintenances',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
'public/uploads/categories',
'public/uploads/companies',
'public/uploads/components',
'public/uploads/consumables',
'public/uploads/departments',
'public/uploads/locations',
'public/uploads/manufacturers',
'public/uploads/models', // ...it's been this way for 9 years (as of late 2025)
'public/uploads/suppliers',
];
$public_files = [
'public/uploads/logo.*',
'public/uploads/setting-email_logo*',
'public/uploads/setting-label_logo*',
'public/uploads/setting-logo*',
'public/uploads/favicon.*',
'public/uploads/favicon-uploaded.*',
];
$sqlfiles = [];
$sqlfile_indices = [];
$interesting_files = [];
$boring_files = [];
$unsafe_files = [];
$good_extensions = config('filesystems.allowed_upload_extensions_array');
$private_extensions = array_merge($good_extensions, ['csv', 'key']); // add csv, and 'key'
$public_extensions = array_diff($good_extensions, ['xml']); // remove xml
$sanitizer = new Sanitizer;
/**
* TODO: I _hate_ the "continue 3" thing we keep doing here
* I think a better approach might be to have the "each file" stuff be in a method on this class, and the
* boring_files and interesting_files be properties on it that we fill out. Then, in that method, we could
* just do a 'return' once the file is actually handled (yay or nay). We could also start to break out some of
* the _other_ things that we do into their own methods too? But I don't care about that as much.
*/
for ($i = 0; $i < $za->numFiles; $i++) {
$stat_results = $za->statIndex($i);
// echo "index: $i\n";
// print_r($stat_results);
$raw_path = $stat_results['name'];
if (strpos($raw_path, '\\') !== false) { // found a backslash, swap it to forward-slash
$raw_path = strtr($raw_path, '\\', '/');
// print "Translating file: ".$stat_results['name']." to: ".$raw_path."\n";
}
// skip macOS resource fork files (?!?!?!)
if (strpos($raw_path, '__MACOSX') !== false && strpos($raw_path, '._') !== false) {
// print "SKIPPING macOS Resource fork file: $raw_path\n";
// $boring_files[] = $raw_path; //stop adding this to the boring files list; it's just confusing
continue;
}
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
Log::debug('Found a sql file!');
$sqlfiles[] = $raw_path;
$sqlfile_indices[] = $i;
continue;
}
if ($raw_path[-1] == '/') {
// last character is '/' - this is a directory, and we don't need it, and we don't need to warn about it
continue;
}
if (in_array(basename($raw_path), ['.gitkeep', '.gitignore', '.DS_Store'])) {
// skip these boring files silently without reporting on them; they're stupid
continue;
}
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
foreach (['public' => $public_dirs, 'private' => $private_dirs] as $purpose => $dirs) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($dirs as $dir => $destdir) {
if (is_int($dir)) {
$dir = $destdir;
}
$last_pos = strrpos($raw_path, $dir.'/');
if ($last_pos !== false) {
// print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
// print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
// the CSV bit, below, is because we store CSV files as "blahcsv" - without an extension
if (! in_array($extension, $allowed_extensions) && ! ($dir == 'storage/private_uploads/imports' && substr($raw_path, -3) == 'csv' && $extension == '')) {
$unsafe_files[] = $raw_path;
Log::debug($raw_path.' from directory '.$dir.' is being skipped');
} else {
if ($dir != $destdir) {
Log::debug("Getting ready to save file $raw_path to new directory $destdir");
}
$interesting_files[$raw_path] = ['dest' => $destdir, 'index' => $i];
}
continue 3;
}
}
}
foreach (['public' => $public_files, 'private' => $private_files] as $purpose => $files) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($files as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
$file = substr($file, 0, -1); // trim last character (which should be the wildcard)
}
$last_pos = strrpos($raw_path, $file); // no trailing slash!
if ($last_pos !== false) {
if (! in_array($extension, $allowed_extensions)) {
// gathering potentially unsafe files here to return at exit
$unsafe_files[] = $raw_path;
Log::debug('Potentially unsafe file '.$raw_path.' is being skipped');
$boring_files[] = $raw_path;
continue 3;
}
// print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
// no wildcards found in $file, process 'normally'
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { // again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
continue 3;
}
}
}
}
$boring_files[] = $raw_path; // if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
} // end of pre-processing the ZIP file for-loop
// print_r($interesting_files);exit(-1);
if (count($sqlfiles) != 1) {
return $this->error('There should be exactly *one* sql backup file found, found: '.(count($sqlfiles) == 0 ? 'None' : implode(', ', $sqlfiles)));
}
if (strpos($sqlfiles[0], 'db-dumps') === false) {
// return $this->error("SQL backup file is missing 'db-dumps' component of full pathname: ".$sqlfiles[0]);
// older Snipe-IT installs don't have the db-dumps subdirectory component
}
$sql_stat = $za->statIndex($sqlfile_indices[0]);
// $this->info("SQL Stat is: ".print_r($sql_stat,true));
$sql_contents = $za->getStream($sql_stat['name']); // maybe copy *THIS* thing?
// OKAY, now that we *found* the sql file if we're doing just the guess-prefix thing, we can do that *HERE* I think?
if ($this->option('sanitize-guess-prefix')) {
$prefix = SQLStreamer::guess_prefix($sql_contents);
$this->line($prefix);
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
}
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness
if ($this->option('sql-stdout-only')) {
$sql_importer = new SQLStreamer($sql_contents, STDOUT, $this->option('sanitize-with-prefix'));
$bytes_read = $sql_importer->line_aware_piping();
return $this->warn("$bytes_read total bytes read");
// TODO - it'd be nice to dump this message to STDERR so that STDOUT is just pure SQL,
// which would be good for redirecting to a file, and not having to trim the last line off of it
}
// how to invoke the restore?
$pipes = [];
$env_vars = getenv();
$env_vars['MYSQL_PWD'] = config('database.connections.mysql.password');
// TODO notes: we are stealing the dump_binary_path (which *probably* also has your copy of the mysql binary in it. But it might not, so we might need to extend this)
// we unilaterally prepend a slash to the `mysql` command. This might mean your path could look like /blah/blah/blah//mysql - which should be fine. But maybe in some environments it isn't?
$mysql_binary = config('database.connections.mysql.dump.dump_binary_path').\DIRECTORY_SEPARATOR.'mysql'.(\DIRECTORY_SEPARATOR == '\\' ? '.exe' : '');
if (! file_exists($mysql_binary)) {
return $this->error("mysql tool at: '$mysql_binary' does not exist, cannot restore. Please edit DB_DUMP_PATH in your .env to point to a directory that contains the mysqldump and mysql binary");
}
$proc_results = proc_open("$mysql_binary -h " .
escapeshellarg(config('database.connections.mysql.host')) .
' -u ' . escapeshellarg(config('database.connections.mysql.username')) . ' ' .
' -P ' . escapeshellarg(config('database.connections.mysql.port')) . ' ' .
escapeshellarg(config('database.connections.mysql.database')), // yanked -p since we pass via ENV
[0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']],
$pipes,
null,
$env_vars); // this is not super-duper awesome-secure, but definitely more secure than showing it on the CLI, or dropping temporary files with passwords in them.
if ($proc_results === false) {
return $this->error('Unable to invoke mysql via CLI');
}
// I'm not sure about these?
stream_set_blocking($pipes[1], false); // use non-blocking reads for stdout
stream_set_blocking($pipes[2], false); // use non-blocking reads for stderr
// $this->info("Stdout says? ".fgets($pipes[1])); //FIXME: I think we might need to set non-blocking mode to use this properly?
// $this->info("Stderr says? ".fgets($pipes[2])); //FIXME: ditto, same.
// should we read stdout?
// fwrite($pipes[0],config("database.connections.mysql.password")."\n"); //this doesn't work :(
// $sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
// FIXME - this feels like it wants to go somewhere else?
// and it doesn't seem 'right' - if you can't get a stream to the .sql file,
// why do we care what's happening with pipes and stdout and stderr?!
if ($sql_contents === false) {
$stdout = fgets($pipes[1]);
$this->info($stdout);
$stderr = fgets($pipes[2]);
$this->info($stderr);
return false;
}
try {
if ($this->option('sanitize-with-prefix') === null) {
// "Legacy" direct-piping
$bytes_read = 0;
while (($buffer = fgets($sql_contents, SQLStreamer::$buffer_size)) !== false) {
$bytes_read += strlen($buffer);
// Log::debug("Buffer is: '$buffer'");
$bytes_written = fwrite($pipes[0], $buffer);
if ($bytes_written === false) {
throw new Exception('Unable to write to pipe');
}
}
} else {
$sql_importer = new SQLStreamer($sql_contents, $pipes[0], $this->option('sanitize-with-prefix'));
$bytes_read = $sql_importer->line_aware_piping();
}
} catch (\Exception $e) {
Log::error('Error during restore!!!! '.$e->getMessage());
// FIXME - put these back and/or put them in the right places?!
$err_out = fgets($pipes[1]);
$err_err = fgets($pipes[2]);
Log::error('Error OUTPUT: '.$err_out);
$this->info($err_out);
Log::error('Error ERROR : '.$err_err);
$this->error($err_err);
throw $e;
}
if (! feof($sql_contents) || $bytes_read == 0) {
return $this->error('Not at end of file for sql file, or zero bytes read. aborting!');
}
fclose($pipes[0]);
fclose($sql_contents);
$this->line(stream_get_contents($pipes[1]));
fclose($pipes[1]);
$this->error(stream_get_contents($pipes[2]));
fclose($pipes[2]);
// wait, have to do fclose() on all pipes first?
$close_results = proc_close($proc_results);
if ($close_results != 0) {
return $this->error('There may have been a problem with the database import: Error number '.$close_results);
}
// and now copy the files over too (right?)
// FIXME - we don't prune the filesystem space yet!!!!
if ($this->option('no-progress')) {
$bar = null;
} else {
$bar = $this->output->createProgressBar(count($interesting_files));
}
foreach ($interesting_files as $pretty_file_name => $file_details) {
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
$migrated_file_name = $file_details['dest'].'/'.basename($pretty_file_name);
if (strcasecmp(substr($pretty_file_name, -4), '.svg') === 0) {
$svg_contents = $za->getFromIndex($file_details['index']);
$cleaned_svg = $sanitizer->sanitize($svg_contents);
file_put_contents($migrated_file_name, $cleaned_svg);
} else {
$fp = $za->getStream($ugly_file_name);
// $this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (! is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); // 0755 is what Laravel uses, so we do that
}
$migrated_file = fopen($migrated_file_name, 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
}
fclose($migrated_file);
fclose($fp);
// $this->info("Wrote $ugly_file_name to $pretty_file_name");
}
if ($bar) {
$bar->advance();
}
}
if ($bar) {
$bar->finish();
$this->line('');
} else {
$this->info(count($interesting_files).' files were succesfully transferred');
}
if (count($unsafe_files) > 0) {
foreach ($unsafe_files as $unsafe_file) {
$this->warn('Potentially unsafe file '.$unsafe_file.' was skipped');
}
}
foreach ($boring_files as $boring_file) {
$this->warn($boring_file.' was skipped.');
}
}
}
-159
View File
@@ -1,159 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Encryption\Encrypter;
use Illuminate\Support\Facades\Artisan;
class RotateAppKey extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:rotate-key
{previous_key? : The previous key to rotate from}
{--emergency : Emergency mode - rotate from .env APP_KEY to newly-generated one, modifying .env}
{--force : Skip interactive confirmation}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rotates APP_KEY to a new value, optionally taking the previous key as an argument';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// make sure they specify only exactly one of --emergency, or a filename. Not neither, and not both.
if ((! $this->option('emergency') && ! $this->argument('previous_key')) || ($this->option('emergency') && $this->argument('previous_key'))) {
$this->error('Specify only one of --emergency, or an app key value, in order to rotate keys');
return 1;
}
if ($this->option('emergency')) {
$msg = "\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ";
} else {
$msg = "\n****************************************************\nTHIS WILL DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND RE-ENCRYPT THEM WITH YOUR\nAPP_KEY.\n\nThere is NO undo. \n\nMake SURE you have a database backup BEFORE running this command. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup? ";
}
if ($this->option('force') || $this->confirm($msg)) {
// Get the existing app_key and ciphers
// We put them in a variable since we clear the cache partway through here.
if ($this->option('emergency')) {
$old_app_key = config('app.key');
$cipher = config('app.cipher');
// Generate a new one
Artisan::call('key:generate', ['--show' => true]);
$new_app_key = trim(Artisan::output());
// Clear the config cache
Artisan::call('config:clear');
// Write the new app key to the .env file
$this->writeNewEnvironmentFileWith($new_app_key);
} elseif ($this->argument('previous_key')) {
$old_app_key = $this->argument('previous_key');
$cipher = config('app.cipher'); // just a guess?
$new_app_key = config('app.key');
}
$this->warn('Your app cipher is: '.$cipher);
$this->warn('Your old APP_KEY is: '.$old_app_key);
$this->warn('Your new APP_KEY is: '.$new_app_key);
// Manually create an old encrypter instance using the old app key
// and also create a new encrypter instance so we can re-crypt the field
// using the newly generated app key
$oldEncrypter = new Encrypter(base64_decode(substr($old_app_key, 7)), $cipher);
$newEncrypter = new Encrypter(base64_decode(substr($new_app_key, 7)), $cipher);
$fields = CustomField::where('field_encrypted', '1')->get();
foreach ($fields as $field) {
$assets = Asset::whereNotNull($field->db_column)->get();
foreach ($assets as $asset) {
try {
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
$this->line('DECRYPTED: '.$field->db_column);
} catch (DecryptException $e) {
$this->line('Could not decrypt '.$field->db_column.' using "old key" - skipping...');
continue;
} catch (\Exception $e) {
$this->error('Error decrypting '.$field->db_column.', reason: '.$e->getMessage().'. Aborting key rotation');
throw $e;
}
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
$this->line('ENCRYPTED: '.$field->db_column);
$asset->save();
}
}
// Handle the LDAP password if one is provided
$setting = Setting::first();
if ($setting->ldap_pword != '') {
try {
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
$setting->save();
$this->warn('LDAP password has been re-encrypted.');
} catch (DecryptException $e) {
$this->warn('Unable to decrypt old LDAP password; skipping');
}
}
} else {
$this->info('This operation has been canceled. No changes have been made.');
}
}
/**
* Write a new environment file with the given key.
*
* @param string $key
* @return void
*/
protected function writeNewEnvironmentFileWith($key)
{
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$this->keyReplacementPattern(),
'APP_KEY="'.$key.'"',
file_get_contents($this->laravel->environmentFilePath())
));
}
/**
* Get a regex pattern that will match env APP_KEY with any random key.
*
* @return string
*/
protected function keyReplacementPattern()
{
$escaped = '="?'.preg_quote($this->laravel['config']['app.key'], '/').'"?';
return "/^APP_KEY{$escaped}/m";
}
}
@@ -1,45 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\SamlNonce;
use Illuminate\Console\Command;
class SamlClearExpiredNonces extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'saml:clear_expired_nonces';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears out expired SAML assertions from the saml_nonces table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
SamlNonce::where('not_valid_after', '<=', now())->delete();
return 0;
}
}
@@ -1,123 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Mail\UnacceptedAssetReminderMail;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Facades\Mail;
class SendAcceptanceReminder extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:acceptance-reminder';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This will resend users with unaccepted items a reminder to accept or decline them.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$pending = CheckoutAcceptance::query()
->with([
'checkoutable' => function (MorphTo $morph) {
$morph->morphWith([
Asset::class => ['model.category', 'assignedTo', 'adminuser', 'company', 'checkouts'],
Accessory::class => ['category', 'company', 'checkouts'],
LicenseSeat::class => ['user', 'license', 'checkouts'],
Component::class => ['assignedTo', 'company', 'checkouts'],
Consumable::class => ['company', 'checkouts'],
]);
},
'assignedTo',
])
->whereHasMorph(
'checkoutable',
[Asset::class, Accessory::class, LicenseSeat::class, Component::class, Consumable::class],
fn ($q) => $q->whereNull('accepted_at')
->whereNull('declined_at')
)
->pending()
->get();
$count = 0;
$unacceptedAssetGroups = $pending
->map(function ($acceptance) {
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
})
->groupBy(function ($item) {
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
});
$no_email_list = [];
foreach ($unacceptedAssetGroups as $unacceptedAssetGroup) {
// The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user.
// Collapsing and flattening the collection doesn't work above.
$acceptance = $unacceptedAssetGroup[0]['acceptance'];
$locale = $acceptance->assignedTo?->locale;
$email = $acceptance->assignedTo?->email;
if (! $email) {
$no_email_list[] = [
'id' => $acceptance->assignedTo?->id,
'name' => $acceptance->assignedTo?->display_name,
];
} else {
$count++;
}
$item_count = $unacceptedAssetGroup->count();
if ($locale && $email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale));
} elseif ($email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count)));
}
}
$this->info($count.' users notified.');
$headers = ['ID', 'Name'];
$rows = [];
foreach ($no_email_list as $user) {
$rows[] = [$user['id'], $user['name']];
}
if (! empty($rows)) {
$this->info('The following users do not have an email address:');
$this->table($headers, $rows);
}
return 0;
}
}
@@ -1,54 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Console\Command;
class SendCurrentInventoryToUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:user-inventory';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This will send users a report of all of the items currently checked out to them.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$users = User::whereNull('deleted_at')->whereNotNull('email')->with('assets', 'accessories', 'licenses')->get();
$count = 0;
foreach ($users as $user) {
if (($user->assets->count() > 0) || ($user->accessories->count() > 0) || ($user->licenses->count() > 0) || ($user->consumables->count() > 0)) {
$count++;
$user->notify((new CurrentInventory($user)));
}
}
$this->info($count.' users notified.');
}
}
@@ -1,102 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\ExpectedCheckinAdminNotification;
use App\Notifications\ExpectedCheckinNotification;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;
class SendExpectedCheckinAlerts extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'snipeit:expected-checkin {--with-output : Display the results in a table in your console in addition to sending the email}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Check for overdue or upcoming expected checkins.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$settings = Setting::getSettings();
$interval = $settings->due_checkin_days ?? 0;
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);
$count = 0;
if (! $this->option('with-output')) {
$this->info('Run this command with the --with-output option to see the full list in the console.');
}
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForCheckin($settings)->orderBy('assets.expected_checkin', 'desc')->get();
$this->info($assets->count().' assets must be checked on or before '.Helper::getFormattedDateObject($interval_date, 'date', false));
foreach ($assets as $asset) {
if ($asset->assignedTo && (isset($asset->assignedTo->email)) && ($asset->assignedTo->email != '') && $asset->checkedOutToUser()) {
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
$count++;
}
}
if ($this->option('with-output')) {
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
$this->table(
[
trans('general.id'),
trans('admin/hardware/form.tag'),
trans('admin/hardware/form.model'),
trans('general.model_no'),
trans('general.purchase_date'),
trans('admin/hardware/form.expected_checkin'),
],
$assets->map(fn ($assets) => [
trans('general.id') => $assets->id,
trans('admin/hardware/form.tag') => $assets->asset_tag,
trans('admin/hardware/form.model') => $assets->model->name,
trans('general.model_no') => $assets->model->model_number,
trans('general.purchase_date') => $assets->purchase_date_formatted,
trans('admin/hardware/form.eol_date') => $assets->expected_checkin_formattedDate ? $assets->expected_checkin_formattedDate.' ('.$assets->expected_checkin_diff_for_humans.')' : '',
])
);
}
}
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
return new AlertRecipient($item);
});
Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
}
$this->info('Sent checkin reminders to to '.$count.' users.');
}
}
@@ -1,129 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Mail\ExpiringAssetsMail;
use App\Mail\ExpiringLicenseMail;
use App\Models\Asset;
use App\Models\License;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendExpirationAlerts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:expiring-alerts {--expired-licenses}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Check for expiring warrantees and service agreements, and sends out an alert email.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$settings = Setting::getSettings();
$alert_interval = $settings->alert_interval;
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))
->map(fn ($item) => trim($item)) // Trim each email
->filter(fn ($item) => ! empty($item))
->all();
// Expiring Assets
$assets = Asset::getExpiringWarrantyOrEol($alert_interval);
$assets->load(['assignedTo', 'supplier']);
if ($assets->count() > 0) {
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
$this->table(
[
trans('general.id'),
trans('admin/hardware/form.tag'),
trans('admin/hardware/form.model'),
trans('general.model_no'),
trans('general.purchase_date'),
trans('admin/hardware/form.eol_rate'),
trans('admin/hardware/form.eol_date'),
trans('admin/hardware/form.warranty_expires'),
],
$assets->map(fn ($item) => [
trans('general.id') => $item->id,
trans('admin/hardware/form.tag') => $item->asset_tag,
trans('admin/hardware/form.model') => $item->model->name,
trans('general.model_no') => $item->model->model_number,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/hardware/form.eol_rate') => $item->model->eol,
trans('admin/hardware/form.eol_date') => $item->eol_date ? $item->eol_formatted_date.' ('.$item->eol_diff_for_humans.')' : '',
trans('admin/hardware/form.warranty_expires') => $item->warranty_expires ? $item->warranty_expires_formatted_date.' ('.$item->warranty_expires_diff_for_humans.')' : '',
])
);
}
// Expiring licenses
$licenses = License::query()->ExpiringLicenses($alert_interval, $this->option('expired-licenses'))
->with('manufacturer', 'category')
->orderBy('expiration_date', 'ASC')
->orderBy('termination_date', 'ASC')
->get();
if ($licenses->count() > 0) {
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $alert_interval));
$this->table(
[
trans('general.id'),
trans('general.name'),
trans('general.purchase_date'),
trans('admin/licenses/form.expiration'),
trans('mail.expires'),
trans('admin/licenses/form.termination_date'),
trans('mail.terminates')],
$licenses->map(fn ($item) => [
trans('general.id') => $item->id,
trans('general.name') => $item->name,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
trans('mail.expires') => $item->expires_formatted_date ? $item->expires_diff_for_humans : '',
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
trans('mail.terminates') => $item->terminates_diff_for_humans,
])
);
}
// Send a message even if the count is 0
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $alert_interval]));
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $alert_interval]));
} else {
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');
}
}
}
}
@@ -1,67 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Helpers\Helper;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\InventoryAlert;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;
class SendInventoryAlerts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:inventory-alerts';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command checks for low inventory, and sends out an alert email.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$settings = Setting::getSettings();
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
$items = Helper::checkLowInventory();
if (($items) && (count($items) > 0)) {
$this->info(trans_choice('mail.low_inventory_alert', count($items)));
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new AlertRecipient($item);
});
Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
} else {
$this->info('No low inventory items found. No mail sent.');
}
} else {
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');
}
}
}
}

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