Compare commits

...

148 Commits

Author SHA1 Message Date
snipe
93014d7f34 Better handle backups in S3
Signed-off-by: snipe <snipe@snipe.net>
2025-03-04 14:58:32 +00:00
snipe
759e3794df Small upgrader UI fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-03-04 14:57:28 +00:00
snipe
c50b14763f Merge pull request #16403 from snipe/#16402_each_localization
Fixed #16402 - localize "each" string in components tab on asset view
2025-03-04 13:35:08 +00:00
snipe
369a68fe57 Fixed #16402 - Localize “each” in string
Signed-off-by: snipe <snipe@snipe.net>
2025-03-04 13:33:04 +00:00
snipe
83855d44d0 Merge pull request #16251 from Godmartinz/Audit_Checkin_warning_fix
refactors audit notification to mail, adds test, adds  alerts check to scheduler
2025-03-04 12:51:42 +00:00
snipe
6f847294ed Merge pull request #15911 from Fiala06/patch-1
Fixed duplicate entries preventing LDAP sync from continuing
2025-03-04 12:49:54 +00:00
snipe
d556d1c6e7 Merge pull request #16150 from Godmartinz/add-translations-to-settings
Adds Translation strings to General and Branding Settings
2025-03-04 12:48:21 +00:00
snipe
3bb94e98f0 Merge pull request #16398 from marcusmoore/bug/sc-28535
Avoid using authenticated user's email address in email partial
2025-03-04 12:47:10 +00:00
Marcus Moore
8f5f6f3502 Avoid using authenticated user's email address in email partial 2025-03-03 16:28:08 -08:00
snipe
b3792bfa00 Merge pull request #16396 from marcusmoore/chore/migrate-checkbox-helpers-pt10
Replace calls to Form::checkbox pt10
2025-03-03 20:52:54 +00:00
Marcus Moore
40f7257723 Replace call to Form::checkbox 2025-03-03 12:44:50 -08:00
snipe
8486256142 Merge pull request #16381 from marcusmoore/chore/migrate-checkbox-helpers-pt9
Replace calls to Form::checkbox pt9
2025-03-03 20:28:21 +00:00
snipe
7f36750e33 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2025-03-03 19:54:14 +00:00
snipe
de046db106 Add @azmcnutt as a contributor 2025-03-03 19:53:30 +00:00
snipe
eb1d27a5bc Merge pull request #16379 from azmcnutt/feature/settings_ldap_invert_active_flag
Feature/settings ldap invert active flag
2025-03-03 19:52:57 +00:00
Godfrey M
cc0b9f404a merged develop, fix conflicts 2025-02-27 15:38:31 -08:00
Marcus Moore
70332696c6 Fix test by passing in required properties 2025-02-27 15:23:17 -08:00
Marcus Moore
7a9b5d61b0 Replace another Form::checkbox 2025-02-27 13:25:32 -08:00
Marcus Moore
5876259893 Replace another Form::checkbox 2025-02-27 13:19:26 -08:00
Marcus Moore
8755c54edc Replace Form::checkbox 2025-02-27 13:13:39 -08:00
Marcus Moore
014f3b7652 Cast to boolean 2025-02-27 13:12:01 -08:00
Marcus Moore
3a2579b205 WIP: replace Form::checkbox 2025-02-27 13:08:02 -08:00
James M
149474bfe3 Update general.php
FIX: Spelling error
2025-02-27 12:42:47 -07:00
James M
b2b768dede Merge branch 'snipe:develop' into develop 2025-02-27 12:25:07 -07:00
snipe
a9ed9e2a7f Merge pull request #16378 from snipe/wrap_pdf_table_results
Wrap long text in PDF export in tables
2025-02-27 19:06:34 +00:00
snipe
ce8523b00a Fixed wrapping
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 18:58:18 +00:00
snipe
7076a68d35 Wrap table results in PDF
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 18:38:10 +00:00
James M
112112d258 Feat: #14926 LDAP Active Flag - Add config option to make False = Enable 2025-02-27 10:52:12 -07:00
snipe
3928c8afe9 Merge pull request #16376 from uberbrady/improve_safety_csv_charset_detection
Add some safeties around the charset-detection and transliteration
2025-02-27 16:26:44 +00:00
snipe
23ce54e80c Make sure we’re saving the last_login in 2FA auths
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 16:17:59 +00:00
Brady Wetherington
646e3e8df5 Complete failed-transliteration test, clean up error, new translation string 2025-02-27 16:10:56 +00:00
snipe
30c4e9dbf7 Use formatter for created_at on unaccepted assets
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 15:48:24 +00:00
snipe
27fc30a881 Nicer button layout on unaccepted assets
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 15:43:53 +00:00
snipe
8ac7cda4ee Merge pull request #16366 from marcusmoore/chore/migrate-checkbox-helpers-pt7
Replace calls to Form::checkbox pt7
2025-02-27 15:01:31 +00:00
snipe
6f04d314a8 Merge pull request #16367 from marcusmoore/chore/migrate-checkbox-helpers-pt8
Replace calls to Form::checkbox pt8
2025-02-27 15:01:19 +00:00
snipe
1051b1d16d Merge pull request #16375 from snipe/fixes_16371_name_not_included_in_reminder_emails
Fixed #16371 - incorrect count and missing name in acceptance reminder email
2025-02-27 15:00:04 +00:00
snipe
115bb94704 Merge pull request #16156 from marcusmoore/acceptance-reminder-subject
Added "Reminder" to subject line of follow up asset checkout emails
2025-02-27 14:55:59 +00:00
snipe
25807cc62f Fixed constructor
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 14:22:48 +00:00
snipe
cd1d1b2d3e Fixed count
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 14:22:40 +00:00
Brady Wetherington
6dcd3bfd30 Add some safeties around the charset-detection and transliteration 2025-02-27 13:44:31 +00:00
snipe
b8799f8038 Bumped bash
Signed-off-by: snipe <snipe@snipe.net>
2025-02-27 12:21:28 +00:00
Marcus Moore
a0dc056da8 Replace Form::checkbox on label settings page 2025-02-26 16:33:07 -08:00
Marcus Moore
27aeb518ff Replace Form::checkbox on general settings page 2025-02-26 16:18:00 -08:00
snipe
dc619bb0dc Merge pull request #16365 from marcusmoore/chore/migrate-checkbox-helpers-pt6
Replace calls to Form::checkbox pt6
2025-02-26 23:54:50 +00:00
Marcus Moore
245a16c377 Replace Form::checkbox on branding settings page 2025-02-26 15:37:07 -08:00
Marcus Moore
de3c1d159f Replace Form::checkbox on branding settings page 2025-02-26 15:35:17 -08:00
Marcus Moore
af6d9e4a00 Replace Form::checkbox on custom report pages 2025-02-26 15:15:48 -08:00
snipe
8c8af3062e Merge pull request #16354 from snipe/dont_checkin_for_pending_on_asset_update
Allow pending as an asset status type that does not automatically check the asset in
2025-02-26 21:05:11 +00:00
snipe
2ff47edb94 Merge pull request #16361 from Godmartinz/acceptancer_reminder_unlisted_email_info
Fixed acceptance reminder command lag on users with no associated email
2025-02-26 20:33:02 +00:00
snipe
d923d29bad Merge pull request #16360 from spencerrlongg/bug/sc-28537
Add Safety Around Bulk Status Update
2025-02-26 20:32:19 +00:00
Godfrey M
899119ae2d changes output to a table 2025-02-26 12:30:19 -08:00
snipe
e031de8e49 Merge pull request #16363 from snipe/added_a_few_more_no_interaction_calls
Added `--no-interaction` to additional passport commands
2025-02-26 20:19:37 +00:00
snipe
a81c520d93 Follow up for #16341
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 20:15:09 +00:00
Godfrey M
8352e81228 fix unaccepted reminder test 2025-02-26 11:50:08 -08:00
Godfrey M
fd0174ff32 remove unwanted changes to livewire 2025-02-26 11:31:41 -08:00
Godfrey M
cc26aa02b2 fix acceptance reminder command no email list 2025-02-26 11:25:21 -08:00
snipe
616f3558dd Update example env
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 19:14:25 +00:00
spencerrlongg
d6e266cec1 make super safe 2025-02-26 13:11:49 -06:00
snipe
31516d7f24 Merge pull request #16356 from snipe/added_fields_for_model_search
Added name, model_number and notes for strict search
2025-02-26 12:46:13 +00:00
snipe
e79af255aa Added name, model_number and notes for strict search
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 12:43:54 +00:00
snipe
5b0d7f4064 One more chonk
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 12:01:50 +00:00
snipe
2f6af10c5d Bumped chunk for custom report
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 11:59:34 +00:00
snipe
afabda9235 Remove greater than 0 for alert threshold - fixes FD-47040
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 11:38:33 +00:00
snipe
1618c9ae8e Changed confirmation message
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 10:53:41 +00:00
snipe
5344ef4a1a Allow pending as an asset status that does not automatically check the asset in
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 10:46:38 +00:00
snipe
16420b1e00 Audit Log Number under Days to next Audit [sc-28530]
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 10:25:07 +00:00
snipe
62f66e724e Drop errors to warnings to stop pooping on rollbar
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 09:54:13 +00:00
snipe
9b0ea51d35 Moved composer clear commands to after composer install per #16334
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 09:37:31 +00:00
snipe
992214fc66 Check for adminuser withTrashed
Signed-off-by: snipe <snipe@snipe.net>
2025-02-26 08:25:22 +00:00
snipe
93dab12461 Merge pull request #16342 from snipe/nicer_upgrade_script
Nicer upgrade.php UI
2025-02-26 07:16:28 +00:00
snipe
ea0f105180 Merge pull request #16347 from marcusmoore/chore/migrate-checkbox-helpers-pt4
Replace calls to Form::checkbox pt4
2025-02-26 07:05:38 +00:00
snipe
5b6da0c1e8 Merge pull request #16348 from marcusmoore/chore/migrate-checkbox-helpers-pt5
Replace calls to Form::checkbox pt5
2025-02-26 07:05:22 +00:00
snipe
4d7655bbe1 Merge pull request #16346 from marcusmoore/chore/migrate-checkbox-helpers-pt3
Replace calls to Form::checkbox pt3
2025-02-26 07:04:27 +00:00
snipe
5e3855ee5b Merge pull request #16345 from marcusmoore/fixes/update-custom-fields
Fixed renaming custom fields
2025-02-26 07:03:40 +00:00
Marcus Moore
e01226a174 Replace Form::checkbox on saml settings page 2025-02-25 17:31:05 -08:00
Marcus Moore
f9ccf32af4 Replace Form::checkbox on ldap settings page 2025-02-25 17:22:44 -08:00
Marcus Moore
2c5170a218 Replace Form::checkbox on security settings page 2025-02-25 17:09:25 -08:00
Marcus Moore
15f842e2dc Replace Form::checkbox on bulk user edit page 2025-02-25 16:00:46 -08:00
Marcus Moore
46b31dfe14 Replace Form::checkbox on user create and edit page 2025-02-25 14:32:39 -08:00
Marcus Moore
d88c79366c Replace Form::checkbox on google settings page 2025-02-25 14:27:07 -08:00
Marcus Moore
de330a47cd Replace Form::checkbox on asset tag settings page 2025-02-25 14:25:02 -08:00
Marcus Moore
ecd7dc2094 Replace Form::checkbox on alert settings page 2025-02-25 14:23:26 -08:00
Marcus Moore
510946e0eb Replace Form::checkbox in logo upload partial 2025-02-25 14:20:43 -08:00
Marcus Moore
37e4a13979 Replace Form::checkbox in image upload partial 2025-02-25 14:15:12 -08:00
Marcus Moore
c73d64cdbc Remove manual doctrine mapping for enums 2025-02-25 13:37:23 -08:00
snipe
4a0410d969 Uncomment git stash
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 16:55:39 +00:00
snipe
668b9f8fb9 Nicer upgrade.php UI
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 16:51:32 +00:00
snipe
037d2d9e84 Merge pull request #16341 from uberbrady/fix_passport_install_no_interaction
Fixes #16331 - Don't make passport:install command require user input
2025-02-25 16:43:49 +00:00
Brady Wetherington
bce2007b97 Fixes #16331 - Don't make passport:install command require user input 2025-02-25 16:38:40 +00:00
Brady Wetherington
09a5e5b1bd Whoops! Need minimum 8.2, not 8.1 (probably mis-merge?) 2025-02-25 14:40:45 +00:00
snipe
7b4f4b6b7f Merge pull request #16337 from joakimbergros/develop
Fixed #16173: `useraccountcontrol` was not included in the ldap query attributes
2025-02-25 13:49:38 +00:00
Joakim Bergros
5c66334017 Added a check to see if the user has specified that is an ActiveDirectory server in the configuration before adding the useraccountcontrol attribute to the ldap query. 2025-02-25 14:22:22 +01:00
Joakim Bergros
ae82051b73 Fixed #16173: useraccountcontrol was not included in the ldap query attributes.
`$results` did not include the `useraccountcontrol` and thus rendered the fallback logic void when `active_flag` was blank.

 Added a condition to check if `active_flag` is blank and only then add `useraccountcontrol` to the ldap query since it is then a requirement in accordance with "we respect the userAccountControl attribute" text in the `admin/ldap` route.

[`elseif' will become true when `active_flag` is blank](b141945add/app/Console/Commands/LdapSync.php (L364))
2025-02-25 13:55:53 +01:00
snipe
c3a2e81afd Updated version
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 12:18:22 +00:00
snipe
18c7cbbbbb Merge pull request #16336 from snipe/change_order_in_upgrade_script
Fixed #16334 - Changed composer order in upgrade script
2025-02-25 12:09:15 +00:00
snipe
3718f08c72 Changed composer order in upgrade script
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 12:05:00 +00:00
snipe
b7821a69b6 Added link to the google apps script repo
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 11:07:04 +00:00
snipe
8049f21068 Updated laravel version in readme
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 11:02:00 +00:00
snipe
8140110bf9 Chnage dev docker env to en-US from en
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 10:57:38 +00:00
snipe
a4587f6322 Merge pull request #16335 from snipe/added_label_test
Added label test
2025-02-25 10:40:46 +00:00
snipe
163e19f1e9 Added label test
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 10:37:06 +00:00
snipe
8a40c3ecb4 Merge pull request #16325 from spencerrlongg/bug/sc-28520
Remove remaining hardware references in favor of new RMB
2025-02-25 10:09:35 +00:00
spencerrlongg
d949a9689f fix all 2025-02-24 22:35:47 -06:00
snipe
27a7c6fd10 Merge pull request #16324 from marcusmoore/chore/migrate-checkbox-helpers-p2
Replace calls to Form::checkbox pt2
2025-02-25 02:33:02 +00:00
Marcus Moore
4d55a67628 Merge branch 'develop' into chore/migrate-checkbox-helpers-p2 2025-02-24 16:37:59 -08:00
snipe
9d453226bd Merge pull request #16321 from spencerrlongg/bug/sc-28515
Changed Parameter Name for RMB
2025-02-25 00:17:37 +00:00
snipe
cd26694767 Merge pull request #16322 from marcusmoore/fixes/fix-test-edit-route
Fixed edit routes in tests
2025-02-25 00:17:10 +00:00
Marcus Moore
a26cc2ced1 Use .edit routes instead of .update 2025-02-24 15:55:05 -08:00
spencerrlongg
1fd81d17a3 route model binding change 2025-02-24 17:53:41 -06:00
snipe
134045f1ec Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2025-02-24 23:46:55 +00:00
snipe
b6b6a3eec7 Remove req for 0 min
Signed-off-by: snipe <snipe@snipe.net>
2025-02-24 23:46:50 +00:00
snipe
b62714d702 Merge pull request #16062 from snipe/remove_mcrypt
Removed references to mcrypt
2025-02-24 23:22:32 +00:00
Brady Wetherington
271bcc66bf Remove lib-mcrypt as a dependency 2025-02-24 23:19:08 +00:00
snipe
ced560daa9 Add @jostrander as a contributor 2025-02-24 23:16:27 +00:00
snipe
b395c9130a Merge remote-tracking branch 'origin/master' into develop 2025-02-24 23:06:46 +00:00
snipe
ed46a757fa Merge pull request #16320 from jostrander/fix/location-edit
Fixed: fix 500 on edit locations page by referencing current location
2025-02-24 23:03:01 +00:00
Jesse Ostrander
6a8a41b389 fix: reference to item location on location edit method 2025-02-24 17:34:18 -05:00
snipe
4800f4c853 Merge branch 'develop' into remove_mcrypt 2025-02-24 22:12:56 +00:00
snipe
c79ff49c85 Add @addex12 as a contributor 2025-02-24 20:30:04 +00:00
Marcus Moore
e88bba51bb Merge branch 'develop' into acceptance-reminder-subject 2025-02-24 11:55:50 -08:00
Marcus Moore
f97211f6cd Remove unused language string 2025-02-24 11:45:58 -08:00
Marcus Moore
027c2b3627 Change subject to "You have Unaccepted Assets." 2025-02-24 11:45:23 -08:00
Godfrey M
4c43a06eee add overdue asset to test 2025-02-13 11:19:49 -08:00
Godfrey M
25c8449e86 remove unused 2025-02-13 11:14:18 -08:00
Godfrey M
13e1f4a127 adds mailable 2025-02-13 11:09:39 -08:00
Godfrey M
ed96fd766c refactors audit notification to mail, adds test, ads check to scheduler 2025-02-13 11:08:53 -08:00
Marcus Moore
77635c3737 Migrate Form::checkboxes on license edit view 2025-02-06 17:05:35 -08:00
Marcus Moore
b937b7e767 Migrate Form::checkboxes on asset history import page 2025-02-06 17:01:09 -08:00
Marcus Moore
7cbb3f7e07 Add assertion 2025-01-30 11:48:55 -08:00
Marcus Moore
7e9c564d0b Simplify test 2025-01-30 11:47:43 -08:00
Marcus Moore
fc88b2487f Extract method 2025-01-30 11:44:37 -08:00
Marcus Moore
e94ee48f74 Extract helper 2025-01-30 10:37:11 -08:00
Marcus Moore
6a4a5d1380 Add translation 2025-01-30 10:35:31 -08:00
Marcus Moore
ab9e9b66d2 Reduce complexity 2025-01-29 16:27:18 -08:00
Marcus Moore
c15c338ffd Merge if/else 2025-01-29 16:25:37 -08:00
Marcus Moore
d1197d015c Add another case scenario 2025-01-29 16:24:43 -08:00
Marcus Moore
ce31ce477e Inline additional variables 2025-01-29 16:16:47 -08:00
Marcus Moore
78f9292555 Inline variable 2025-01-29 16:15:27 -08:00
Marcus Moore
4e7c6bd2cf Fix relationship 2025-01-29 16:14:09 -08:00
Marcus Moore
70aed45bfe Improve naming 2025-01-29 15:56:20 -08:00
Marcus Moore
e2805f4033 Add "Reminder" to subject line 2025-01-29 15:36:45 -08:00
Marcus Moore
d254a40e0a Scaffold tests 2025-01-29 15:21:10 -08:00
Marcus Moore
fdcb891cbb Improve test case 2025-01-29 15:20:56 -08:00
Godfrey M
16d322d70e fix translation 2025-01-29 10:38:30 -08:00
Godfrey M
2163312997 adds translations for branding and general settings 2025-01-29 10:35:26 -08:00
Godfrey M
0dfb71cfe5 added some translations to branding and general setrting 2025-01-28 11:56:10 -08:00
snipe
99526cfc2c Remove mcrypt and legacy recrypter
Signed-off-by: snipe <snipe@snipe.net>
2025-01-13 19:54:00 +00:00
Fiala06
bdb0e6c2a3 Update LdapSync.php
Fix for duplicate entries preventing the sync from continuing. 

  SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '13178-6' for key 'PRIMARY' (Connection: mysql, SQL: insert into users_groups (group_id, user_id) values (6, 13178))
2024-12-02 11:07:37 -08:00
87 changed files with 944 additions and 843 deletions

View File

@@ -3271,6 +3271,33 @@
"contributions": [
"code"
]
},
{
"login": "addex12",
"name": "Adugna Gizaw",
"avatar_url": "https://avatars.githubusercontent.com/u/18550946?v=4",
"profile": "https://orbalia.pythonanywhere.com/",
"contributions": [
"translation"
]
},
{
"login": "jostrander",
"name": "Jesse Ostrander",
"avatar_url": "https://avatars.githubusercontent.com/u/760989?v=4",
"profile": "https://github.com/jostrander",
"contributions": [
"code"
]
},
{
"login": "azmcnutt",
"name": "James M",
"avatar_url": "https://avatars.githubusercontent.com/u/31522486?v=4",
"profile": "https://github.com/azmcnutt",
"contributions": [
"code"
]
}
]
}

View File

@@ -16,7 +16,7 @@ APP_DEBUG=true
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en
APP_LOCALE=en-US
MAX_RESULTS=500
# --------------------------------------------

View File

@@ -99,7 +99,7 @@ PASSPORT_COOKIE_NAME='snipeit_passport_token'
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=15
BS_TABLE_STORAGE=cookieStorage
BS_TABLE_STORAGE=localStorage
BS_TABLE_DEEPLINK=true
# --------------------------------------------

View File

@@ -67,7 +67,7 @@ jobs:
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install
php artisan passport:install --no-interaction
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit

View File

@@ -65,7 +65,7 @@ jobs:
run: |
php artisan key:generate
php artisan migrate --force
php artisan passport:install
php artisan passport:install --no-interaction
chmod -R 777 storage bootstrap/cache
- name: Execute tests (Unit and Feature tests) via PHPUnit

View File

@@ -3,7 +3,7 @@
"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.1.0",
"php_min_version": "8.2.0",
"php_max_major_minor": "8.4",
"php_max_wontwork": "8.5.0",
"current_snipeit_version": "8.0"

View File

@@ -53,7 +53,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<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") | [<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/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") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -40,7 +40,6 @@ autoconf \
libc-dev \
libldap-common \
pkg-config \
libmcrypt-dev \
php8.3-dev \
ca-certificates \
unzip \
@@ -51,11 +50,6 @@ dnsutils \
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
RUN php go-pear.phar
RUN pecl install mcrypt
RUN bash -c "echo extension=/usr/lib/php/20210902/mcrypt.so > /etc/php/8.3/mods-available/mcrypt.ini"
RUN phpenmod mcrypt
RUN phpenmod gd
RUN phpenmod bcmath

View File

@@ -7,7 +7,7 @@
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 10](http://laravel.com).
It is built on [Laravel 11](http://laravel.com).
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
@@ -94,6 +94,8 @@ Since the release of the JSON REST API, several third-party developers have been
- [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.
We also have a handful of [Google Apps scripts](https://github.com/grokability/google-apps-scripts-for-snipe-it) to help with various tasks.
-----
### Join the Community!

View File

@@ -125,6 +125,10 @@ class LdapSync extends Command
*/
$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) {
@@ -357,9 +361,15 @@ class LdapSync extends Command
// (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;
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
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,
@@ -424,8 +434,12 @@ class LdapSync extends Command
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
$user->groups()->attach($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) {

View File

@@ -1,157 +0,0 @@
<?php
namespace App\Console\Commands;
use App\LegacyEncrypter\McryptEncrypter;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RecryptFromMcrypt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows upgrading users to de-encrypt their deprecated mcrypt encrypted fields and re-encrypt them using the current OpenSSL encryption.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Check and see if they have a legacy app key listed in their .env
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
$errors = [];
if (! $legacy_key) {
$this->error('ERROR: You do not have a LEGACY_APP_KEY set in your .env file. Please locate your old APP_KEY and ADD a line to your .env file like: LEGACY_APP_KEY=YOUR_OLD_APP_KEY');
return false;
}
// Do some basic legacy app key length checks
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1]) == 44)) {
$legacy_key = base64_decode($key_parts[1], true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
}
// Check that the app key is 32 characters
if ($legacy_length_check === true) {
$this->comment('INFO: Your LEGACY_APP_KEY looks correct. Okay to continue.');
} else {
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters or base64 followed by 44 characters for later versions). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
return false;
}
$this->error('================================!!!! WARNING !!!!================================');
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm('Are you SURE you wish to continue?'))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
try {
Storage::disk('local')->put($backup_file, 'APP_KEY: '.config('app.key'));
Storage::disk('local')->append($backup_file, 'LEGACY_APP_KEY: '.$legacy_key);
} catch (\Exception $e) {
$this->info('WARNING: Could not backup app keys');
}
if ($legacy_cipher) {
$mcrypter = new McryptEncrypter($legacy_key, $legacy_cipher);
} else {
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_pword == '') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted', '=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
$query = Asset::withTrashed();
foreach ($custom_fields as $custom_field) {
$this->comment('FIELD TO RECRYPT: '.$custom_field->name.' ('.$custom_field->db_column.')');
$query->orWhereNotNull($custom_field->db_column);
}
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$columnName} != '') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
}
}
$asset->save();
$bar->advance();
}
$bar->finish();
if (count($errors) > 0) {
$this->comment("\n\n");
$this->error("The decrypter encountered some errors: \n");
foreach ($errors as $error) {
$this->error($error);
}
}
}
}
}

View File

@@ -64,28 +64,43 @@ class SendAcceptanceReminder extends Command
->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){
$this->info($acceptance->assignedTo?->present()->fullName().' has no email address.');
$no_email_list[] = [
'id' => $acceptance->assignedTo?->id,
'name' => $acceptance->assignedTo?->present()->fullName(),
];
} 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)));
}
$count++;
}
$this->info($count.' users notified.');
$headers = ['ID', 'Name'];
$rows = [];
foreach ($no_email_list as $user) {
$rows[] = [$user['id'], $user['name']];
}
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
return 0;
}

View File

@@ -2,13 +2,12 @@
namespace App\Console\Commands;
use App\Mail\SendUpcomingAuditMail;
use App\Models\Asset;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\SendUpcomingAuditNotification;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendUpcomingAuditReport extends Command
{
@@ -48,19 +47,19 @@ class SendUpcomingAuditReport extends Command
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
$this->info($assets->count().' assets must be audited in on or before '.$interval_date.' is deadline');
$assets = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
$this->info($assets->count() . ' assets must be audited in on or before ' . $interval_date . ' is deadline');
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
if ((count($assets) !== 0) && ($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);
});
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item))
->all();
$this->info('Sending Admin SendUpcomingAuditNotification to: '.$settings->alert_email);
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
$this->info('Sending Admin SendUpcomingAuditNotification to: ' . $settings->alert_email);
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets, $settings->audit_warning_days));
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Console;
use App\Console\Commands\ImportLocations;
use App\Console\Commands\ReEncodeCustomFieldNames;
use App\Console\Commands\RestoreDeletedUsers;
use App\Models\Setting;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -18,12 +19,14 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
if(Setting::getSettings()->alerts_enabled === 1) {
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
}
$schedule->command('snipeit:backup')->weekly();
$schedule->command('backup:clean')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
$schedule->command('auth:clear-resets')->everyFifteenMinutes();
$schedule->command('saml:clear_expired_nonces')->weekly();
}

View File

@@ -77,6 +77,18 @@ class AssetModelsController extends Controller
$assetmodels->onlyTrashed();
}
if ($request->filled('name')) {
$assetmodels = $assetmodels->where('models.name', '=', $request->input('name'));
}
if ($request->filled('model_number')) {
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
}
if ($request->filled('notes')) {
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
}
if ($request->filled('category_id')) {
$assetmodels = $assetmodels->where('models.category_id', '=', $request->input('category_id'));
}

View File

@@ -66,25 +66,41 @@ class ImportController extends Controller
if (! ini_get('auto_detect_line_endings')) {
ini_set('auto_detect_line_endings', '1');
}
$file_contents = $file->getContent(); //TODO - this *does* load the whole file in RAM, but we need that to be able to 'iconv' it?
$encoding = $detector->getEncoding($file_contents);
$reader = null;
if (strcasecmp($encoding, 'UTF-8') != 0) {
$transliterated = iconv($encoding, 'UTF-8', $file_contents);
if ($transliterated !== false) {
$tmpname = tempnam(sys_get_temp_dir(), '');
$tmpresults = file_put_contents($tmpname, $transliterated);
if ($tmpresults !== false) {
if (function_exists('iconv')) {
$file_contents = $file->getContent(); //TODO - this *does* load the whole file in RAM, but we need that to be able to 'iconv' it?
$encoding = $detector->getEncoding($file_contents);
\Log::warning("Discovered encoding: $encoding in uploaded CSV");
$reader = null;
if (strcasecmp($encoding, 'UTF-8') != 0) {
$transliterated = false;
try {
$transliterated = iconv(strtoupper($encoding), 'UTF-8', $file_contents);
} catch (\Exception $e) {
$transliterated = false; //blank out the partially-decoded string
return response()->json(
Helper::formatStandardApiResponse(
'error',
null,
trans('admin/hardware/message.import.transliterate_failure', ["encoding" => $encoding])
),
422
);
}
if ($transliterated !== false) {
$tmpname = tempnam(sys_get_temp_dir(), '');
$tmpresults = file_put_contents($tmpname, $transliterated);
$transliterated = null; //save on memory?
$newfile = new UploadedFile($tmpname, $file->getClientOriginalName(), null, null, true); //WARNING: this is enabling 'test mode' - which is gross, but otherwise the file won't be treated as 'uploaded'
if ($newfile->isValid()) {
$file = $newfile;
if ($tmpresults !== false) {
$newfile = new UploadedFile($tmpname, $file->getClientOriginalName(), null, null, true); //WARNING: this is enabling 'test mode' - which is gross, but otherwise the file won't be treated as 'uploaded'
if ($newfile->isValid()) {
$file = $newfile;
}
}
}
}
$file_contents = null; //try to save on memory, I guess?
}
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
$file_contents = null; //try to save on memory, I guess?
try {
$import->header_row = $reader->fetchOne(0);

View File

@@ -290,10 +290,12 @@ class StatuslabelsController extends Controller
/**
* Returns a boolean response based on whether the status label
* is one that is deployable.
* is one that is deployable or pending.
*
* This is used by the hardware create/edit view to determine whether
* we should provide a dropdown of users for them to check the asset out to.
* we should provide a dropdown of users for them to check the asset out to,
* and whether we show a warning that the asset will be checked in if it's already
* assigned but the status is changed to one that isn't pending or deployable
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
@@ -301,7 +303,7 @@ class StatuslabelsController extends Controller
public function checkIfDeployable($id) : string
{
$statuslabel = Statuslabel::findOrFail($id);
if ($statuslabel->getStatuslabelType() == 'deployable') {
if (($statuslabel->getStatuslabelType() == 'pending') || ($statuslabel->getStatuslabelType() == 'deployable')) {
return '1';
}

View File

@@ -71,12 +71,12 @@ class AssetFilesController extends Controller
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.file_not_found'));
return redirect()->route('hardware.show', $asset)->with('error', trans('general.file_not_found'));
}
}
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found'));
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
}
@@ -102,7 +102,7 @@ class AssetFilesController extends Controller
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found'));
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
}
}

View File

@@ -340,15 +340,14 @@ class AssetsController extends Controller
$status = Statuslabel::find($request->input('status_id'));
// This is a non-deployable status label - we should check the asset back in.
if (($status && $status->getStatuslabelType() != 'deployable') && ($target = $asset->assignedTo)) {
// This is an archived or undeployable - we should check the asset back in.
// Pending is allowed here
if (($status) && (($status->getStatuslabelType() != 'pending') && ($status->getStatuslabelType() != 'deployable')) && ($target = $asset->assignedTo)) {
$originalValues = $asset->getRawOriginal();
$asset->assigned_to = null;
$asset->assigned_type = null;
$asset->accepted = null;
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update', date('Y-m-d H:i:s'), $originalValues));
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update with '.$status->getStatuslabelType().' status', date('Y-m-d H:i:s'), $originalValues));
}
if ($asset->assigned_to == '') {

View File

@@ -358,7 +358,11 @@ class BulkAssetsController extends Controller
* to someone/something.
*/
if ($request->filled('status_id')) {
$updated_status = Statuslabel::find($request->input('status_id'));
try {
$updated_status = Statuslabel::findOrFail($request->input('status_id'));
} catch (ModelNotFoundException $e) {
return redirect($bulk_back_url)->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
// We cannot assign a non-deployable status type if the asset is already assigned.
// This could probably be added to a form request.
@@ -366,7 +370,7 @@ class BulkAssetsController extends Controller
// Otherwise we need to make sure the status type is still a deployable one.
if (
($asset->assigned_to == '')
|| ($updated_status->deployable == '1') && ($asset->assetstatus->deployable == '1')
|| ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
) {
$this->update_array['status_id'] = $updated_status->id;
}

View File

@@ -206,6 +206,7 @@ class LoginController extends Controller
$user->password = bcrypt($request->input('password'));
}
$user->last_login = \Carbon::now();
$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc.
@@ -432,6 +433,7 @@ class LoginController extends Controller
if (Google2FA::verifyKey($user->two_factor_secret, $secret)) {
$user->two_factor_enrolled = 1;
$user->last_login = \Carbon::now();
$user->saveQuietly();
$request->session()->put('2fa_authed', $user->id);

View File

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

View File

@@ -100,7 +100,7 @@ class LocationsController extends Controller
public function edit(Location $location) : View | RedirectResponse
{
$this->authorize('update', Location::class);
return view('locations/edit');
return view('locations/edit')->with('item', $location);
}
/**

View File

@@ -266,7 +266,7 @@ class ReportsController extends Controller
$actionlogs = Actionlog::with('item', 'user', 'target', 'location', 'adminuser')
->orderBy('created_at', 'DESC')
->chunk(20, function ($actionlogs) use ($handle) {
->chunk(500, function ($actionlogs) use ($handle) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
Log::debug('Walking results: '.$executionTime);
$count = 0;
@@ -748,7 +748,7 @@ class ReportsController extends Controller
}
Log::debug($assets->toSql());
$assets->orderBy('assets.id', 'ASC')->chunk(20, function ($assets) use ($handle, $customfields, $request) {
$assets->orderBy('assets.id', 'ASC')->chunk(500, function ($assets) use ($handle, $customfields, $request) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
Log::debug('Walking results: '.$executionTime);
@@ -1175,18 +1175,13 @@ class ReportsController extends Controller
}
$email = $assetItem->assignedTo?->email;
$locale = $assetItem->assignedTo?->locale;
// Only send notification if assigned
if ($locale && $email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note))->locale($locale));
} elseif ($email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)));
}
if ($email == ''){
if (is_null($email) || $email === '') {
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
}
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note, firstTimeSending: false))->locale($locale));
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
}

3
app/Http/Controllers/SettingsController.php Executable file → Normal file
View File

@@ -256,7 +256,7 @@ class SettingsController extends Controller
Artisan::call('migrate', ['--force' => true]);
if ((! file_exists(storage_path().'/oauth-private.key')) || (! file_exists(storage_path().'/oauth-public.key'))) {
Artisan::call('migrate', ['--path' => 'vendor/laravel/passport/database/migrations', '--force' => true]);
Artisan::call('passport:install');
Artisan::call('passport:install', ['--no-interaction' => true]);
}
return view('setup/migrate')
@@ -851,6 +851,7 @@ class SettingsController extends Controller
$setting->ldap_auth_filter_query = $request->input('ldap_auth_filter_query');
$setting->ldap_version = $request->input('ldap_version', 3);
$setting->ldap_active_flag = $request->input('ldap_active_flag');
$setting->ldap_invert_active_flag = $request->input('ldap_invert_active_flag');
$setting->ldap_emp_num = $request->input('ldap_emp_num');
$setting->ldap_email = $request->input('ldap_email');
$setting->ldap_manager = $request->input('ldap_manager');

View File

@@ -26,9 +26,9 @@ class StoreNotificationSettings extends FormRequest
return [
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email_array|nullable',
'alert_threshold' => 'numeric|nullable|gt:0',
'alert_threshold' => 'numeric|nullable',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable',
'due_checkin_days' => 'numeric|nullable|gt:0',
'audit_interval' => 'numeric|nullable|gt:0',
];

View File

@@ -1,81 +0,0 @@
<?php
namespace App\LegacyEncrypter;
use Illuminate\Contracts\Encryption\DecryptException;
abstract class BaseEncrypter
{
/**
* The encryption key.
*
* @var string
*/
protected $key;
/**
* Create a MAC for the given value.
*
* @param string $iv
* @param string $value
* @return string
*/
protected function hash($iv, $value)
{
return hash_hmac('sha256', $iv.$value, $this->key);
}
/**
* Get the JSON array from the given payload.
*
* @param string $payload
* @return array
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
protected function getJsonPayload($payload)
{
$payload = json_decode(base64_decode($payload), true);
// If the payload is not valid JSON or does not have the proper keys set we will
// assume it is invalid and bail out of the routine since we will not be able
// to decrypt the given value. We'll also check the MAC for this encryption.
if (! $payload || $this->invalidPayload($payload)) {
throw new DecryptException('The payload is invalid.');
}
if (! $this->validMac($payload)) {
throw new DecryptException('The MAC is invalid.');
}
return $payload;
}
/**
* Verify that the encryption payload is valid.
*
* @param array|mixed $data
* @return bool
*/
protected function invalidPayload($data)
{
return ! is_array($data) || ! isset($data['iv']) || ! isset($data['value']) || ! isset($data['mac']);
}
/**
* Determine if the MAC for the given payload is valid.
*
* @param array $payload
* @return bool
*
* @throws \RuntimeException
*/
protected function validMac(array $payload)
{
$bytes = random_bytes(16);
$calcMac = hash_hmac('sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true);
return hash_equals(hash_hmac('sha256', $payload['mac'], $bytes, true), $calcMac);
}
}

View File

@@ -1,214 +0,0 @@
<?php
namespace App\LegacyEncrypter;
use Exception;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;
use Illuminate\Contracts\Encryption\EncryptException;
use RuntimeException;
/**
* @deprecated since version 5.1. Use Illuminate\Encryption\Encrypter.
*/
class McryptEncrypter extends BaseEncrypter implements EncrypterContract
{
/**
* The algorithm used for encryption.
*
* @var string
*/
protected $cipher;
/**
* The block size of the cipher.
*
* @var int
*/
protected $block;
/**
* Create a new encrypter instance.
*
* @param string $key
* @param string $cipher
* @return void
*
* @throws \RuntimeException
*/
public function __construct($key, $cipher = MCRYPT_RIJNDAEL_128)
{
$key = (string) $key;
if (static::supported($key, $cipher)) {
$this->key = $key;
$this->cipher = $cipher;
$this->block = mcrypt_get_iv_size($this->cipher, MCRYPT_MODE_CBC);
} else {
throw new RuntimeException('The only supported ciphers are MCRYPT_RIJNDAEL_128 and MCRYPT_RIJNDAEL_256.');
}
}
/**
* Determine if the given key and cipher combination is valid.
*
* @param string $key
* @param string $cipher
* @return bool
*/
public static function supported($key, $cipher)
{
return defined('MCRYPT_RIJNDAEL_128') &&
($cipher === MCRYPT_RIJNDAEL_128 || $cipher === MCRYPT_RIJNDAEL_256);
}
/**
* Encrypt the given value.
*
* @param string $value
* @return string
*
* @throws \Illuminate\Contracts\Encryption\EncryptException
*/
public function encrypt($value, $serialize = true)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'));
if (! is_string($json)) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
/**
* Pad and use mcrypt on the given value and input vector.
*
* @param string $value
* @param string $iv
* @return string
*/
protected function padAndMcrypt($value, $iv)
{
$value = $this->addPadding(serialize($value));
return mcrypt_encrypt($this->cipher, $this->key, $value, MCRYPT_MODE_CBC, $iv);
}
/**
* Decrypt the given value.
*
* @param string $payload
* @return string
*/
public function decrypt($payload, $unserialize = true)
{
$payload = $this->getJsonPayload($payload);
// We'll go ahead and remove the PKCS7 padding from the encrypted value before
// we decrypt it. Once we have the de-padded value, we will grab the vector
// and decrypt the data, passing back the unserialized from of the value.
$value = base64_decode($payload['value']);
$iv = base64_decode($payload['iv']);
return unserialize($this->stripPadding($this->mcryptDecrypt($value, $iv)));
}
/**
* Run the mcrypt decryption routine for the value.
*
* @param string $value
* @param string $iv
* @return string
*
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
protected function mcryptDecrypt($value, $iv)
{
try {
return mcrypt_decrypt($this->cipher, $this->key, $value, MCRYPT_MODE_CBC, $iv);
} catch (Exception $e) {
throw new DecryptException($e->getMessage());
}
}
/**
* Add PKCS7 padding to a given value.
*
* @param string $value
* @return string
*/
protected function addPadding($value)
{
$pad = $this->block - (strlen($value) % $this->block);
return $value.str_repeat(chr($pad), $pad);
}
/**
* Remove the padding from the given value.
*
* @param string $value
* @return string
*/
protected function stripPadding($value)
{
$pad = ord($value[($len = strlen($value)) - 1]);
return $this->paddingIsValid($pad, $value) ? substr($value, 0, $len - $pad) : $value;
}
/**
* Determine if the given padding for a value is valid.
*
* @param string $pad
* @param string $value
* @return bool
*/
protected function paddingIsValid($pad, $value)
{
$beforePad = strlen($value) - $pad;
return substr($value, $beforePad) == str_repeat(substr($value, -1), $pad);
}
/**
* Get the IV size for the cipher.
*
* @return int
*/
protected function getIvSize()
{
return mcrypt_get_iv_size($this->cipher, MCRYPT_MODE_CBC);
}
/**
* Get the random data source available for the OS.
*
* @return int
*/
protected function getRandomizer()
{
if (defined('MCRYPT_DEV_URANDOM')) {
return MCRYPT_DEV_URANDOM;
}
if (defined('MCRYPT_DEV_RANDOM')) {
return MCRYPT_DEV_RANDOM;
}
mt_srand();
return MCRYPT_RAND;
}
}

View File

@@ -115,7 +115,7 @@ class CheckoutableListener
}
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail') );
} catch (Exception $e) {
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
'error' => $e->getMessage(),
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
'event' => $event,
@@ -211,7 +211,7 @@ class CheckoutableListener
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
}
} catch (Exception $e) {
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
'error' => $e->getMessage(),
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
'event' => $event,

View File

@@ -12,11 +12,11 @@ class CategoryEditForm extends Component
public $originalSendCheckInEmailValue;
public $requireAcceptance;
public bool $requireAcceptance;
public $sendCheckInEmail;
public bool $sendCheckInEmail;
public $useDefaultEula;
public bool $useDefaultEula;
public function mount()
{

View File

@@ -20,10 +20,12 @@ class CheckoutAssetMail extends Mailable
{
use Queueable, SerializesModels;
private bool $firstTimeSending;
/**
* Create a new message instance.
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
@@ -36,6 +38,8 @@ class CheckoutAssetMail extends Mailable
$this->last_checkout = '';
$this->expected_checkin = '';
$this->firstTimeSending = $firstTimeSending;
if ($this->item->last_checkout) {
$this->last_checkout = Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false);
@@ -56,7 +60,7 @@ class CheckoutAssetMail extends Mailable
return new Envelope(
from: $from,
subject: trans('mail.Asset_Checkout_Notification'),
subject: $this->getSubject(),
);
}
@@ -107,4 +111,13 @@ class CheckoutAssetMail extends Mailable
{
return [];
}
private function getSubject(): string
{
if ($this->firstTimeSending) {
return trans('mail.Asset_Checkout_Notification');
}
return trans('mail.unaccepted_asset_reminder');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class SendUpcomingAuditMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct($params, $threshold)
{
$this->assets = $params;
$this->threshold = $threshold;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans_choice('mail.upcoming-audits', $this->assets->count(), ['count' => $this->assets->count(), 'threshold' => $this->threshold]),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'notifications.markdown.upcoming-audits',
with: [
'assets' => $this->assets,
'threshold' => $this->threshold,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -19,9 +19,10 @@ class UnacceptedAssetReminderMail extends Mailable
*/
public function __construct($checkout_info, $count)
{
$this->count = $count;
$this->target = $checkout_info['acceptance']?->assignedTo;
$this->acceptance = $checkout_info['acceptance'];
$this->target = $checkout_info?->assignedTo;
$this->acceptance = $checkout_info;
}
/**

View File

@@ -293,16 +293,22 @@ class Actionlog extends SnipeModel
public function daysUntilNextAudit($monthInterval = 12, $asset = null)
{
$now = Carbon::now();
$last_audit_date = $this->created_at;
$next_audit = $last_audit_date->addMonth($monthInterval);
$next_audit_days = $now->diffInDays($next_audit);
$last_audit_date = $this->created_at; // this is the action log's created at, not the asset itself
$next_audit = $last_audit_date->addMonth($monthInterval); // this actually *modifies* the $last_audit_date
$next_audit_days = round($now->diffInDays($next_audit, true));
$override_default_next = $next_audit;
// Override the default setting for interval if the asset has its own next audit date
if (($asset) && ($asset->next_audit_date)) {
$override_default_next = \Carbon::parse($asset->next_audit_date);
$next_audit_days = $override_default_next->diffInDays($now);
$override_default_next = Carbon::parse($asset->next_audit_date);
$next_audit_days = round($override_default_next->diffInDays($now, true));
}
// Show as negative number if the next audit date is before the audit date we're looking at
if ($this->created_at > $override_default_next) {
$next_audit_days = '-'.$next_audit_days;
}
return $next_audit_days;
}

View File

@@ -234,7 +234,7 @@ class AssetModel extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}

View File

@@ -149,11 +149,6 @@ class CustomField extends Model
return true;
}
// This is just a dumb thing we have to include because Laraval/Doctrine doesn't
// play well with enums or a table that EVER had enums. :(
$platform = Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('enum', 'string');
// Rename the field if the name has changed
Schema::table(self::$table_name, function ($table) use ($custom_field) {
$table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug());

View File

@@ -90,12 +90,6 @@ class AssetMaintenancesPresenter extends Presenter
'searchable' => true,
'sortable' => true,
'title' => trans('admin/asset_maintenances/form.asset_maintenance_type'),
], [
'field' => 'title',
'searchable' => true,
'sortable' => true,
'switchable' => false,
'title' => trans('admin/asset_maintenances/form.title'),
], [
'field' => 'start_date',
'searchable' => true,

View File

@@ -140,8 +140,8 @@ class Label implements View
$barcode2DTarget = $asset->serial;
break;
case 'hardware_id':
default:
$barcode2DTarget = route('hardware.show', ['hardware' => $asset->id]);
default:
$barcode2DTarget = route('hardware.show', $asset);
break;
}
$assetData->put('barcode2d', (object)[

View File

@@ -105,4 +105,17 @@ $config = [
// (by default, the PUBLIC_FILESYSTEM DISK is 'local_public', in the public/uploads directory)
$config['disks']['public'] = $config['disks'][env('PUBLIC_FILESYSTEM_DISK','local_public')];
// Handle the 'backup' disk for S3 consideration
if (env('PRIVATE_FILESYSTEM_DISK') == 's3_private') {
$config['disks']['backup']['driver'] = 's3';
$config['disks']['backup']['key'] = $config['disks']['s3_private']['key'];
$config['disks']['backup']['secret'] = $config['disks']['s3_private']['secret'];
$config['disks']['backup']['region'] = $config['disks']['s3_private']['region'];
$config['disks']['backup']['bucket'] = $config['disks']['s3_private']['bucket'];
$config['disks']['backup']['url'] = $config['disks']['s3_private']['url'];
$config['disks']['backup']['root'] = $config['disks']['s3_private']['root'];
$config['disks']['backup']['visibility'] = 'private';
}
return $config;

View File

@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v8.0.0',
'full_app_version' => 'v8.0.0 - build 16962-g982cfeca32',
'build_version' => '16962',
'app_version' => 'v8.0.2',
'full_app_version' => 'v8.0.2 - build 17048-g44dd06161',
'build_version' => '17048',
'prerelease_version' => '',
'hash_version' => 'g982cfeca32',
'full_hash' => 'v8.0.0-172-g982cfeca32',
'branch' => 'master',
);
'hash_version' => 'g44dd06161',
'full_hash' => 'v8.0.2-54-g44dd06161',
'branch' => 'develop',
);

View File

@@ -27,6 +27,10 @@ class CheckoutAcceptanceFactory extends Factory
public function configure(): static
{
return $this->afterCreating(function (CheckoutAcceptance $acceptance) {
if ($acceptance->checkoutable instanceof Asset) {
$this->createdAssociatedActionLogEntry($acceptance);
}
if ($acceptance->checkoutable instanceof Asset && $acceptance->assignedTo instanceof User) {
$acceptance->checkoutable->update([
'assigned_to' => $acceptance->assigned_to_id,
@@ -51,4 +55,23 @@ class CheckoutAcceptanceFactory extends Factory
'declined_at' => null,
]);
}
public function accepted()
{
return $this->state([
'accepted_at' => now()->subDay(),
'declined_at' => null,
]);
}
private function createdAssociatedActionLogEntry(CheckoutAcceptance $acceptance): void
{
$acceptance->checkoutable->assetlog()->create([
'action_type' => 'checkout',
'target_id' => $acceptance->assigned_to_id,
'target_type' => get_class($acceptance->assignedTo),
'item_id' => $acceptance->checkoutable_id,
'item_type' => $acceptance->checkoutable_type,
]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('ldap_invert_active_flag')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('ldap_invert_active_flag');
});
}
};

View File

@@ -13,4 +13,8 @@ return [
'no_depreciations_warning' => '<strong>Warning: </strong>
You do not currently have any depreciations set up.
Please set up at least one depreciation to view the depreciation report.',
'depreciation_method' => 'Depreciation Method',
'linear_depreciation' => 'Linear (Default)',
'half_1' => 'Half-year convention, always applied',
'half_2' => 'Half-year convention, applied with condition',
];

View File

@@ -56,7 +56,7 @@ return [
'asset_location_update_actual' => 'Update only actual location',
'asset_not_deployable' => 'That asset status is not deployable. This asset cannot be checked out.',
'asset_not_deployable_checkin' => 'That asset status is not deployable. Using this status label will checkin the asset.',
'asset_deployable' => 'That status is deployable. This asset can be checked out.',
'asset_deployable' => 'This asset can be checked out.',
'processing_spinner' => 'Processing... (This might take a bit of time on large files)',
'optional_infos' => 'Optional Information',
'order_details' => 'Order Related Information',

View File

@@ -66,6 +66,7 @@ return [
'file_already_deleted' => 'The file selected was already deleted',
'header_row_has_malformed_characters' => 'One or more attributes in the header row contain malformed UTF-8 characters',
'content_row_has_malformed_characters' => 'One or more attributes in the first row of content contain malformed UTF-8 characters',
'transliterate_failure' => 'Transliteration from :encoding to UTF-8 failed due to invalid characters in input'
],

View File

@@ -4,6 +4,7 @@ return [
'info' => 'Select the options you want for your asset report.',
'deleted_user' => 'Deleted user',
'send_reminder' => 'Send reminder',
'cannot_send_reminder' => 'User has been deleted or does not have an email address so cannot receive a reminder',
'reminder_sent' => 'Reminder sent',
'acceptance_deleted' => 'Acceptance request deleted',
'acceptance_request' => 'Acceptance request',

View File

@@ -42,6 +42,7 @@ return [
'confirm_purge' => 'Confirm Purge',
'confirm_purge_help' => 'Enter the text "DELETE" in the box below to purge your deleted records. This action cannot be undone and will PERMANENTLY delete all soft-deleted items and users. (You should make a backup first, just to be safe.)',
'custom_css' => 'Custom CSS',
'custom_css_placeholder' => 'Add your custom CSS',
'custom_css_help' => 'Enter any custom CSS overrides you would like to use. Do not include the &lt;style&gt;&lt;/style&gt; tags.',
'custom_forgot_pass_url' => 'Custom Password Reset URL',
'custom_forgot_pass_url_help' => 'This replaces the built-in forgotten password URL on the login screen, useful to direct people to internal or hosted LDAP password reset functionality. It will effectively disable local user forgotten password functionality.',
@@ -69,6 +70,7 @@ return [
'favicon_size' => 'Favicons should be square images, 16x16 pixels.',
'footer_text' => 'Additional Footer Text ',
'footer_text_help' => 'This text will appear in the right-side footer. Links are allowed using <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>. Line breaks, headers, images, etc may result in unpredictable results.',
'footer_text_placeholder' => 'Optional footer text',
'general_settings' => 'General Settings',
'general_settings_help' => 'Default EULA and more',
'generate_backup' => 'Generate Backup',
@@ -118,6 +120,8 @@ return [
'ldap_version' => 'LDAP Version',
'ldap_active_flag' => 'LDAP Active Flag',
'ldap_activated_flag_help' => 'This value is used to determine whether a synced user can login to Snipe-IT. <strong>It does not affect the ability to check items in or out to them</strong>, and should be the <strong>attribute name</strong> within your AD/LDAP, <strong>not the value</strong>. <br><br>If this field is set to a field name that does not exist in your AD/LDAP, or the value in the AD/LDAP field is set to <code>0</code> or <code>false</code>, <strong>user login will be disabled</strong>. If the value in the AD/LDAP field is set to <code>1</code> or <code>true</code> or <em>any other text</em> means the user can log in. When the field is blank in your AD, we respect the <code>userAccountControl</code> attribute, which usually allows non-suspended users to log in.',
'ldap_invert_active_flag' => 'LDAP Invert Active Flag',
'ldap_invert_active_flag_help' => 'If enabled: when the value returned by LDAP Active Flag is <code>0</code> or <code>false</code> the user account will be active.',
'ldap_emp_num' => 'LDAP Employee Number',
'ldap_email' => 'LDAP Email',
'ldap_test' => 'Test LDAP',
@@ -132,6 +136,7 @@ return [
'login_user_agent' => 'User Agent',
'login_help' => 'List of attempted logins',
'login_note' => 'Login Note',
'login_note_placeholder' => "If you do not have a login or have found a device belonging to this company, please call technical support at 888-555-1212. Thank you.",
'login_note_help' => 'Optionally include a few sentences on your login screen, for example to assist people who have found a lost or stolen device. This field accepts <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>',
'login_remote_user_text' => 'Remote User login options',
'login_remote_user_enabled_text' => 'Enable Login with Remote User Header',
@@ -345,6 +350,8 @@ return [
'setup_migration_create_user' => 'Next: Create User',
'ldap_settings_link' => 'LDAP Settings Page',
'slack_test' => 'Test <i class="fab fa-slack"></i> Integration',
'status_label_name' => 'Status Label Name',
'super_admin_only' => 'Super Admin Only',
'label2_enable' => 'New Label Engine',
'label2_enable_help' => 'Switch to the new label engine. <b>Note: You will need to save this setting before setting others.</b>',
'label2_template' => 'Template',
@@ -378,6 +385,7 @@ return [
'database_driver' => 'Database Driver',
'bs_table_storage' => 'Table Storage',
'timezone' => 'Timezone',
'test_mail' => 'Test Mail',
'profile_edit' => 'Edit Profile',
'profile_edit_help' => 'Allow users to edit their own profiles.',
'default_avatar' => 'Upload custom default avatar',
@@ -387,6 +395,15 @@ return [
'due_checkin_days' => 'Due For Checkin Warning',
'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?',
'no_groups' => 'No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.',
'text' => 'Text',
'logo_option_types' => [
'text' => 'Text',
'logo' => 'Logo',
'logo_and_text' => 'Logo and Text',
],
/* Keywords for settings overview help */

View File

@@ -599,5 +599,6 @@ return [
'generic_model_not_found' => 'That :model was not found or you do not have permission to access it',
'deleted_models' => 'Deleted Asset Models',
'deleted_users' => 'Deleted Users',
'cost_each' => ':amount each',
];

View File

@@ -26,9 +26,9 @@
<livewire:category-edit-form
:default-eula-text="$snipeSettings->default_eula_text"
:eula-text="old('eula_text', $item->eula_text)"
:require-acceptance="old('require_acceptance', $item->require_acceptance)"
:send-check-in-email="old('checkin_email', $item->checkin_email)"
:use-default-eula="old('use_default_eula', $item->use_default_eula)"
:require-acceptance="(bool) old('require_acceptance', $item->require_acceptance)"
:send-check-in-email="(bool) old('checkin_email', $item->checkin_email)"
:use-default-eula="(bool) old('use_default_eula', $item->use_default_eula)"
/>
@include ('partials.forms.edit.image-upload', ['image_path' => app('categories_upload_path')])

View File

@@ -87,7 +87,7 @@
<div class="form-group">
<div class="col-sm-10 col-md-offset-2">
<label class="form-control">
{{ Form::checkbox('match_firstnamelastname', '1', old('match_firstnamelastname')) }}
<input type="checkbox" name="match_firstnamelastname" value="1" @checked(old('match_firstnamelastname'))>
<!-- enclosing span needed for the checkbox CSS to work properly, since there's HTML in the label text -->
<span>
{!! trans('admin/hardware/general.csv_import_match_f-l') !!}
@@ -100,7 +100,7 @@
<div class="form-group">
<div class="col-sm-10 col-md-offset-2">
<label class="form-control">
{{ Form::checkbox('match_flastname', '1', old('match_flastname')) }}
<input type="checkbox" name="match_flastname" value="1" @checked(old('match_flastname'))>
<!-- enclosing span needed for the checkbox CSS to work properly, since there's HTML in the label text -->
<span>
{!! trans('admin/hardware/general.csv_import_match_initial_last') !!}
@@ -113,7 +113,7 @@
<div class="form-group">
<div class="col-sm-10 col-md-offset-2">
<label class="form-control">
{{ Form::checkbox('match_firstname', '1', old('match_firstname')) }}
<input type="checkbox" name="match_firstname" value="1" @checked(old('match_firstname'))>
<!-- enclosing span needed for the checkbox CSS to work properly, since there's HTML in the label text -->
<span>
{!! trans('admin/hardware/general.csv_import_match_first') !!}
@@ -126,7 +126,7 @@
<div class="form-group">
<div class="col-sm-10 col-md-offset-2">
<label class="form-control">
{{ Form::checkbox('match_email', '1', old('match_email')) }}
<input type="checkbox" name="match_email" value="1" @checked(old('match_email'))>
<!-- enclosing span needed for the checkbox CSS to work properly, since there's HTML in the label text -->
<span>
{!! trans('admin/hardware/general.csv_import_match_email') !!}
@@ -139,7 +139,7 @@
<div class="form-group">
<div class="col-sm-10 col-md-offset-2">
<label class="form-control">
{{ Form::checkbox('match_username', '1', old('match_username')) }}
<input type="checkbox" name="match_username" value="1" @checked(old('match_username'))>
<!-- enclosing span needed for the checkbox CSS to work properly, since there's HTML in the label text -->
<span>
{!! trans('admin/hardware/general.csv_import_match_username') !!}

View File

@@ -1211,7 +1211,7 @@
<a href="{{ route('components.show', $component->id) }}">{{ $component->name }}</a>
</td>
<td>{{ $component->pivot->assigned_qty }}</td>
<td>{{ Helper::formatCurrencyOutput($component->purchase_cost) }} each</td>
<td>{{ trans('general.cost_each', ['amount' => Helper::formatCurrencyOutput($component->purchase_cost)]) }} </td>
<td>{{ $component->serial }}</td>
<td>
<a href="{{ route('components.checkin.show', $component->pivot->id) }}" class="btn btn-sm bg-purple hidden-print" data-tooltip="true">{{ trans('general.checkin') }}</a>

View File

@@ -68,7 +68,7 @@
</div>
<div class="col-md-7">
<label class="form-control">
{{ Form::Checkbox('reassignable', '1', old('reassignable', $item->id ? $item->reassignable : '1'),array('aria-label'=>'reassignable')) }}
<input type="checkbox" name="reassignable" value="1" aria-label="reassignable" @checked(old('reassignable', $item->id ? $item->reassignable : '1'))>
{{ trans('general.yes') }}
</label>
</div>
@@ -124,7 +124,7 @@
<div class="col-md-3 control-label"><strong>{{ trans('admin/licenses/form.maintained') }}</strong></div>
<div class="col-md-7">
<label class="form-control">
{{ Form::Checkbox('maintained', '1', old('maintained', $item->maintained),array('aria-label'=>'maintained')) }}
<input type="checkbox" name="maintained" value="1" aria-label="maintained" @checked(old('maintained', $item->maintained))>
{{ trans('general.yes') }}
</label>
</div>

View File

@@ -23,12 +23,25 @@
<div class="col-md-9 col-md-offset-3">
@if ($defaultEulaText!='')
<label class="form-control">
{{ Form::checkbox('use_default_eula', '1', $useDefaultEula, ['wire:model.live' => 'useDefaultEula', 'aria-label'=>'use_default_eula']) }}
<input
type="checkbox"
name="use_default_eula"
value="1"
wire:model.live="useDefaultEula"
aria-label="use_default_eula"
/>
<span>{!! trans('admin/categories/general.use_default_eula') !!}</span>
</label>
@else
<label class="form-control form-control--disabled">
{{ Form::checkbox('use_default_eula', '0', $useDefaultEula, ['wire:model.live' => 'useDefaultEula', 'class'=>'disabled','disabled' => 'disabled', 'aria-label'=>'use_default_eula']) }}
<input
type="checkbox"
name="use_default_eula"
value="0"
wire:model.live="useDefaultEula"
aria-label="use_default_eula"
disabled
/>
<span>{!! trans('admin/categories/general.use_default_eula_disabled') !!}</span>
</label>
@endif
@@ -39,7 +52,13 @@
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('require_acceptance', '1', $requireAcceptance, ['wire:model.live' => 'requireAcceptance', 'aria-label'=>'require_acceptance']) }}
<input
type="checkbox"
name="require_acceptance"
value="1"
wire:model.live="requireAcceptance"
aria-label="require_acceptance"
/>
{{ trans('admin/categories/general.require_acceptance') }}
</label>
</div>
@@ -49,7 +68,14 @@
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('checkin_email', '1', $sendCheckInEmail, ['wire:model.live' => 'sendCheckInEmail', 'aria-label'=>'checkin_email', 'disabled' => $this->sendCheckInEmailDisabled]) }}
<input
type="checkbox"
name="checkin_email"
value="1"
wire:model.live="sendCheckInEmail"
aria-label="checkin_email"
@disabled($this->sendCheckInEmailDisabled)
/>
{{ trans('admin/categories/general.checkin_email') }}
</label>
@if ($this->shouldDisplayEmailMessage)

View File

@@ -10,8 +10,15 @@
<div class="col-md-3">
@if ($fieldset_id)
<label class="form-control">
{{ Form::checkbox('add_default_values', 1, old('add_default_values', $add_default_values), ['data-livewire-component' => $this->getId(), 'id' => 'add_default_values', 'wire:model.live' => 'add_default_values', 'disabled' => $this->fields->isEmpty()]) }}
<input
type="checkbox"
name="add_default_values"
value="1"
id="add_default_values"
wire:model.live="add_default_values"
data-livewire-component="{{ $this->getId() }}"
@disabled($this->fields->isEmpty())
/>
{{ trans('admin/models/general.add_default_values') }}
</label>
@endif

View File

@@ -164,7 +164,7 @@
</li>
@endif
@if ($model->created_by)
@if ($model->adminuser)
<li>{{ trans('general.created_by') }}:
{{ $model->adminuser->present()->name() }}
</li>

View File

@@ -10,7 +10,7 @@
@php
$checkin = Helper::getFormattedDateObject($asset->expected_checkin, 'date');
@endphp
| [{{ $asset->present()->name }}]({{ route('hardware.show', ['hardware' => $asset->id]) }}) | [{{ $asset->assignedTo->present()->fullName }}]({{ route($asset->targetShowRoute().'.show', [$asset->assignedTo->id]) }}) | {{ $checkin['formatted'] }}
| [{{ $asset->present()->name }}]({{ route('hardware.show', $asset) }}) | [{{ $asset->assignedTo->present()->fullName }}]({{ route($asset->targetShowRoute().'.show', [$asset->assignedTo->id]) }}) | {{ $checkin['formatted'] }}
@endforeach
@endcomponent

View File

@@ -29,7 +29,16 @@
data_export_options = $(this).attr('data-export-options');
export_options = data_export_options ? JSON.parse(data_export_options) : {};
export_options['htmlContent'] = false; // this is already the default; but let's be explicit about it
export_options['jspdf']= {"orientation": "l"};
export_options['jspdf'] = {
"orientation": "l",
"autotable": {
"styles": {
overflow: 'linebreak'
},
tableWidth: 'wrap'
}
};
// tableWidth: 'wrap',
// the following callback method is necessary to prevent XSS vulnerabilities
// (this is taken from Bootstrap Tables's default wrapper around jQuery Table Export)
export_options['onCellHtmlData'] = function (cell, rowIndex, colIndex, htmlData) {

View File

@@ -1,7 +1,7 @@
<div class="form-group {{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-3 col-xs-12 control-label">{{ trans('admin/suppliers/table.email') }}</label>
<div class="col-md-8 col-xs-12">
<input type="text" name="email" id="email" value="{{ old('email', ($item->email ?? $user->email)) }}" class="form-control" maxlength="191" style="width:100%; display:flex;">
<input type="text" name="email" id="email" value="{{ old('email', $item->email) }}" class="form-control" maxlength="191" style="width:100%; display:flex;">
{!! $errors->first('email', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
</div>
</div>

View File

@@ -4,7 +4,7 @@
<div class="form-group{{ $errors->has('image_delete') ? ' has-error' : '' }}">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('image_delete', '1', old('image_delete'), ['aria-label'=>'image_delete']) }}
<input type="checkbox" name="image_delete" value="1" @checked(old('image_delete')) aria-label="image_delete">
{{ trans('general.image_delete') }}
{!! $errors->first('image_delete', '<span class="alert-msg">:message</span>') !!}
</label>

View File

@@ -47,7 +47,7 @@
<div class="col-md-9 col-md-offset-3">
<label id="{{ $logoId }}-deleteCheckbox" for="{{ $logoClearVariable }}" style="font-weight: normal" class="form-control">
{{ Form::checkbox($logoClearVariable, '1', old($logoClearVariable)) }}
<input type="checkbox" name="{{ $logoClearVariable }}" value="1" @checked(old($logoClearVariable))>
Remove current {{ ucwords(str_replace('_', ' ', $logoVariable)) }} image
</label>
</div>

View File

@@ -35,13 +35,12 @@
<thead>
<tr>
<th class="col-sm-1" data-field="file" data-visible="false" data-formatter="auditImageFormatter">{{ trans('admin/hardware/table.image') }}</th>
<th class="col-sm-2" data-field="created_at" data-formatter="dateDisplayFormatter" data-sortable="true" data-searchable="true">{{ trans('general.audit') }}</th>
<th class="col-sm-2" data-field="created_by" data-sortable="true" data-searchable="true" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
<th class="col-sm-2" data-field="created_by" data-sortable="true" data-searchable="true" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
<th class="col-sm-2" data-field="item" data-sortable="true" data-searchable="true" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
<th class="col-sm-1" data-field="location" data-sortable="true" data-searchable="true" data-formatter="locationsLinkObjFormatter">{{ trans('general.location') }}</th>
<th class="col-sm-2" data-field="created_at" data-formatter="dateDisplayFormatter" data-sortable="true" data-searchable="true">{{ trans('general.last_audit') }}</th>
<th class="col-sm-2" data-field="next_audit_date" data-formatter="dateDisplayFormatter">{{ trans('general.next_audit_date') }}</th>
<th class="col-sm-1" data-field="days_to_next_audit">{{ trans('general.days_to_next_audit') }}</th>
<th class="col-sm-2" data-field="note" data-sortable="true" data-searchable="true">{{ trans('general.notes') }}</th>
</tr>
</thead>

View File

@@ -93,152 +93,152 @@
</label>
<label class="form-control">
{{ Form::checkbox('id', '1', $template->checkmarkValue('id')) }}
<input type="checkbox" name="id" value="1" @checked($template->checkmarkValue('id')) />
{{ trans('general.id') }}
</label>
<label class="form-control">
{{ Form::checkbox('company', '1', $template->checkmarkValue('company')) }}
<input type="checkbox" name="company" value="1" @checked($template->checkmarkValue('company')) />
{{ trans('general.company') }}
</label>
<label class="form-control">
{{ Form::checkbox('asset_tag', '1', $template->checkmarkValue('asset_tag')) }}
<input type="checkbox" name="asset_tag" value="1" @checked($template->checkmarkValue('asset_tag')) />
{{ trans('general.asset_tag') }}
</label>
<label class="form-control">
{{ Form::checkbox('asset_name', '1', $template->checkmarkValue('asset_name')) }}
<input type="checkbox" name="asset_name" value="1" @checked($template->checkmarkValue('asset_name')) />
{{ trans('admin/hardware/form.name') }}
</label>
<label class="form-control">
{{ Form::checkbox('manufacturer', '1', $template->checkmarkValue('manufacturer')) }}
<input type="checkbox" name="manufacturer" value="1" @checked($template->checkmarkValue('manufacturer')) />
{{ trans('general.manufacturer') }}
</label>
<label class="form-control">
{{ Form::checkbox('model', '1', $template->checkmarkValue('model')) }}
<input type="checkbox" name="model" value="1" @checked($template->checkmarkValue('model')) />
{{ trans('general.asset_models') }}
</label>
<label class="form-control">
{{ Form::checkbox('category', '1', $template->checkmarkValue('category')) }}
<input type="checkbox" name="category" value="1" @checked($template->checkmarkValue('category')) />
{{ trans('general.category') }}
</label>
<label class="form-control">
{{ Form::checkbox('serial', '1', $template->checkmarkValue('serial')) }}
<input type="checkbox" name="serial" value="1" @checked($template->checkmarkValue('serial')) />
{{ trans('admin/hardware/table.serial') }}
</label>
<label class="form-control">
{{ Form::checkbox('purchase_date', '1', $template->checkmarkValue('purchase_date')) }}
<input type="checkbox" name="purchase_date" value="1" @checked($template->checkmarkValue('purchase_date')) />
{{ trans('admin/licenses/table.purchase_date') }}
</label>
<label class="form-control">
{{ Form::checkbox('purchase_cost', '1', $template->checkmarkValue('purchase_cost')) }}
<input type="checkbox" name="purchase_cost" value="1" @checked($template->checkmarkValue('purchase_cost')) />
{{ trans('admin/hardware/form.cost') }}
</label>
<label class="form-control">
{{ Form::checkbox('eol', '1', $template->checkmarkValue('eol')) }}
<input type="checkbox" name="eol" value="1" @checked($template->checkmarkValue('eol')) />
{{ trans('admin/hardware/form.eol_date') }}
</label>
<label class="form-control">
{{ Form::checkbox('order', '1', $template->checkmarkValue('order')) }}
<input type="checkbox" name="order" value="1" @checked($template->checkmarkValue('order')) />
{{ trans('admin/hardware/form.order') }}
</label>
<label class="form-control">
{{ Form::checkbox('supplier', '1', $template->checkmarkValue('supplier')) }}
<input type="checkbox" name="supplier" value="1" @checked($template->checkmarkValue('supplier')) />
{{ trans('general.suppliers') }}
</label>
<label class="form-control">
{{ Form::checkbox('location', '1', $template->checkmarkValue('location')) }}
<input type="checkbox" name="location" value="1" @checked($template->checkmarkValue('location')) />
{{ trans('general.location') }}
</label>
<label class="form-control" style="margin-left: 25px;">
{{ Form::checkbox('location_address', '1', $template->checkmarkValue('location_address')) }}
<input type="checkbox" name="location_address" value="1" @checked($template->checkmarkValue('location_address')) />
{{ trans('general.address') }}
</label>
<label class="form-control">
{{ Form::checkbox('rtd_location', '1', $template->checkmarkValue('rtd_location')) }}
<input type="checkbox" name="rtd_location" value="1" @checked($template->checkmarkValue('rtd_location')) />
{{ trans('admin/hardware/form.default_location') }}
</label>
<label class="form-control" style="margin-left: 25px;">
{{ Form::checkbox('rtd_location_address', '1', $template->checkmarkValue('rtd_location_address')) }}
<input type="checkbox" name="rtd_location_address" value="1" @checked($template->checkmarkValue('rtd_location_address')) />
{{ trans('general.address') }}
</label>
<label class="form-control">
{{ Form::checkbox('status', '1', $template->checkmarkValue('status')) }}
<input type="checkbox" name="status" value="1" @checked($template->checkmarkValue('status')) />
{{ trans('general.status') }}
</label>
<label class="form-control">
{{ Form::checkbox('warranty', '1', $template->checkmarkValue('warranty')) }}
<input type="checkbox" name="warranty" value="1" @checked($template->checkmarkValue('warranty')) />
{{ trans('admin/hardware/form.warranty') }}
</label>
<label class="form-control">
{{ Form::checkbox('depreciation', '1', $template->checkmarkValue('depreciation')) }}
<input type="checkbox" name="depreciation" value="1" @checked($template->checkmarkValue('depreciation')) />
{{ trans('general.depreciation') }}
</label>
<label class="form-control">
{{ Form::checkbox('checkout_date', '1', $template->checkmarkValue('checkout_date')) }}
<input type="checkbox" name="checkout_date" value="1" @checked($template->checkmarkValue('checkout_date')) />
{{ trans('admin/hardware/table.checkout_date') }}
</label>
<label class="form-control">
{{ Form::checkbox('checkin_date', '1', $template->checkmarkValue('checkin_date')) }}
<input type="checkbox" name="checkin_date" value="1" @checked($template->checkmarkValue('checkin_date')) />
{{ trans('admin/hardware/table.last_checkin_date') }}
</label>
<label class="form-control">
{{ Form::checkbox('expected_checkin', '1', $template->checkmarkValue('expected_checkin')) }}
<input type="checkbox" name="expected_checkin" value="1" @checked($template->checkmarkValue('expected_checkin')) />
{{ trans('admin/hardware/form.expected_checkin') }}
</label>
<label class="form-control">
{{ Form::checkbox('created_at', '1', $template->checkmarkValue('created_at')) }}
<input type="checkbox" name="created_at" value="1" @checked($template->checkmarkValue('created_at')) />
{{ trans('general.created_at') }}
</label>
<label class="form-control">
{{ Form::checkbox('updated_at', '1', $template->checkmarkValue('updated_at')) }}
<input type="checkbox" name="updated_at" value="1" @checked($template->checkmarkValue('updated_at')) />
{{ trans('general.updated_at') }}
</label>
<label class="form-control">
{{ Form::checkbox('deleted_at', '1', $template->checkmarkValue('deleted_at')) }}
<input type="checkbox" name="deleted_at" value="1" @checked($template->checkmarkValue('deleted_at')) />
{{ trans('general.deleted') }}
</label>
<label class="form-control">
{{ Form::checkbox('last_audit_date', '1', $template->checkmarkValue('last_audit_date')) }}
<input type="checkbox" name="last_audit_date" value="1" @checked($template->checkmarkValue('last_audit_date')) />
{{ trans('general.last_audit') }}
</label>
<label class="form-control">
{{ Form::checkbox('next_audit_date', '1', $template->checkmarkValue('next_audit_date')) }}
<input type="checkbox" name="next_audit_date" value="1" @checked($template->checkmarkValue('next_audit_date')) />
{{ trans('general.next_audit_date') }}
</label>
<label class="form-control">
{{ Form::checkbox('notes', '1', $template->checkmarkValue('notes')) }}
<input type="checkbox" name="notes" value="1" @checked($template->checkmarkValue('notes')) />
{{ trans('general.notes') }}
</label>
<label class="form-control" style="margin-left: 25px;">
{{ Form::checkbox('url', '1', $template->checkmarkValue('url')) }}
<input type="checkbox" name="url" value="1" @checked($template->checkmarkValue('url')) />
{{ trans('general.url') }}
</label>
@@ -248,64 +248,64 @@
<h2>{{ trans('general.checked_out_to_fields') }}: </h2>
<label class="form-control">
{{ Form::checkbox('assigned_to', '1', $template->checkmarkValue('assigned_to')) }}
<input type="checkbox" name="assigned_to" value="1" @checked($template->checkmarkValue('assigned_to')) />
{{ trans('admin/licenses/table.assigned_to') }}
</label>
<label class="form-control">
{{ Form::checkbox('username', '1', $template->checkmarkValue('username')) }}
<input type="checkbox" name="username" value="1" @checked($template->checkmarkValue('username')) />
{{ trans('admin/users/table.username') }}
</label>
<label class="form-control">
{{ Form::checkbox('employee_num', '1', $template->checkmarkValue('employee_num')) }}
<input type="checkbox" name="employee_num" value="1" @checked($template->checkmarkValue('employee_num')) />
{{ trans('general.employee_number') }}
</label>
<label class="form-control">
{{ Form::checkbox('manager', '1', $template->checkmarkValue('manager')) }}
<input type="checkbox" name="manager" value="1" @checked($template->checkmarkValue('manager')) />
{{ trans('admin/users/table.manager') }}
</label>
<label class="form-control">
{{ Form::checkbox('department', '1', $template->checkmarkValue('department')) }}
<input type="checkbox" name="department" value="1" @checked($template->checkmarkValue('department')) />
{{ trans('general.department') }}
</label>
<label class="form-control">
{{ Form::checkbox('title', '1', $template->checkmarkValue('title')) }}
<input type="checkbox" name="title" value="1" @checked($template->checkmarkValue('title')) />
{{ trans('admin/users/table.title') }}
</label>
<!-- new -->
<label class="form-control">
{{ Form::checkbox('phone', '1', $template->checkmarkValue('phone')) }}
<input type="checkbox" name="phone" value="1" @checked($template->checkmarkValue('phone')) />
{{ trans('admin/users/table.phone') }}
</label>
<label class="form-control">
{{ Form::checkbox('user_address', '1', $template->checkmarkValue('user_address')) }}
<input type="checkbox" name="user_address" value="1" @checked($template->checkmarkValue('user_address')) />
{{ trans('general.address') }}
</label>
<label class="form-control">
{{Form::checkbox('user_city', '1', $template->checkmarkValue('user_city'))}}
<input type="checkbox" name="user_city" value="1" @checked($template->checkmarkValue('user_city')) />
{{ trans('general.city') }}
</label>
<label class="form-control">
{{Form::checkbox('user_state', '1', $template->checkmarkValue('user_state'))}}
<input type="checkbox" name="user_state" value="1" @checked($template->checkmarkValue('user_state')) />
{{ trans('general.state') }}
</label>
<label class="form-control">
{{Form::checkbox('user_country', '1', $template->checkmarkValue('user_country'))}}
<input type="checkbox" name="user_country" value="1" @checked($template->checkmarkValue('user_country')) />
{{ trans('general.country') }}
</label>
<label class="form-control">
{{Form::checkbox('user_zip', '1', $template->checkmarkValue('user_zip'))}}
<input type="checkbox" name="user_zip" value="1" @checked($template->checkmarkValue('user_zip')) />
{{ trans('general.zip') }}
</label>
@@ -318,7 +318,7 @@
@foreach ($customfields as $customfield)
<label class="form-control">
{{ Form::checkbox($customfield->db_column_name(), '1', $template->checkmarkValue($customfield->db_column_name())) }}
<input type="checkbox" name="{{ $customfield->db_column_name() }}" value="1" @checked($template->checkmarkValue($customfield->db_column_name())) />
{{ $customfield->name }}
</label>
@@ -549,13 +549,13 @@
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('exclude_archived', '1', $template->checkmarkValue('exclude_archived', '0')) }}
<input type="checkbox" name="exclude_archived" value="1" @checked($template->checkmarkValue('exclude_archived', '0')) />
{{ trans('general.exclude_archived') }}
</label>
</div>
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('use_bom', '1', $template->checkmarkValue('use_bom', '0')) }}
<input type="checkbox" name="use_bom" value="1" @checked($template->checkmarkValue('use_bom', '0')) />
{{ trans('general.bom_remark') }}
</label>
</div>

View File

@@ -68,7 +68,7 @@
@foreach ($assetsForReport as $item)
@if ($item['assetItem'])
<tr @if($item['acceptance']->trashed()) style="text-decoration: line-through" @endif>
<td>{{ $item['acceptance']->created_at }}</td>
<td>{{ Helper::getFormattedDateObject($item['acceptance']->created_at, 'datetime', false) }}</td>
<td>{{ ($item['assetItem']->company) ? $item['assetItem']->company->name : '' }}</td>
<td>{!! $item['assetItem']->model->category->present()->nameUrl() !!}</td>
<td>{!! $item['assetItem']->present()->modelUrl() !!}</td>
@@ -79,13 +79,18 @@
<nobr>
@if(!$item['acceptance']->trashed())
<form method="post" class="white-space: nowrap;" action="{{ route('reports/unaccepted_assets_sent_reminder') }}">
@if ($item['acceptance']->assignedTo)
@if (($item['acceptance']->assignedTo) && ($item['acceptance']->assignedTo->email))
@csrf
<input type="hidden" name="acceptance_id" value="{{ $item['acceptance']->id }}">
<button class="btn btn-sm btn-warning" data-tooltip="true" data-title="{{ trans('admin/reports/general.send_reminder') }}">
<i class="fa fa-repeat" aria-hidden="true"></i>
</button>
@else
<span data-tooltip="true" data-title="{{ trans('admin/reports/general.cannot_send_reminder') }}">
<a class="btn btn-sm btn-warning disabled" href="#">
<i class="fa fa-repeat" aria-hidden="true"></i>
</a>
</span>
@endif
<a href="{{ route('reports/unaccepted_assets_delete', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm btn-danger delete-asset" data-tooltip="true" data-toggle="modal" data-content="{{ trans('general.delete_confirm', ['item' =>trans('admin/reports/general.acceptance_request')]) }}" data-title="{{ trans('general.delete') }}" onClick="return false;"><i class="fa fa-trash"></i></a>
</form>

View File

@@ -45,7 +45,7 @@
<div class="form-group {{ $errors->has('alerts_enabled') ? 'error' : '' }}">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('alerts_enabled', '1', old('alerts_enabled', $setting->alerts_enabled)) }}
<input type="checkbox" name="alerts_enabled" value="1" @checked(old('alerts_enabled', $setting->alerts_enabled))>
{{ trans('admin/settings/general.alerts_enabled') }}
</label>
</div>
@@ -55,7 +55,7 @@
<div class="form-group {{ $errors->has('show_alerts_in_menu') ? 'error' : '' }}">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('show_alerts_in_menu', '1', old('show_alerts_in_menu', $setting->show_alerts_in_menu)) }}
<input type="checkbox" name="show_alerts_in_menu" value="1" @checked(old('show_alerts_in_menu', $setting->show_alerts_in_menu))>
{{ trans('admin/settings/general.show_alerts_in_menu') }}
</label>
</div>

View File

@@ -47,7 +47,7 @@
</div>
<div class="col-md-7">
<label class="form-control">
{{ Form::checkbox('auto_increment_assets', '1', old('auto_increment_assets', $setting->auto_increment_assets),array('aria-label'=>'auto_increment_assets')) }}
<input type="checkbox" name="auto_increment_assets" value="1" @checked(old('auto_increment_assets', $setting->auto_increment_assets)) aria-label="auto_increment_assets">
{{ trans('admin/settings/general.enabled') }}
</label>
</div>

View File

@@ -68,7 +68,9 @@
</div>
</div>
@php
$optionTypes = trans('admin/settings/general.logo_option_types');
@endphp
<!-- Branding -->
<div class="form-group {{ $errors->has('brand') ? 'error' : '' }}">
@@ -76,8 +78,13 @@
<label for="brand">{{ trans('admin/settings/general.web_brand') }}</label>
</div>
<div class="col-md-9">
{!! Form::select('brand', array('1'=>'Text','2'=>'Logo','3'=>'Logo + Text'), old('brand', $setting->brand), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!}
{!! $errors->first('brand', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<select name="brand" id="brand" class="form-control select2 minimumResultsForSearch" style="width: 150px;">
@foreach($optionTypes as $value => $label)
<option value="{{ $value }}" {{ old('brand', $setting->brand) == $value ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
</div>
@@ -135,7 +142,7 @@
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('restore_default_avatar', '1', old('restore_default_avatar', $setting->restore_default_avatar)) }}
<input type="checkbox" name="restore_default_avatar" value="1" @checked(old('restore_default_avatar', $setting->restore_default_avatar)) />
<span>{!! trans('admin/settings/general.restore_default_avatar', ['default_avatar'=> Storage::disk('public')->url('default.png')]) !!}</span>
</label>
<p class="help-block">
@@ -152,7 +159,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('load_remote', '1', old('load_remote', $setting->load_remote)) }}
<input type="checkbox" name="load_remote" value="1" @checked(old('load_remote', $setting->load_remote)) />
{{ trans('general.yes') }}
{!! $errors->first('load_remote', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</label>
@@ -172,7 +179,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('logo_print_assets', '1', old('logo_print_assets', $setting->logo_print_assets),array('aria-label'=>'logo_print_assets')) }}
<input type="checkbox" name="logo_print_assets" value="1" @checked(old('logo_print_assets', $setting->logo_print_assets)) aria-label="logo_print_assets"/>
{{ trans('admin/settings/general.logo_print_assets_help') }}
</label>
@@ -187,7 +194,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('show_url_in_emails', '1', old('show_url_in_emails', $setting->show_url_in_emails),array('aria-label'=>'show_url_in_emails')) }}
<input type="checkbox" name="show_url_in_emails" value="1" @checked(old('show_url_in_emails', $setting->show_url_in_emails)) aria-label="show_url_in_emails" />
{{ trans('general.yes') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.show_url_in_emails_help_text') }}</p>
@@ -228,7 +235,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('allow_user_skin', '1', old('allow_user_skin', $setting->allow_user_skin)) }}
<input type="checkbox" name="allow_user_skin" value="1" @checked(old('allow_user_skin', $setting->allow_user_skin))/>
{{ trans('general.yes') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.allow_user_skin_help_text') }}</p>
@@ -272,10 +279,10 @@
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{!! Form::select('support_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('support_footer', $setting->support_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!}
{!! Form::select('support_footer', array('on'=>trans('admin/settings/general.enabled'),'off'=>trans('admin/settings/general.two_factor_disabled'),'admin'=>trans('admin/settings/general.super_admin_only')), old('support_footer', $setting->support_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!}
<p class="text-warning"><i class="fas fa-lock"></i> {{ trans('general.feature_disabled') }}</p>
@else
{!! Form::select('support_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('support_footer', $setting->support_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!}
{!! Form::select('support_footer', array('on'=>trans('admin/settings/general.enabled'),'off'=>trans('admin/settings/general.two_factor_disabled'),'admin'=>trans('admin/settings/general.super_admin_only')), old('support_footer', $setting->support_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!}
@endif
@@ -291,10 +298,10 @@
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{!! Form::select('version_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('version_footer', $setting->version_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!}
{!! Form::select('version_footer', array('on'=>trans('admin/settings/general.enabled'),'off'=>trans('admin/settings/general.two_factor_disabled'),'admin'=>trans('admin/settings/general.super_admin_only')), old('version_footer', $setting->version_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!}
<p class="text-warning"><i class="fas fa-lock"></i> {{ trans('general.feature_disabled') }}</p>
@else
{!! Form::select('version_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('version_footer', $setting->version_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!}
{!! Form::select('version_footer', array('on'=>trans('admin/settings/general.enabled'),'off'=>trans('admin/settings/general.two_factor_disabled'),'admin'=>trans('admin/settings/general.super_admin_only')), old('version_footer', $setting->version_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!}
@endif
<p class="help-block">{{ trans('admin/settings/general.version_footer_help') }}</p>

View File

@@ -45,7 +45,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('full_multiple_companies_support', '1', old('full_multiple_companies_support', $setting->full_multiple_companies_support),array('aria-label'=>'full_multiple_companies_support')) }}
<input type="checkbox" name="full_multiple_companies_support" value="1" @checked(old('full_multiple_companies_support', $setting->full_multiple_companies_support)) aria-label="full_multiple_companies_support" />
{{ trans('admin/settings/general.full_multiple_companies_support_text') }}
</label>
{!! $errors->first('full_multiple_companies_support', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@@ -64,7 +64,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('require_accept_signature', '1', old('require_accept_signature', $setting->require_accept_signature)) }}
<input type="checkbox" name="require_accept_signature" value="1" @checked(old('require_accept_signature', $setting->require_accept_signature)) />
{{ trans('general.yes') }}
</label>
{!! $errors->first('require_accept_signature', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@@ -136,7 +136,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('show_images_in_email', '1', old('show_images_in_email', $setting->show_images_in_email)) }}
<input type="checkbox" name="show_images_in_email" value="1" @checked(old('show_images_in_email', $setting->show_images_in_email)) />
{{ trans('general.yes') }}
{!! $errors->first('show_images_in_email', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</label>
@@ -152,7 +152,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('unique_serial', '1', old('unique_serial', $setting->unique_serial),array('class' => 'minimal')) }}
<input type="checkbox" name="unique_serial" value="1" @checked(old('unique_serial', $setting->unique_serial)) />
{{ trans('general.yes') }}
{!! $errors->first('unique_serial', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</label>
@@ -245,11 +245,11 @@
<div class="col-md-9">
@if (config('app.lock_passwords'))
<textarea class="form-control disabled" name="login_note" placeholder="If you do not have a login or have found a device belonging to this company, please call technical support at 888-555-1212. Thank you." rows="2" aria-label="login_note" readonly>{{ old('login_note', $setting->login_note) }}</textarea>
<textarea class="form-control disabled" name="login_note" placeholder="{{trans('admin/settings/general.login_note_placeholder')}}" rows="2" aria-label="login_note" readonly>{{ old('login_note', $setting->login_note) }}</textarea>
{!! $errors->first('login_note', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
<p class="text-warning"><i class="fas fa-lock"></i> {{ trans('general.feature_disabled') }}</p>
@else
<textarea class="form-control" name="login_note" aria-label="login_note" placeholder="If you do not have a login or have found a device belonging to this company, please call technical support at 888-555-1212. Thank you." rows="2">{{ old('login_note', $setting->login_note) }}</textarea>
<textarea class="form-control" name="login_note" aria-label="login_note" placeholder="{{trans('admin/settings/general.login_note_placeholder')}}" rows="2">{{ old('login_note', $setting->login_note) }}</textarea>
{!! $errors->first('login_note', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@endif
<p class="help-block">{!! trans('admin/settings/general.login_note_help') !!}</p>
@@ -259,7 +259,7 @@
<!-- Mail test -->
<div class="form-group">
<div class="col-md-3">
<label for="login_note">Test Mail</label>
<label for="login_note">{{trans('admin/settings/general.test_mail')}}</label>
</div>
<div class="col-md-9" id="mailtestrow">
<a class="btn btn-default btn-sm pull-left" id="mailtest" style="margin-right: 10px;">
@@ -311,7 +311,7 @@
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('show_archived_in_list', '1', old('show_archived_in_list', $setting->show_archived_in_list),array('aria-label'=>'show_archived_in_list')) }}
<input type="checkbox" name="show_archived_in_list" value="1" @checked(old('show_archived_in_list', $setting->show_archived_in_list)) aria-label="show_archived_in_list" />
{{ trans('admin/settings/general.show_archived_in_list_text') }}
</label>
{!! $errors->first('show_archived_in_list', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
@@ -325,7 +325,7 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('show_assigned_assets', '1', old('show_assigned_assets', $setting->show_assigned_assets),array('class' => 'minimal')) }}
<input type="checkbox" name="show_assigned_assets" value="1" @checked(old('show_assigned_assets', $setting->show_assigned_assets)) />
{{ trans('general.yes') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.show_assigned_assets_help') }}</p>
@@ -340,15 +340,19 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('show_in_model_list[]', 'image', old('show_in_model_list', $snipeSettings->modellistCheckedValue('image')),array('class' => 'minimal', 'aria-label'=>'show_in_model_list' )) }} {{ trans('general.image') }}
<input type="checkbox" name="show_in_model_list[]" value="image" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('image'))) aria-label="show_in_model_list"/>
{{ trans('general.image') }}
</label>
<label class="form-control">
{{ Form::checkbox('show_in_model_list[]', 'category', old('show_in_model_list', $snipeSettings->modellistCheckedValue('category')),array('class' => 'minimal', 'aria-label'=>'show_in_model_list' )) }} {{ trans('general.category') }}
<input type="checkbox" name="show_in_model_list[]" value="category" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('category'))) aria-label="show_in_model_list"/>
{{ trans('general.category') }}
</label>
<label class="form-control">
{{ Form::checkbox('show_in_model_list[]', 'manufacturer', old('show_in_model_list', $snipeSettings->modellistCheckedValue('manufacturer')),array('class' => 'minimal', 'aria-label'=>'show_in_model_list' )) }} {{ trans('general.manufacturer') }} </label>
<input type="checkbox" name="show_in_model_list[]" value="manufacturer" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('manufacturer'))) aria-label="show_in_model_list"/>
{{ trans('general.manufacturer') }} </label>
<label class="form-control">
{{ Form::checkbox('show_in_model_list[]', 'model_number', old('show_in_model_list', $snipeSettings->modellistCheckedValue('model_number')),array('class' => 'minimal', 'aria-label'=>'show_in_model_list' )) }} {{ trans('general.model_no') }}
<input type="checkbox" name="show_in_model_list[]" value="model_number" @checked(old('show_in_model_list', $snipeSettings->modellistCheckedValue('model_number'))) aria-label="show_in_model_list"/>
{{ trans('general.model_no') }}
</label>
</div>
</div>
@@ -370,13 +374,13 @@
<!-- Depreciation method -->
<div class="form-group {{ $errors->has('depreciation_method') ? 'error' : '' }}">
<div class="col-md-3">
<label for="depreciation_method">{{ trans('Depreciation method') }}</label>
<label for="depreciation_method">{{ trans('admin/depreciations/general.depreciation_method') }}</label>
</div>
<div class="col-md-9">
{{ Form::select('depreciation_method', array(
'default' => 'Linear (default)',
'half_1' => 'Half-year convention, always applied',
'half_2' => 'Half-year convention, applied with condition',
'default' => trans('admin/depreciations/general.linear_depreciation'),
'half_1' => trans('admin/depreciations/general.half_1'),
'half_2' => trans('admin/depreciations/general.half_2'),
), old('username_format', $setting->depreciation_method), ['class' =>'select2', 'style' => 'width: 80%']) }}
</div>
</div>

View File

@@ -54,7 +54,7 @@
<div class="col-md-8 col-md-offset-3">
<label class="form-control{{ (config('app.lock_passwords')===true) ? ' form-control--disabled': '' }}">
<span class="sr-only">{{ trans('admin/settings/general.pwd_secure_uncommon') }}</span>
{{ Form::checkbox('google_login', '1', old('google_login', $setting->google_login),array('aria-label'=>'google_login', (config('app.lock_passwords')===true) ? 'disabled': '')) }}
<input type="checkbox" name="google_login" value="1" @checked(old('google_login', $setting->google_login)) @disabled(config('app.lock_passwords')) aria-label="google_login">
{{ trans('admin/settings/general.enable_google_login') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.enable_google_login_help') }}</p>

View File

@@ -144,7 +144,7 @@
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('alt_barcode_enabled', '1', old('alt_barcode_enabled', $setting->alt_barcode_enabled),array( 'aria-label'=>'alt_barcode_enabled')) }}
<input type="checkbox" name="alt_barcode_enabled" value="1" @checked(old('alt_barcode_enabled', $setting->alt_barcode_enabled)) aria-label="alt_barcode_enabled"/>
{{ trans('admin/settings/general.display_alt_barcode') }}
</label>
</div>
@@ -189,7 +189,7 @@
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('qr_code', '1', old('qr_code', $setting->qr_code),array('aria-label'=>'qr_code')) }}
<input type="checkbox" name="qr_code" value="1" @checked(old('qr_code', $setting->qr_code)) aria-label="qr_code" />
{{ trans('admin/settings/general.display_qr') }}
</label>
</div>
@@ -480,23 +480,23 @@
</div>
<div class="col-md-9">
<label class="form-control">
{{ Form::checkbox('labels_display_name', '1', old('labels_display_name', $setting->labels_display_name),['class' => 'minimal', 'aria-label'=>'labels_display_name']) }}
<input type="checkbox" name="labels_display_name" value="1" @checked(old('labels_display_name', $setting->labels_display_name)) aria-label="labels_display_name" />
{{ trans('admin/hardware/form.name') }}
</label>
<label class="form-control">
{{ Form::checkbox('labels_display_serial', '1', old('labels_display_serial', $setting->labels_display_serial),['class' => 'minimal', 'aria-label'=>'labels_display_serial']) }}
<input type="checkbox" name="labels_display_serial" value="1" @checked(old('labels_display_serial', $setting->labels_display_serial)) aria-label="labels_display_serial" />
{{ trans('admin/hardware/form.serial') }}
</label>
<label class="form-control">
{{ Form::checkbox('labels_display_tag', '1', old('labels_display_tag', $setting->labels_display_tag),['class' => 'minimal', 'aria-label'=>'labels_display_tag']) }}
<input type="checkbox" name="labels_display_tag" value="1" @checked(old('labels_display_tag', $setting->labels_display_tag)) aria-label="labels_display_tag" />
{{ trans('admin/hardware/form.tag') }}
</label>
<label class="form-control">
{{ Form::checkbox('labels_display_model', '1', old('labels_display_model', $setting->labels_display_model),['class' => 'minimal', 'aria-label'=>'labels_display_model']) }}
<input type="checkbox" name="labels_display_model" value="1" @checked(old('labels_display_model', $setting->labels_display_model)) aria-label="labels_display_model" />
{{ trans('admin/hardware/form.model') }}
</label>
<label class="form-control">
{{ Form::checkbox('labels_display_company_name', '1', old('labels_display_company_name', $setting->labels_display_company_name),['class' => 'minimal', 'aria-label'=>'labels_display_company_name']) }}
<input type="checkbox" name="labels_display_company_name" value="1" @checked(old('labels_display_company_name', $setting->labels_display_company_name)) aria-label="labels_display_company_name"/>
{{ trans('admin/companies/table.name') }}
</label>
</div> <!--/.col-md-9-->

View File

@@ -75,7 +75,7 @@
<div class="col-md-8">
<label class="form-control">
{{ Form::checkbox('ldap_enabled', '1', old('ldap_enabled', $setting->ldap_enabled)) }}
<input type="checkbox" name="ldap_enabled" value="1" id="ldap_enabled" @checked(old('ldap_enabled', $setting->ldap_enabled)) />
{{ trans('admin/settings/general.ldap_enabled') }}
</label>
@@ -96,7 +96,7 @@
</div>
<div class="col-md-8">
<label class="form-control">
{{ Form::checkbox('is_ad', '1', old('is_ad', $setting->is_ad)) }}
<input type="checkbox" name="is_ad" value="1" id="is_ad" @checked(old('is_ad', $setting->is_ad))/>
{{ trans('admin/settings/general.is_ad') }}
</label>
@error('is_ad')
@@ -122,7 +122,7 @@
</div>
<div class="col-md-8">
<label class="form-control">
{{ Form::checkbox('ldap_pw_sync', '1', old('ldap_pw_sync', $setting->ldap_pw_sync)) }}
<input type="checkbox" name="ldap_pw_sync" value="1" id="ldap_pw_sync" @checked(old('ldap_pw_sync', $setting->ldap_pw_sync)) />
{{ trans('general.yes') }}
</label>
@@ -255,7 +255,7 @@
</div>
<div class="col-md-8">
<label class="form-control">
{{ Form::checkbox('ldap_tls', '1', old('ldap_tls', $setting->ldap_tls)) }}
<input type="checkbox" name="ldap_tls" value="1" id="ldap_tls" @checked(old('ldap_tls', $setting->ldap_tls)) />
{{ trans('admin/settings/general.ldap_tls_help') }}
</label>
@error('ldap_tls')
@@ -281,7 +281,7 @@
</div>
<div class="col-md-8">
<label class="form-control">
{{ Form::checkbox('ldap_server_cert_ignore', '1', old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore)) }}
<input type="checkbox" name="ldap_server_cert_ignore" value="1" id="ldap_server_cert_ignore" @checked(old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore)) />
{{ trans('admin/settings/general.ldap_server_cert_ignore') }}
</label>
@error('ldap_server_cert_ignore')
@@ -554,6 +554,32 @@
</div>
</div>
<!-- LDAP invert active flag -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('ldap_invert_active_flag', trans('admin/settings/general.ldap_invert_active_flag')) }}
</div>
<div class="col-md-8">
<label class="form-control">
<input type="checkbox" name="ldap_invert_active_flag" value="1" id="ldap_invert_active_flag" @checked(old('ldap_invert_active_flag', $setting->ldap_invert_active_flag)) />
<p class="help-block">{!! trans('admin/settings/general.ldap_invert_active_flag_help') !!}</p>
</label>
@error('ldap_invert_active_flag')
<span class="alert-msg">
<x-icon type="x" />
{{ $message }}
</span>
@enderror
@if (config('app.lock_passwords')===true)
<p class="text-warning">
<x-icon type="locked" />
{{ trans('general.feature_disabled') }}
</p>
@endif
</div>
</div>
<!-- LDAP emp number -->
<div class="form-group {{ $errors->has('ldap_emp_num') ? 'error' : '' }}">
<div class="col-md-3">

View File

@@ -52,7 +52,7 @@
<div class="col-md-9">
<label class="form-control{{ config('app.lock_passwords') === true ? ' form-control--disabled': '' }}">
{{ Form::checkbox('saml_enabled', '1', old('saml_enabled', $setting->saml_enabled), ['class' => config('app.lock_passwords') === true ? 'disabled ': '', config('app.lock_passwords') === true ? 'disabled ': '', ]) }}
<input type="checkbox" name="saml_enabled" value="1" @checked(old('saml_enabled', $setting->saml_enabled)) @disabled(config('app.lock_passwords')) @class(['disabled' => config('app.lock_passwords')])/>
{{ trans('admin/settings/general.saml_enabled') }}
</label>
@@ -146,7 +146,7 @@
</div>
<div class="col-md-9">
<label class="form-control{{ config('app.lock_passwords') === true ? ' form-control--disabled': '' }}">
{{ Form::checkbox('saml_forcelogin', '1', old('saml_forcelogin', $setting->saml_forcelogin),['class' => $setting->demoMode, $setting->demoMode]) }}
<input type="checkbox" name="saml_forcelogin" value="1" @checked(old('saml_forcelogin', $setting->saml_forcelogin)) @disabled(config('app.lock_passwords')) @class(['disabled' => config('app.lock_passwords')]) />
{{ trans('admin/settings/general.saml_forcelogin') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.saml_forcelogin_help') }}</p>
@@ -162,7 +162,7 @@
</div>
<div class="col-md-9">
<label class="form-control{{ config('app.lock_passwords') === true ? ' form-control--disabled': '' }}">
{{ Form::checkbox('saml_slo', '1', old('saml_slo', $setting->saml_slo),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
<input type="checkbox" name="saml_slo" value="1" @checked(old('saml_slo', $setting->saml_slo)) @disabled(config('app.lock_passwords')) @class(['minimal', 'disabled' => config('app.lock_passwords')])/>
{{ trans('admin/settings/general.saml_slo') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.saml_slo_help') }}</p>

View File

@@ -83,27 +83,27 @@
<div class="col-md-9">
<label class="form-control">
<span class="sr-only">{{ trans('admin/settings/general.pwd_secure_uncommon') }}</span>
{{ Form::checkbox('pwd_secure_uncommon', '1', old('pwd_secure_uncommon', $setting->pwd_secure_uncommon),array( 'aria-label'=>'pwd_secure_uncommon')) }}
<input type="checkbox" name="pwd_secure_uncommon" value="1" @checked(old('pwd_secure_uncommon', $setting->pwd_secure_uncommon)) aria-label="pwd_secure_uncommon"/>
{{ trans('admin/settings/general.pwd_secure_uncommon') }}
</label>
<label class="form-control">
{{ Form::checkbox("pwd_secure_complexity['disallow_same_pwd_as_user_fields']", 'disallow_same_pwd_as_user_fields', old('disallow_same_pwd_as_user_fields', strpos($setting->pwd_secure_complexity, 'disallow_same_pwd_as_user_fields')!==false), array('aria-label'=>'pwd_secure_complexity')) }}
<input type="checkbox" name="pwd_secure_complexity['disallow_same_pwd_as_user_fields']" value="disallow_same_pwd_as_user_fields" @checked(old('disallow_same_pwd_as_user_fields', strpos($setting->pwd_secure_complexity, 'disallow_same_pwd_as_user_fields')!==false)) aria-label="pwd_secure_complexity"/>
{{ trans('admin/settings/general.pwd_secure_complexity_disallow_same_pwd_as_user_fields') }}
</label>
<label class="form-control">
{{ Form::checkbox("pwd_secure_complexity['letters']", 'letters', old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'letters')!==false), array('aria-label'=>'pwd_secure_complexity')) }}
<input type="checkbox" name="pwd_secure_complexity['letters']" value="letters" @checked(old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'letters')!==false)) aria-label="pwd_secure_complexity"/>
{{ trans('admin/settings/general.pwd_secure_complexity_letters') }}
</label>
<label class="form-control">
{{ Form::checkbox("pwd_secure_complexity['numbers']", 'numbers', old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'numbers')!==false), array('aria-label'=>'pwd_secure_complexity')) }}
<input type="checkbox" name="pwd_secure_complexity['numbers']" value="numbers" @checked(old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'numbers')!==false)) aria-label="pwd_secure_complexity"/>
{{ trans('admin/settings/general.pwd_secure_complexity_numbers') }}
</label>
<label class="form-control">
{{ Form::checkbox("pwd_secure_complexity['symbols']", 'symbols', old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'symbols')!==false), array('aria-label'=>'pwd_secure_complexity')) }}
<input type="checkbox" name="pwd_secure_complexity['symbols']" value="symbols" @checked(old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'symbols')!==false)) aria-label="pwd_secure_complexity"/>
{{ trans('admin/settings/general.pwd_secure_complexity_symbols') }}
</label>
<label class="form-control">
{{ Form::checkbox("pwd_secure_complexity['case_diff']", 'case_diff', old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'case_diff')!==false), array('aria-label'=>'pwd_secure_complexity')) }}
<input type="checkbox" name="pwd_secure_complexity['case_diff']" value="case_diff" @checked(old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'case_diff')!==false)) aria-label="pwd_secure_complexity"/>
{{ trans('admin/settings/general.pwd_secure_complexity_case_diff') }}
</label>
@@ -129,7 +129,7 @@
<p class="text-warning"><i class="fas fa-lock"></i> {{ trans('general.feature_disabled') }}</p>
@else
<label class="form-control">
{{ Form::checkbox('login_remote_user_enabled', '1', old('login_remote_user_enabled', $setting->login_remote_user_enabled),array('aria-label'=>'login_remote_user')) }}
<input type="checkbox" name="login_remote_user_enabled" value="1" @checked(old('login_remote_user_enabled', $setting->login_remote_user_enabled)) aria-label="login_remote_user"/>
<label for="login_remote_user_enabled">{{ trans('admin/settings/general.login_remote_user_enabled_text') }}</label>
</label>
@@ -158,7 +158,7 @@
<!-- Disable other logins mechanism -->
<label class="form-control">
{{ Form::checkbox('login_common_disabled', '1', old('login_common_disabled', $setting->login_common_disabled),array('aria-label'=>'login_common_disabled')) }}
<input type="checkbox" name="login_common_disabled" value="1" @checked(old('login_common_disabled', $setting->login_common_disabled)) aria-label="login_common_disabled"/>
{{ trans('admin/settings/general.login_common_disabled_text') }}
</label>
{!! $errors->first('login_common_disabled', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}

View File

@@ -42,7 +42,7 @@
<div class="form-group">
<div class=" col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('null_department_id', '1', false) }}
<input type="checkbox" name="null_department_id" value="1" />
{{ trans_choice('general.set_users_field_to_null', count($users), ['field' => trans('general.department'), 'user_count' => count($users)]) }}
</label>
</div>
@@ -55,7 +55,7 @@
<div class="form-group">
<div class=" col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('null_location_id', '1', false) }}
<input type="checkbox" name="null_location_id" value="1" />
{{ trans_choice('general.set_users_field_to_null', count($users), ['field' => trans('general.location'), 'user_count' => count($users)]) }}
</label>
</div>
@@ -69,7 +69,7 @@
<div class="form-group">
<div class=" col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('null_company_id', '1', false) }}
<input type="checkbox" name="null_company_id" value="1" />
{{ trans_choice('general.set_users_field_to_null', count($users), ['field' => trans('general.company'), 'user_count' => count($users)]) }}
</label>
</div>
@@ -83,7 +83,7 @@
<div class="form-group">
<div class=" col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('null_manager_id', '1', false) }}
<input type="checkbox" name="null_manager_id" value="1" />
{{ trans_choice('general.set_users_field_to_null', count($users), ['field' => trans('admin/users/table.manager'), 'user_count' => count($users)]) }}
</label>
</div>
@@ -102,7 +102,7 @@
<div class="form-group">
<div class=" col-md-9 col-md-offset-3">
<label class="form-control">
{{ Form::checkbox('null_locale', '1', false) }}
<input type="checkbox" name="null_locale" value="1" />
{{ trans_choice('general.set_users_field_to_null', count($users), ['field' => trans('general.language'), 'user_count' => count($users)]) }}
</label>
</div>
@@ -246,7 +246,7 @@
</div>
<div class="col-md-5">
<label class="form-control">
{{ Form::checkbox('null_start_date', '1', false) }}
<input type="checkbox" name="null_start_date" value="1" />
{{ trans_choice('general.set_to_null', count($users),['selection_count' => count($users)]) }}
</label>
</div>
@@ -264,7 +264,7 @@
</div>
<div class="col-md-5">
<label class="form-control">
{{ Form::checkbox('null_end_date', '1', false) }}
<input type="checkbox" name="null_end_date" value="1" />
{{ trans_choice('general.set_to_null', count($users),['selection_count' => count($users)]) }}
</label>
</div>

View File

@@ -213,7 +213,7 @@
@elseif ($user->id === Auth::user()->id)
<!-- disallow the user from editing their own login status -->
<label class="form-control form-control--disabled">
{{ Form::checkbox('activated', '1', old('activated', $user->activated), ['disabled' => true, 'checked'=> 'checked', 'aria-label'=>'update_real_loc']) }}
<input type="checkbox" name="activated" value="1" checked disabled aria-label="activated">
{{ trans('admin/users/general.activated_help_text') }}
</label>
<p class="text-warning">{{ trans('admin/users/general.activated_disabled_help_text') }}</p>
@@ -259,7 +259,7 @@
<div class="col-md-8 col-md-offset-3">
<label class="form-control form-control--disabled">
{{ Form::checkbox('email_user', '1', old('email_user'), ['id' => "email_user_checkbox", 'aria-label'=>'email_user']) }}
<input type="checkbox" name="email_user" value="1" id="email_user_checkbox" @checked(old('email_user')) aria-label="email_user">
{{ trans('admin/users/general.email_user_creds_on_create') }}
</label>

View File

@@ -126,7 +126,7 @@ Route::group(
// Redirect old legacy /asset_id/view urls to the resource route version
Route::get('{assetId}/view', function ($assetId) {
return redirect()->route('hardware.show', ['hardware' => $assetId]);
return redirect()->route('hardware.show', $assetId);
});
Route::get('{asset}/qr_code',

View File

@@ -428,7 +428,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -465,7 +465,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -502,7 +502,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -543,7 +543,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php php php-mcrypt php-curl php-mysql php-gd php-ldap php-zip php-mbstring php-xml php-bcmath curl git unzip"
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php php php-curl php-mysql php-gd php-ldap php-zip php-mbstring php-xml php-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -584,7 +584,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -628,7 +628,7 @@ case $distro in
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php8.28.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="cron mariadb-server mariadb-client apache2 libapache2-mod-php8.28.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
@@ -688,12 +688,11 @@ EOL
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-mcrypt php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
PACKAGES="mariadb-server mariadb-client apache2 libapache2-mod-php8.2 php8.2 php8.2-curl php8.2-mysql php8.2-gd php8.2-ldap php8.2-zip php8.2-mbstring php8.2-xml php8.2-bcmath curl git unzip"
install_packages
echo "* Configuring Apache."
create_virtualhost
log "phpenmod mcrypt"
log "phpenmod mbstring"
log "a2enmod rewrite"
log "a2ensite $APP_NAME.conf"
@@ -728,7 +727,7 @@ EOL
amazon-linux-extras install -y php8.2
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-mcrypt php-ldap php-json php-simplexml php-process php-zip php-sodium"
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-ldap php-json php-simplexml php-process php-zip php-sodium"
install_packages
echo "* Configuring Apache."
@@ -769,7 +768,7 @@ EOL
log "yum-config-manager --enable remi-php82"
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-mcrypt php-ldap php-json php-simplexml php-process php-zip"
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-ldap php-json php-simplexml php-process php-zip"
install_packages
echo "* Configuring Apache."
@@ -812,7 +811,7 @@ EOL
progress
echo "* Installing Apache httpd, PHP, MariaDB and other requirements."
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-mcrypt php-ldap php-json php-simplexml php-process php-zip"
PACKAGES="httpd mariadb-server git unzip php php-mysqlnd php-bcmath php-embedded php-gd php-mbstring php-ldap php-json php-simplexml php-process php-zip"
install_packages
echo "* Configuring Apache."

View File

@@ -0,0 +1,36 @@
<?php
namespace Tests\Feature\Assets\Ui;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\User;
use Tests\TestCase;
class AssetLabelTest extends TestCase
{
public function testUserWithPermissionsCanAccessPage()
{
$assets = Asset::factory()->count(20)->create();
$id_array = $assets->pluck('id')->toArray();
$this->actingAs(User::factory()->viewAssets()->create())->post('/hardware/bulkedit', [
'ids' => $id_array,
'bulk_actions' => 'labels',
])->assertStatus(200);
}
public function testRedirectOfNoAssetsSelected()
{
$id_array = [];
$this->actingAs(User::factory()->viewAssets()->create())
->from(route('hardware.index'))
->post('/hardware/bulkedit', [
'ids' => $id_array,
'bulk_actions' => 'Labels',
])->assertStatus(302)
->assertRedirect(route('hardware.index'));
}
}

View File

@@ -43,9 +43,13 @@ class SendAcceptanceReminderTest extends TestCase
CheckoutAcceptance::factory()->pending()->create([
'assigned_to_id' => $userA->id,
]);
$headers = ['ID', 'Name'];
$rows = [
[$userA->id, $userA->present()->fullName()],
];
$this->artisan('snipeit:acceptance-reminder')
->expectsOutput($userA->present()->fullName().' has no email address.')
->expectsOutput("The following users do not have an email address:")
->expectsTable($headers, $rows)
->assertExitCode(0);
Mail::assertNotSent(UnacceptedAssetReminderMail::class);

View File

@@ -45,4 +45,24 @@ class ImportTest extends TestCase
]);
$this->assertEquals($evil_string, $results->json()['files'][0]['first_row'][0]);
}
public function testStoreInternationalAssetMisparse(): void
{
$evil_maker = function ($arr) {
$results = '';
foreach ($arr as $thing) {
$results .= chr($thing);
}
return $results;
};
// 0xC0 makes it 'not unicode', and 0xFF makes it 'likely WINDOWS-1251', and 0x98 at the end makes it 'not-valid-Windows-1251'
$evil_content = $evil_maker([0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x98]);
$this->actingAsForApi(User::factory()->superuser()->create());
$results = $this->post(route('api.imports.store'), ['files' => [UploadedFile::fake()->createWithContent("myname.csv", $evil_content)]])
->assertStatus(422)
->assertStatusMessageIs('error')
->assertMessagesAre(trans('admin/hardware/message.import.transliterate_failure', ["encoding" => "windows-1251"]));
}
}

View File

@@ -11,7 +11,7 @@ class UpdateLicenseTest extends TestCase
public function testPageRenders()
{
$this->actingAs(User::factory()->superuser()->create())
->get(route('licenses.update', License::factory()->create()->id))
->get(route('licenses.edit', License::factory()->create()->id))
->assertOk();
}
}

View File

@@ -10,7 +10,10 @@ class CategoryEditFormTest extends TestCase
{
public function testTheComponentCanRender()
{
Livewire::test(CategoryEditForm::class)->assertStatus(200);
Livewire::test(CategoryEditForm::class, [
'sendCheckInEmail' => true,
'useDefaultEula' => true,
])->assertStatus(200);
}
public function testSendEmailCheckboxIsCheckedOnLoadWhenSendEmailIsExistingSetting()

View File

@@ -21,7 +21,7 @@ class UpdateLocationsTest extends TestCase
public function testPageRenders()
{
$this->actingAs(User::factory()->superuser()->create())
->get(route('locations.update', Location::factory()->create()))
->get(route('locations.edit', Location::factory()->create()))
->assertOk();
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Tests\Feature\Notifications\Email;
use App\Mail\CheckoutAssetMail;
use App\Models\CheckoutAcceptance;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class AssetAcceptanceReminderTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Mail::fake();
}
public function testMustHavePermissionToSendReminder()
{
$checkoutAcceptance = CheckoutAcceptance::factory()->pending()->create();
$userWithoutPermission = User::factory()->create();
$this->actingAs($userWithoutPermission)
->post($this->routeFor($checkoutAcceptance))
->assertForbidden();
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function testReminderNotSentIfAcceptanceDoesNotExist()
{
$this->actingAs(User::factory()->canViewReports()->create())
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => 999999,
]));
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function testReminderNotSentIfAcceptanceAlreadyAccepted()
{
$checkoutAcceptanceAlreadyAccepted = CheckoutAcceptance::factory()->accepted()->create();
$this->actingAs(User::factory()->canViewReports()->create())
->post($this->routeFor($checkoutAcceptanceAlreadyAccepted));
Mail::assertNotSent(CheckoutAssetMail::class);
}
public static function CheckoutAcceptancesToUsersWithoutEmailAddresses()
{
yield 'User with null email address' => [
function () {
return CheckoutAcceptance::factory()
->pending()
->forAssignedTo(['email' => null])
->create();
}
];
yield 'User with empty string email address' => [
function () {
return CheckoutAcceptance::factory()
->pending()
->forAssignedTo(['email' => ''])
->create();
}
];
}
#[DataProvider('CheckoutAcceptancesToUsersWithoutEmailAddresses')]
public function testUserWithoutEmailAddressHandledGracefully($callback)
{
$checkoutAcceptance = $callback();
$this->actingAs(User::factory()->canViewReports()->create())
->post($this->routeFor($checkoutAcceptance))
// check we didn't crash...
->assertRedirect();
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function testReminderIsSentToUser()
{
$checkoutAcceptance = CheckoutAcceptance::factory()->pending()->create();
$this->actingAs(User::factory()->canViewReports()->create())
->post($this->routeFor($checkoutAcceptance))
->assertRedirect(route('reports/unaccepted_assets'));
Mail::assertSent(CheckoutAssetMail::class, 1);
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) use ($checkoutAcceptance) {
return $mail->hasTo($checkoutAcceptance->assignedTo->email)
&& $mail->hasSubject(trans('mail.unaccepted_asset_reminder'));
});
}
private function routeFor(CheckoutAcceptance $checkoutAcceptance): string
{
return route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $checkoutAcceptance->id,
]);
}
}

View File

@@ -4,6 +4,7 @@ namespace Tests\Feature\Notifications\Email;
use App\Mail\ExpiringAssetsMail;
use App\Mail\ExpiringLicenseMail;
use App\Mail\SendUpcomingAuditMail;
use App\Models\Asset;
use App\Models\License;
use App\Models\Setting;
@@ -88,4 +89,38 @@ class ExpiringAlertsNotificationTest extends TestCase
return $mail->licenses->contains($expiredLicense) || $mail->licenses->contains($notExpiringLicense);
});
}
public function testAuditWarningThresholdEmailNotification()
{
$this->markIncompleteIfSqlite();
Mail::fake();
$this->settings->enableAlertEmail('admin@example.com');
$this->settings->setAuditWarningDays(15);
$alert_email = Setting::first()->alert_email;
$upcomingAuditableAsset = Asset::factory()->create([
'next_audit_date' => now()->addDays(14)->format('Y-m-d'),
'deleted_at' => null,
]);
$overDueForAuditableAsset = Asset::factory()->create([
'next_audit_date' => now()->subDays(1)->format('Y-m-d'),
'deleted_at' => null,
]);
$notAuditableAsset = Asset::factory()->create([
'next_audit_date' => now()->addDays(30)->format('Y-m-d'),
'deleted_at' => null,
]);
$this->artisan('snipeit:upcoming-audits')->assertExitCode(0);
Mail::assertSent(SendUpcomingAuditMail::class, function($mail) use ($alert_email, $upcomingAuditableAsset, $overDueForAuditableAsset) {
return $mail->hasTo($alert_email) && ($mail->assets->contains($upcomingAuditableAsset) && $mail->assets->contains($overDueForAuditableAsset));
});
Mail::assertNotSent(SendUpcomingAuditMail::class, function($mail) use ($alert_email, $notAuditableAsset) {
return $mail->hasTo($alert_email) && $mail->assets->contains($notAuditableAsset);
});
}
}

View File

@@ -32,6 +32,12 @@ class Settings
'alert_threshold' => $days,
]);
}
public function setAuditWarningDays(int $days): Settings
{
return $this->update([
'audit_warning_days' => $days,
]);
}
public function disableAlertEmail(): Settings
{
return $this->update([

View File

@@ -7,9 +7,7 @@ use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
use Carbon\Carbon;
use App\Notifications\CheckoutAssetNotification;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class NotificationTest extends TestCase
@@ -33,8 +31,8 @@ class NotificationTest extends TestCase
Mail::fake();
$asset->checkOut($user, $admin->id);
Mail::assertSent(CheckoutAssetMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasSubject(trans('mail.Asset_Checkout_Notification'));
});
}
public function testDefaultEulaIsSentWhenSetInCategory()

View File

@@ -1,4 +1,9 @@
<?php
$error_icon = "\e[91m✘\e[0m";
$success_icon = "\e[92m√\e[0m";
$info_icon = "\e[93mⓘ\e[0m";
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('Access denied.');
// We define this because we can't reliably use file_get_contents because some
@@ -25,7 +30,7 @@ function url_get_contents ($Url) {
$output = curl_exec($ch);
curl_close($ch);
if ($output === false) {
print("Error retrieving PHP requirements!\n");
print("\e[91mError retrieving PHP requirements!\e[39m\n");
print("Error was: " . curl_error($ch) . "\n");
print("Try enabling allow_url_fopen in php.ini, or fixing your curl/OpenSSL setup, or try rerunning with --skip-php-compatibility-checks");
return '{}';
@@ -38,6 +43,7 @@ $skip_php_checks = false;
$branch = 'master';
$branch_override = false;
$no_interactive = false;
$skip_backup = false;
// Check for branch or other overrides
if ($argc > 1){
@@ -46,6 +52,9 @@ if ($argc > 1){
case '--skip-php-compatibility-checks':
$skip_php_checks = true;
break;
case '--skip-backup':
$skip_backup = true;
break;
case '--branch':
$arg++;
$branch = $argv[$arg];
@@ -62,7 +71,7 @@ if ($argc > 1){
}
}
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "WELCOME TO THE SNIPE-IT UPGRADER! \n";
echo "--------------------------------------------------------\n\n";
echo "This script will attempt to: \n\n";
@@ -71,8 +80,9 @@ echo "- check your PHP version and extension requirements \n";
echo "- check directory permissions \n";
echo "- do a git pull to bring you to the latest version \n";
echo "- run composer install to get your vendors up to date \n";
echo "- run a backup \n";
echo "- run migrations to get your schema up to date \n";
echo "- clear out old cache settings\n\n";
echo "- clear out old cache settings\e[39m\n\n";
// Fetching most current upgrade requirements from github. Read more here: https://github.com/snipe/snipe-it/pull/14127
@@ -81,29 +91,29 @@ $upgrade_requirements_raw = url_get_contents($remote_requirements_file);
$upgrade_requirements = json_decode($upgrade_requirements_raw, true);
if (! $upgrade_requirements) {
if(!$skip_php_checks){
echo "\nERROR: Failed to retrieve remote requirements from $remote_requirements_file\n\n";
echo "\n\e[91mERROR: Failed to retrieve remote requirements from $remote_requirements_file \e[39m\n\n";
if ($branch_override){
echo "NOTE: You passed a custom branch: $branch\n";
echo " If the above URL doesn't work, that may be why. Please check you branch spelling/existence\n\n";
echo "\e[93mNOTE: You passed a custom branch: $branch\n";
echo "If the above URL doesn't work, that may be why. Please check the branch spelling/existence\e[39m\n\n";
}
if (json_last_error()) {
print "JSON DECODE ERROR DETECTED:\n";
print "\e[91mJSON DECODE ERROR DETECTED:\n";
print json_last_error_msg() . "\n\n";
print "Raw curl output:\n";
print $upgrade_requirements_raw . "\n\n";
print $upgrade_requirements_raw . "\e[39m\n\n";
}
echo "We suggest correcting this, but if you can't, please verify that your requirements conform to those at that url.\n\n";
echo " -- DANGER -- DO AT YOUR OWN RISK --\n";
echo " IF YOU ARE SURE, re-run this script with --skip-php-compatibility-checks to skip this check.\n";
echo " -- DANGER -- THIS COULD BREAK YOUR INSTALLATION";
die("Exiting.\n\n");
echo "\e[93mWe suggest correcting this, but if you can't, please verify that your requirements conform to those at that url.\n\n";
echo "\e[91m-- DANGER -- DO AT YOUR OWN RISK --\n";
echo "IF YOU ARE SURE, re-run this script with --skip-php-compatibility-checks to skip this check.\n";
echo "-- DANGER -- THIS COULD BREAK YOUR INSTALLATION\e[39m\n\n";
die("Aborting upgrade.\n\n");
}
echo "NOTICE: Unable to fetch upgrade requirements, but continuing because you passed --skip-php-compatibility-checks...\n";
echo "\e[93mNOTICE: Unable to fetch upgrade requirements, but continuing because you passed --skip-php-compatibility-checks...e[39m\n";
}
echo "Launching using branch: $branch\n";
echo "Launching using branch: $branch\e[39m\n";
if($upgrade_requirements){
$php_min_works = $upgrade_requirements['php_min_version'];
@@ -133,22 +143,22 @@ if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') || (!function_exists('posix_get
$username = $pwu_data['name'];
if (($username=='root') || ($username=='admin')) {
die("\nERROR: This script should not be run as root/admin. Exiting.\n\n");
die("\n".$error_icon."ERROR: This script should not be run as root/admin. Exiting.\n\n");
}
}
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 1: Checking .env file: \n";
echo "- Your .env is located at ".getcwd()."/.env \n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\\n\n";
// Check the .env looks ok
$env = file('.env');
if (! $env){
echo "\n!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "\n\e[91m!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "Your .env file doesn't seem to exist in this directory or isn't readable! Please look into that.\n";
exit(1);
}
@@ -198,32 +208,32 @@ foreach ($env as $line_num => $line) {
if ($env_key == 'APP_KEY') {
if (($env_value=='') || (strlen($env_value) < 20)) {
$env_bad .= " APP_KEY ERROR in your .env on line #'.{$show_line_num}.': Your APP_KEY should not be blank. Run `php artisan key:generate` to generate one.\n";
$env_bad .= $error_icon." APP_KEY ERROR in your .env on line #'.{$show_line_num}.': Your APP_KEY should not be blank. Run `php artisan key:generate` to generate one.\n";
} else {
$env_good .= " Your APP_KEY is not blank. \n";
$env_good .= $success_icon." Your APP_KEY is not blank. \n";
}
}
if ($env_key == 'APP_URL') {
if (($env_value!="null") && ($env_value!="")) {
$env_good .= ' Your APP_URL is not null or blank. It is set to '.$env_value."\n";
$env_good .= $success_icon.' Your APP_URL is not null or blank. It is set to '.$env_value."\n";
if (!str_begins(trim($env_value), 'http://') && (!str_begins($env_value, 'https://'))) {
$env_bad .= ' APP_URL ERROR in your .env on line #'.$show_line_num.': Your APP_URL should start with https:// or http://!! It is currently set to: '.$env_value;
$env_bad .= $error_icon.' APP_URL ERROR in your .env on line #'.$show_line_num.': Your APP_URL should start with https:// or http://!! It is currently set to: '.$env_value;
} else {
$env_good .= ' Your APP_URL is set to '.$env_value.' and starts with the protocol (https:// or http://)'."\n";
$env_good .= $success_icon.' Your APP_URL is set to '.$env_value.' and starts with the protocol (https:// or http://)'."\n";
}
if (str_ends(trim($env_value), "/")) {
$env_bad .= ' APP_URL ERROR in your .env on line #'.$show_line_num.': Your APP_URL should NOT end with a trailing slash. It is currently set to: '.$env_value;
$env_bad .= $error_icon.' APP_URL ERROR in your .env on line #'.$show_line_num.': Your APP_URL should NOT end with a trailing slash. It is currently set to: '.$env_value;
} else {
$env_good .= ' Your APP_URL ('.$env_value.') does not have a trailing slash.'."\n";
$env_good .= $success_icon.' Your APP_URL ('.$env_value.') does not have a trailing slash.'."\n";
}
} else {
$env_bad .= " APP_URL ERROR in your .env on line #".$show_line_num.": Your APP_URL CANNOT be set to null or left blank.\n";
$env_bad .= $error_icon." APP_URL ERROR in your .env on line #".$show_line_num.": Your APP_URL CANNOT be set to null or left blank.\n";
}
}
@@ -237,34 +247,34 @@ echo $env_good;
if ($env_bad !='') {
echo "!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "Your .env file is misconfigured. Upgrade cannot continue.\n";
echo "\e[91m!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "\e[91mYour .env file is misconfigured. Upgrade cannot continue.\n";
echo "--------------------------------------------------------\n\n";
echo $env_bad;
echo "\n\n--------------------------------------------------------\n";
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
echo "Please correct the issues above in ".getcwd()."/.env and try again.\n";
echo "--------------------------------------------------------\n";
echo "\e[91m!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
echo "\e[91mPlease correct the issues above in ".getcwd()."/.env and try again.\n";
echo "\e[91m--------------------------------------------------------\n";
exit(1);
}
if(!$skip_php_checks){
echo "\n--------------------------------------------------------\n";
echo "STEP 2: Checking PHP requirements: (Required PHP >=". $php_min_works. " - <".$php_max_wontwork.") \n";
echo "--------------------------------------------------------\n\n";
echo "\n\e[95m--------------------------------------------------------\n";
echo "STEP 2: Checking PHP requirements: (Required PHP >=". $php_min_works. " - <".$php_max_wontwork.")\n";
echo "--------------------------------------------------------\e[39m\n\n";
if ((version_compare(phpversion(), $php_min_works, '>=')) && (version_compare(phpversion(), $php_max_wontwork, '<'))) {
echo " Current PHP version: (" . phpversion() . ") is at least " . $php_min_works . " and less than ".$php_max_wontwork."! Continuing... \n";
echo $success_icon." Current PHP version: (" . phpversion() . ") is at least " . $php_min_works . " and less than ".$php_max_wontwork."! Continuing... \n";
echo sprintf("FYI: The php.ini used by this PHP is: %s\n\n", get_cfg_var('cfg_file_path'));
} else {
echo "!!!!!!!!!!!!!!!!!!!!!!!!! PHP VERSION ERROR !!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "\e[91m!!!!!!!!!!!!!!!!!!!!!!!!! PHP VERSION ERROR !!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "This version of PHP (".phpversion().") is NOT compatible with Snipe-IT.\n";
echo "Snipe-IT requires PHP versions between ".$php_min_works." and ".$php_max_wontwork.".\n";
echo "Please install a compatible version of PHP and re-run this script again. \n";
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
echo "\e[91m!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
exit(1);
}
}
@@ -318,7 +328,7 @@ foreach ($required_exts_array as $required_ext) {
foreach ($require_either as $require_either_value) {
if (in_array($require_either_value, $loaded_exts_array)) {
$ext_installed .= ' '.$require_either_value." is installed!\n";
$ext_installed .= $success_icon.' '.$require_either_value." is installed!\n";
$has_one_required_ext = true;
break;
}
@@ -326,20 +336,20 @@ foreach ($required_exts_array as $required_ext) {
// If no match, add it to the string for errors
if (!$has_one_required_ext) {
$ext_missing .= ' MISSING PHP EXTENSION: '.str_replace("|", " OR ", $required_ext)."\n";
$ext_missing .= $error_icon.' MISSING PHP EXTENSION: '.str_replace("|", " OR ", $required_ext)."\n";
break;
}
// If this isn't an either/or option, just add it to the string of errors conventionally
} elseif (!in_array($required_ext, $recommended_exts_array)){
$ext_missing .= ' MISSING PHP EXTENSION: '.$required_ext."\n";
$ext_missing .= $error_icon.' MISSING PHP EXTENSION: '.$required_ext."\n";
} else {
$ext_installed .= '- '.$required_ext." is *NOT* installed, but is recommended...\n";
}
// The required extension string was found in the array of installed extensions - yay!
} else {
$ext_installed .= ' '.$required_ext." is installed!\n";
$ext_installed .= $success_icon.' '.$required_ext." is installed!\n";
}
}
@@ -350,15 +360,15 @@ if ($ext_missing!='') {
echo "--------------------------------------------------------\n";
foreach ($loaded_exts_array as $loaded_ext) {
echo "- ".$loaded_ext."\n";
echo $success_icon.' '.$loaded_ext."\n";
}
echo "--------------------- !! ERROR !! ----------------------\n";
echo "\e[91m--------------------- !! ERROR !! ----------------------\n";
echo $ext_missing;
echo "--------------------------------------------------------\n";
echo "ABORTING THE INSTALLER \n";
echo "Please install the extensions above and re-run this script.\n";
echo "--------------------------------------------------------\n";
echo "\e[91m--------------------------------------------------------\n";
echo "\e[91mABORTING THE INSTALLER \n";
echo "\e[91mPlease install the extensions above and re-run this script.\n";
echo "\e[91m--------------------------------------------------------\n";
exit(1);
} else {
echo $ext_installed."\n";
@@ -367,9 +377,9 @@ if ($ext_missing!='') {
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 3: Checking directory permissions: \n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
$writable_dirs_array =
@@ -395,9 +405,9 @@ $dirs_not_writable = '';
// Loop through the directories that need to be writable
foreach ($writable_dirs_array as $writable_dir) {
if (is_writable($writable_dir)) {
$dirs_writable .= ' '.getcwd().'/'.$writable_dir." is writable \n";
$dirs_writable .= $success_icon.' '.getcwd().'/'.$writable_dir." is writable \n";
} else {
$dirs_not_writable .= ' PERMISSIONS ERROR: '.getcwd().'/'.$writable_dir." is NOT writable\n";
$dirs_not_writable .= $error_icon.' PERMISSIONS ERROR: '.getcwd().'/'.$writable_dir." is NOT writable\n";
}
}
@@ -405,44 +415,24 @@ echo $dirs_writable."\n";
// Print out a useful error message
if ($dirs_not_writable!='') {
echo "--------------------------------------------------------\n";
echo "The following directories/files do not seem writable: \n";
echo "--------------------------------------------------------\n";
echo "\e[91m--------------------------------------------------------\n";
echo "\eThe following directories/files do not seem writable: \n";
echo "\e--------------------------------------------------------\e[39m\n";
echo $dirs_not_writable;
echo "--------------------- !! ERROR !! ----------------------\n";
echo "Please check the permissions on the directories above and re-run this script.\n";
echo "------------------------- :( ---------------------------\n\n";
echo "\e[91m--------------------- !! ERROR !! ----------------------\n";
echo "\ePlease check the permissions on the directories above and re-run this script.\n";
echo "\e------------------------- :( ---------------------------\e[39m\n\n";
exit(1);
}
echo "--------------------------------------------------------\n";
echo "STEP 4: Backing up database: \n";
echo "--------------------------------------------------------\n\n";
$backup = exec('php artisan snipeit:backup', $backup_results, $return_code);
echo '-- ' . implode("\n", $backup_results) . "\n\n";
if ($return_code > 0) {
die("Something went wrong with your backup. Aborting!\n\n");
}
unset($return_code);
echo "--------------------------------------------------------\n";
echo "STEP 5: Putting application into maintenance mode: \n";
echo "--------------------------------------------------------\n\n";
exec('php artisan down', $down_results, $return_code);
echo '-- ' . implode("\n", $down_results) . "\n";
if ($return_code > 0) {
die("Something went wrong with downing you site. This can't be good. Please investigate the error. Aborting!n\n");
}
unset($return_code);
echo "--------------------------------------------------------\n";
echo "STEP 6: Pulling latest from Git (".$branch." branch): \n";
echo "--------------------------------------------------------\n\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 4: Pulling latest from Git (".$branch." branch): \n";
echo "--------------------------------------------------------\e[39m\n\n";
$git_version = shell_exec('git --version');
if ((strpos('git version', $git_version)) === false) {
@@ -466,9 +456,9 @@ if ((strpos('git version', $git_version)) === false) {
}
echo "--------------------------------------------------------\n";
echo "STEP 7: Cleaning up old cached files:\n";
echo "--------------------------------------------------------\n\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 5: Cleaning up old cached files:\n";
echo "--------------------------------------------------------\e[39m\n\n";
// Build an array of the files we generally want to delete because they
// can cause issues with funky caching
@@ -481,39 +471,28 @@ $unused_files = [
foreach ($unused_files as $unused_file) {
if (file_exists($unused_file)) {
echo " Deleting ".$unused_file.". It is no longer used.\n";
echo $success_icon." Deleting ".$unused_file.". It is no longer used.\n";
@unlink($unused_file);
} else {
echo " No ".$unused_file.", so nothing to delete.\n";
echo $success_icon." No ".$unused_file.", so nothing to delete.\n";
}
}
echo "\n";
$config_clear = shell_exec('php artisan config:clear');
$cache_clear = shell_exec('php artisan cache:clear');
$route_clear = shell_exec('php artisan route:clear');
$view_clear = shell_exec('php artisan view:clear');
echo '-- '.$config_clear;
echo '-- '.$cache_clear;
echo '-- '.$route_clear;
echo '-- '.$view_clear;
echo "\n";
echo "--------------------------------------------------------\n";
echo "STEP 8: Updating composer dependencies:\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 6: Updating composer dependencies:\n";
echo "(This may take a moment.)\n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
echo "-- Running the app in ".$app_environment." mode.\n";
// Composer install
if (file_exists('composer.phar')) {
echo " Local composer.phar detected, so we'll use that.\n\n";
echo $success_icon." Local composer.phar detected, so we'll use that.\n\n";
echo "-- Updating local composer.phar\n\n";
$composer_update = shell_exec('php composer.phar self-update');
echo $composer_update."\n\n";
// Use --no-dev only if we are in production mode.
// This will cause errors otherwise, if the user is in develop or local for their APP_ENV
if ($app_environment == 'production') {
@@ -521,10 +500,9 @@ if (file_exists('composer.phar')) {
} else {
$composer = shell_exec('php composer.phar install --prefer-source');
}
$composer_dump = shell_exec('php composer.phar dump');
} else {
echo "-- We couldn't find a local composer.phar. No worries, trying globally.\n";
@@ -541,48 +519,87 @@ if (file_exists('composer.phar')) {
$composer_dump = shell_exec('composer dump');
}
echo $composer_dump."\n";
echo $composer;
$config_clear = shell_exec('php artisan config:clear');
$cache_clear = shell_exec('php artisan cache:clear');
$route_clear = shell_exec('php artisan route:clear');
$view_clear = shell_exec('php artisan view:clear');
echo $success_icon.' '.trim($config_clear)."\n";
echo $success_icon.' '.trim($cache_clear)."\n";
echo $success_icon.' '.trim($route_clear)."\n";
echo $success_icon.' '.trim($view_clear)."\n";
echo "\n";
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 7: Putting application into maintenance mode: \n";
echo "--------------------------------------------------------\e[39m\n\n";
exec('php artisan down', $down_results, $return_code);
echo '-- ' . implode("\n", $down_results) . "\n";
if ($return_code > 0) {
die("Something went wrong with downing your site. This can't be good. Please investigate the error. Aborting!\n\n");
}
unset($return_code);
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 8: Backing up database: \n";
echo "--------------------------------------------------------\e[39m\n\n";
if (!$skip_backup) {
$backup = exec('php artisan snipeit:backup', $backup_results, $return_code);
if ($return_code > 0) {
die($error_icon." Something went wrong with your backup. Aborting!\n\n");
} else {
echo '-- ' . implode("\n", $backup_results) . "\n\n";
}
unset($return_code);
} else {
echo "Upgrade was run with --skip-backup, so no backup will be run.\n\n";
}
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 9: Migrating database:\n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
$migrations = shell_exec('php artisan migrate --force');
echo $migrations."\n";
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 10: Checking for OAuth keys:\n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
if ((!file_exists('storage/oauth-public.key')) || (!file_exists('storage/oauth-private.key'))) {
echo "- No OAuth keys detected. Running passport install now.\n\n";
$passport = shell_exec('php artisan passport:install');
echo $info_icon." No OAuth keys detected. Running passport install now.\n\n";
$passport = shell_exec('php artisan passport:install --no-interaction');
echo $passport;
} else {
echo " OAuth keys detected. Skipping passport install.\n\n";
echo $success_icon." OAuth keys detected. Skipping passport install.\n\n";
}
echo "--------------------------------------------------------\n";
echo "\e[95m--------------------------------------------------------\n";
echo "STEP 11: Taking application out of maintenance mode:\n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
$up = shell_exec('php artisan up');
echo '-- '.$up."\n";
echo $success_icon.' '.trim($up)."\n\n";
echo "---------------------- FINISHED! -----------------------\n";
echo "\e[92m---------------------- FINISHED! -----------------------\n";
echo "All done! Clear your browser cookies and re-login to use \n";
echo "your upgraded Snipe-IT!\n";
echo "--------------------------------------------------------\n\n";
echo "--------------------------------------------------------\e[39m\n\n";
function str_begins($haystack, $needle) {
@@ -592,6 +609,3 @@ function str_begins($haystack, $needle) {
function str_ends($haystack, $needle) {
return (substr_compare($haystack, $needle, -strlen($needle)) === 0);
}