Compare commits

..

636 Commits

Author SHA1 Message Date
snipe
20a84c66a4 Everything is awful :(
Signed-off-by: snipe <snipe@snipe.net>
2025-02-04 21:38:33 +00:00
snipe
0fda3e2961 Fixed comment
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 18:47:59 +00:00
snipe
d6d467d1bc Removed old company identifier
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 18:47:49 +00:00
snipe
6e24c4294a Show multiple companies on user page
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:54:56 +00:00
snipe
25043283d7 Handled ambiguous company_id
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:42:50 +00:00
snipe
a3081fdbaf Added companies to user controllers
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:42:10 +00:00
snipe
218674b4cf Updated tranformer to support multiple companies
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:41:53 +00:00
snipe
6033485755 Updated relationship
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:41:37 +00:00
snipe
0a9bf97ae0 Added relationship and query scopes
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:41:30 +00:00
snipe
b05ab19cde Added companies select box
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:41:12 +00:00
snipe
4c7c48e38b Added migration
Signed-off-by: snipe <snipe@snipe.net>
2025-01-23 15:39:56 +00:00
snipe
e53ed2319c Merge pull request #16118 from marcusmoore/fixes/undefined-key-in-asset-observer 2025-01-22 22:30:55 +00:00
Marcus Moore
8f512e5941 Replace isset with the more appropriate array_key_exists 2025-01-22 14:28:35 -08:00
Marcus Moore
8a1b6b0684 Add isset check 2025-01-22 14:15:35 -08:00
snipe
36f460d32b Default to localStorage for bootstap table cookies
Signed-off-by: snipe <snipe@snipe.net>
2025-01-22 21:48:03 +00:00
snipe
802fcbafa0 Merge pull request #16116 from marcusmoore/bug/sc-20259 2025-01-22 18:50:12 +00:00
Marcus Moore
1098b8cd9d Avoid trying to divide by zero 2025-01-22 10:21:30 -08:00
Marcus Moore
da8999f59a Add failing test 2025-01-22 10:21:18 -08:00
snipe
1212267da3 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-01-22 17:30:41 +00:00
snipe
d696ed8a5a Merge pull request #16107 from marcusmoore/chore/migrate-password-helper 2025-01-22 17:28:23 +00:00
Marcus Moore
c3d17c5727 Remove value for readonly 2025-01-22 09:27:40 -08:00
snipe
cb25d0f2f3 Merge pull request #16115 from snipe/localization/2025-01-22 2025-01-22 17:21:54 +00:00
snipe
be535671bc Updated language files
Signed-off-by: snipe <snipe@snipe.net>
2025-01-22 17:09:41 +00:00
snipe
63838f6e74 Merge pull request #16108 from marcusmoore/chore/migrate-form-submit-helper 2025-01-22 13:16:24 +00:00
Marcus Moore
5ddf0dd789 Migrate form submit helper 2025-01-21 16:36:22 -08:00
Marcus Moore
b003890dd2 Migrate password helpers 2025-01-21 16:14:20 -08:00
snipe
8db8d4beba Merge pull request #16105 from snipe/use_url_fragment_for_file_uploads 2025-01-21 18:10:10 +00:00
snipe
d5309c7d94 Added fragment to uploads for redirect
Signed-off-by: snipe <snipe@snipe.net>
2025-01-21 18:06:00 +00:00
snipe
2668960bb9 Merge pull request #16102 from snipe/added_serial_search_to_activity_report 2025-01-20 21:06:23 +00:00
snipe
8e4878fac0 Added serial as relation search
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 21:00:01 +00:00
snipe
752d89d177 Merge pull request #16101 from snipe/standarize_modelfile_api 2025-01-20 20:23:50 +00:00
snipe
0d7304eb8b Use transformer for model files
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 20:20:35 +00:00
snipe
5afcd8ddb3 Merge pull request #16099 from snipe/add_id_to_locations_importer
Fixed #16097 - added location ID to location importer
2025-01-20 16:58:24 +00:00
snipe
5273408631 Check for matching ID
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:53:48 +00:00
snipe
713c9fdd6f Derp
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:46:22 +00:00
snipe
0a5c41ce23 Log error
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:42:53 +00:00
snipe
48a916ab77 Check that the ID has a value
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:38:31 +00:00
snipe
67ab602e3b Merge pull request #16090 from snipe/fixes/s3_support_for_eulas
Fixed #16000 - add S3 support for eula PDF downloads
2025-01-20 16:26:11 +00:00
snipe
801328ec42 Fixed #16097 - allow location update by ID in importer
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:23:13 +00:00
snipe
4fe83467ee Removed log error
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 16:20:31 +00:00
snipe
27a7a89990 Updated bootstrap tables to 1.24.0
Signed-off-by: snipe <snipe@snipe.net>
2025-01-20 15:06:00 +00:00
snipe
446c7fb483 Merge pull request #16096 from uberbrady/detect_csv_encodings_v2 2025-01-20 14:42:07 +00:00
Brady Wetherington
bbb2af7f53 remove log messages 2025-01-20 13:52:26 +00:00
Brady Wetherington
d65206c7fe Move tests to UI-side 2025-01-20 13:49:42 +00:00
snipe
327491c3a4 Merge pull request #16091 from snipe/hide_password_reset_if_ldap 2025-01-17 19:09:23 +00:00
snipe
02eeb7f916 Added extrab option in bulk edit
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 19:04:23 +00:00
snipe
e9ad43afbe Better hide reset password functionality for LDAP users
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 18:51:08 +00:00
snipe
434068a2ed Fixes #16000 - add S3 support for eula PDF downloads
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 17:49:19 +00:00
snipe
7e65d68392 Merge pull request #16089 from snipe/fixes/15017_card_view
Fixes #15017 - card view for mobile
2025-01-17 17:04:04 +00:00
snipe
b3d35beeb9 Fixes #15017 - card view for mobile
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 16:58:30 +00:00
snipe
fed7c2dc16 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 16:38:04 +00:00
snipe
79951c3f17 Set support footer to on in reset demo script
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 16:37:00 +00:00
snipe
221ffb446c Fixed #15946 - added comtent type to webhook test
Signed-off-by: snipe <snipe@snipe.net>
2025-01-17 16:36:43 +00:00
snipe
57f80290a1 Merge pull request #16081 from marcusmoore/fixes/zerofill-count-on-setup 2025-01-16 21:22:02 +00:00
Marcus Moore
4b2ede7a71 Use 0 for zerofill_count if nothing provided 2025-01-16 12:20:14 -08:00
snipe
5e465fa417 Merge remote-tracking branch 'origin/master' into develop 2025-01-16 19:06:02 +00:00
snipe
de5d66f5d2 Comment out ad-hoc note button for now
Signed-off-by: snipe <snipe@snipe.net>
2025-01-16 19:05:52 +00:00
Brady Wetherington
a50c8c6269 Automatically detect character encoding of CSV files when processsing them
to handle non-UTF-8 file types. Added a new test case and enhanced the test
rigs to be able to write non-UTF-8 files.

Final cleanup
2025-01-16 17:03:37 +00:00
Marcus Moore
96379d0a62 Use provided zerofill_count 2025-01-15 16:39:50 -08:00
snipe
bbc9ed5778 Fixed audit date default on bulk audit
Signed-off-by: snipe <snipe@snipe.net>
2025-01-15 22:28:50 +00:00
snipe
1d0c156d2e Increased due for checkin warning
Signed-off-by: snipe <snipe@snipe.net>
2025-01-15 22:12:18 +00:00
snipe
7de5e2e7ef One more audot field increase
Signed-off-by: snipe <snipe@snipe.net>
2025-01-15 22:11:22 +00:00
snipe
8d935b34eb Fixed audit interval input width
Signed-off-by: snipe <snipe@snipe.net>
2025-01-15 22:07:53 +00:00
snipe
9830959f11 Fixed input width on audit interval
Signed-off-by: snipe <snipe@snipe.net>
2025-01-15 22:07:15 +00:00
snipe
37b39956b5 Merge pull request #16071 from snipe/bug/sc-28149 2025-01-14 14:35:24 +00:00
snipe
9a4fd81c70 Use correct icon for ad-hoc notes on assets
Signed-off-by: snipe <snipe@snipe.net>
2025-01-14 14:34:17 +00:00
snipe
0bb2e98af3 Built assets
Signed-off-by: snipe <snipe@snipe.net>
2025-01-14 12:33:59 +00:00
snipe
1c8d94c953 Merge pull request #15525 from akemidx/updated_ad_hoc_notes 2025-01-14 11:33:21 +00:00
snipe
ca11efd3ee Merge pull request #16036 from Godmartinz/unaccepted_assets_reports_memory_fix 2025-01-13 22:23:30 +00:00
snipe
4a6a94a50e Merge pull request #16010 from AnouarTouati/dev-container-fixes 2025-01-13 22:23:13 +00:00
snipe
2d65b38155 Merge pull request #16011 from AnouarTouati/dev-docker-improv 2025-01-13 21:00:04 +00:00
snipe
587449ef97 Merge pull request #15981 from akemidx/bug/sc-27551 2025-01-13 17:35:58 +00:00
snipe
06ffac9d7d Merge pull request #16046 from uberbrady/ldap_asset_location_switch_fix 2025-01-13 17:33:13 +00:00
snipe
e32ed03f66 Merge pull request #16061 from snipe/moved_keywords_on_admin_settings 2025-01-13 17:10:01 +00:00
snipe
a7543b407f Added a few more words
Signed-off-by: snipe <snipe@snipe.net>
2025-01-13 17:07:22 +00:00
snipe
98b1ba1e39 Moved keywords in lang file
Signed-off-by: snipe <snipe@snipe.net>
2025-01-13 17:04:48 +00:00
snipe
e03ed7567a Merge pull request #15747 from NebelKreis/feature/custom-data-options-for-2d-barcode 2025-01-13 15:20:56 +00:00
Nebel
9f0581275b Merge branch 'develop' into feature/custom-data-options-for-2d-barcode 2025-01-13 15:49:32 +01:00
snipe
3f5c166417 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2025-01-13 13:59:41 +00:00
snipe
cf091377b6 Add @brlin-tw as a contributor 2025-01-13 13:59:34 +00:00
snipe
0cc7a7014f Merge pull request #16053 from brlin-tw/patch-1
Fixed #16054: fix incorrect compose service name in the APP_KEY generation command of the Docker env file
2025-01-13 13:59:05 +00:00
snipe
4790ad53e3 Updated font-awesome to 6.7.2
Signed-off-by: snipe <snipe@snipe.net>
2025-01-13 13:57:45 +00:00
snipe
62dc075834 Add @aHVzY2g as a contributor 2025-01-13 13:55:28 +00:00
snipe
41da04cd22 Merge pull request #16018 from aHVzY2g/patch-3 2025-01-13 13:55:04 +00:00
Marlon Spangenberg
0c4fc56b80 increased label/field size to 2.5, reduced supported fields to 3 2025-01-13 12:20:56 +01:00
Marlon Spangenberg
907f0553d4 moved label value into the same line, changed label font to freemono 2025-01-13 12:14:30 +01:00
snipe
224f04db6c Merge pull request #16050 from Godmartinz/inactive-slack-hook
Adds try/catch around notification failing with an inactive webhook for better user experience
2025-01-10 11:23:41 +00:00
snipe
f0bcf78941 Merge pull request #16051 from marcusmoore/testing/checkoutable-acceptance-factory-fix
Update related asset when checkout acceptance created via factory
2025-01-10 11:22:50 +00:00
林博仁 Buo-ren Lin
c08a4a2b0a Fixed #16054: fix incorrect compose service name in the APP_KEY generation command of the Docker env file
This resolves the following error:

```
$ docker compose run --rm snipe-it php artisan key:generate --show
no such service: snipe-it
```

and is also how it is done in the documentation.

Fixes #16054.

Refer-to: APP_KEY | Docker | Snipe-IT documentation <https://snipe-it.readme.io/docs/docker#app_key>
Signed-off-by: Buo-ren Lin (OSSII) <buoren.lin@ossii.com.tw>
2025-01-10 11:29:49 +08:00
Marcus Moore
8597984787 Update asset's assigned_to and assigned_type after creating 2025-01-09 15:09:57 -08:00
Godfrey M
001ccf1ce9 adds translation string instead of hardcoded message 2025-01-09 14:47:07 -08:00
Godfrey M
082a974ee3 changed from redirect with error to with warning 2025-01-09 14:26:54 -08:00
Godfrey M
152c23b8f3 changed log from warning to error 2025-01-09 12:55:25 -08:00
Godfrey M
b635822a85 add try/catch around notification failing for inactive slack hooks 2025-01-09 12:51:54 -08:00
snipe
ec85e4b5b0 Upgrade less from 4.2.0 to 4.2.1 #15996
Signed-off-by: snipe <snipe@snipe.net>
2025-01-09 20:42:04 +00:00
snipe
1acb7e0fe4 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2025-01-09 20:38:47 +00:00
snipe
5914004a57 Add @AnouarTouati as a contributor 2025-01-09 16:13:05 +00:00
snipe
c5afc66d46 Merge pull request #16009 from AnouarTouati/prod-docker-fixes 2025-01-09 16:12:37 +00:00
snipe
bc69660247 Merge pull request #16049 from snipe/localization/update_strings-2025-01-09 2025-01-09 16:02:03 +00:00
snipe
71c7c64fde Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-01-09 15:54:51 +00:00
snipe
c284ac5287 Merge pull request #16047 from snipe/bug/sc-28054 2025-01-09 15:19:13 +00:00
snipe
639afe5b9b Check for blank OR null on formatter
Signed-off-by: snipe <snipe@snipe.net>
2025-01-09 15:11:22 +00:00
Brady Wetherington
049b9c542b Conditionally update assets when user's location moves via LDAP 2025-01-09 13:43:59 +00:00
Godfrey M
92762896ef moar removal 2025-01-07 15:10:46 -08:00
Godfrey M
c699baf519 removed commented out code, and unrelated crap 2025-01-07 15:10:00 -08:00
Godfrey M
97b765b5cc improved reports query, could be further optimized 2025-01-07 15:06:09 -08:00
snipe
40b41e646d Merge pull request #16033 from Godmartinz/barcode-fix 2025-01-07 19:59:30 +00:00
snipe
63853db450 Merge pull request #16035 from marcusmoore/fixes/unaccepted-assets-reminder 2025-01-07 19:58:23 +00:00
Marcus Moore
48dd3252cb Fix parameter order 2025-01-07 11:47:16 -08:00
snipe
ee6f60e63c Merge pull request #16034 from Godmartinz/fix-locale-check 2025-01-07 19:00:32 +00:00
Godfrey M
f6abf90ba0 fix locale check 2025-01-07 10:51:48 -08:00
Godfrey M
83ee0e0fb6 untangled code visibility from each label engine 2025-01-07 10:37:59 -08:00
Godfrey M
94f44d1b77 fix label type values in db and defaults. update help text 2025-01-07 10:00:52 -08:00
Godfrey M
e12c7473f8 tinkering with the polymorphic eager load 2025-01-07 09:08:36 -08:00
snipe
407d69b370 Merge pull request #16031 from snipe/make_backups_more_configurable 2025-01-07 11:25:17 +00:00
snipe
774e795d97 Pull backup:clean settings into config
Signed-off-by: snipe <snipe@snipe.net>
2025-01-07 11:17:43 +00:00
snipe
f698f74d3e Merge pull request #16028 from marcusmoore/fixes/report-template-column
Fixed migration causing issues with mariadb
2025-01-06 22:58:30 +00:00
Marcus Moore
80f9159f4d Change options to text type 2025-01-06 14:29:18 -08:00
Godfrey M
0ec25d55a0 Merge branch 'develop' into unaccepted_assets_reports_memory_fix 2025-01-06 12:28:17 -08:00
Godfrey M
bb03e00279 fix deprecation on asset obs get unaccept report to populate 2025-01-06 11:26:45 -08:00
snipe
4d3db2ab44 Merge pull request #15919 from Godmartinz/table_dropdownmenu-fix
Removed the `table-responsive` div from several index blades
2025-01-06 18:11:19 +00:00
snipe
c3d52af546 Merge pull request #16024 from snipe/fixes/#15977
Fixed  #15977 - Set order by on companies
2025-01-06 16:12:42 +00:00
snipe
9672a13402 Set order by on companies
Signed-off-by: snipe <snipe@snipe.net>
2025-01-06 16:11:18 +00:00
snipe
9564f7fdb8 Updated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-01-06 12:46:17 +00:00
aHVzY2g
eba1f03363 Added: TZe_24mm_D.php modified TZe_24mm_A that includes 1D code
New label format for TZe_24mm that includes all fields of TZe_24mm_A with the addition of the 1D code at the bottom of the label.
2025-01-03 11:10:44 +01:00
snipe
c7ca4d061f Merge pull request #16013 from ubc-cpsc/bugfix/CVE-2024-56521-tcpdf
Fixes tecnickcom/tcpdf security CVEs
2024-12-30 23:22:32 +00:00
Joël Pittet
115f08ee49 Fixes tecnickcom/tcpdf security CVES: CVE-2024-56522, CVE-2024-56527, CVE-2024-56519, CVE-2024-56521 2024-12-30 13:59:03 -08:00
Anouar Touati
e33c680679 Improvement: Change the logging channel to storage/logs/laravel.log file in dev container 2024-12-29 11:13:47 -05:00
Anouar Touati
1e8ec783b4 following docs for prod docker without specifying APP_VERSION will now get latest version as doc says 2024-12-29 11:05:02 -05:00
Anouar Touati
a6ed958687 Fixed: dev dependencies are not installed in dev container 2024-12-29 11:02:09 -05:00
Anouar Touati
4354f50168 Fixed: Debug mode is not enabled in dev container 2024-12-29 11:02:09 -05:00
snipe
8fc1227974 Merge pull request #16003 from snipe/asset_maintenance_api_fix
Check for valid asset before accessing properties
2024-12-26 10:31:47 +00:00
snipe
d410f168bd Removed extra checks, since we already check higher up
Signed-off-by: snipe <snipe@snipe.net>
2024-12-26 10:28:27 +00:00
snipe
b38f9ad33c Updated return types
Signed-off-by: snipe <snipe@snipe.net>
2024-12-26 10:18:00 +00:00
snipe
f8311815ee Check for valid asset before accessing properties
Signed-off-by: snipe <snipe@snipe.net>
2024-12-26 10:17:45 +00:00
snipe
3edb501973 Fixed typo sanitze to sanitize
Signed-off-by: snipe <snipe@snipe.net>
2024-12-25 20:45:19 +00:00
snipe
49918d3302 Merge pull request #15992 from marcusmoore/fixes/reset-demo-command
Fixed reset demo command
2024-12-19 23:24:37 +00:00
Marcus Moore
876ab44a16 Update settings property keys 2024-12-19 15:18:27 -08:00
snipe
15296d2b1c Merge pull request #15714 from akemidx/saving_custom_report_template
Saved Custom Report Templates
2024-12-19 22:43:59 +00:00
snipe
1434522149 Merge pull request #15912 from marcusmoore/bug/harden-checkout-validation-v2
Harden asset checkout validation
2024-12-19 22:42:06 +00:00
snipe
23af5fb06e Merge pull request #15964 from marcusmoore/testing/accessories-ui
Added Accessory UI tests
2024-12-19 22:40:34 +00:00
snipe
6b8c1eb523 Merge branch 'develop' into testing/accessories-ui 2024-12-19 22:38:04 +00:00
snipe
0984194ec6 Merge pull request #15965 from marcusmoore/testing/consumable-ui
Added Consumable UI tests
2024-12-19 22:36:47 +00:00
snipe
26264e1d55 Merge branch 'develop' into testing/consumable-ui 2024-12-19 22:34:10 +00:00
snipe
dc5dedd5a3 Merge pull request #15975 from marcusmoore/testing/license-checkin
Added tests around license checkin
2024-12-19 22:33:03 +00:00
snipe
d49bfb562a Merge branch 'develop' into testing/license-checkin 2024-12-19 22:29:31 +00:00
snipe
f90dd9d74d Merge pull request #15976 from marcusmoore/testing/ui-render
Added simple front end render tests
2024-12-19 22:27:35 +00:00
snipe
8d24c0af0d Merge pull request #15982 from spencerrlongg/bug/sc-27228
Update Checkout Button Permission for Predefined Kits
2024-12-19 22:25:51 +00:00
snipe
174a01cb6b Merge pull request #15991 from snipe/added_some_sorting_on_audit_report
Added a few order options on audit report
2024-12-19 22:24:57 +00:00
snipe
360e0ff534 Added a few order options on audit report
Signed-off-by: snipe <snipe@snipe.net>
2024-12-19 22:19:33 +00:00
snipe
4f2721e93f Merge pull request #15986 from Godmartinz/fix-settings-seeder
Renames variables to match columns for label settings
2024-12-19 19:06:28 +00:00
snipe
0dce3b8b8c Merge pull request #15987 from spencerrlongg/bug/sc-27192
Add `string` to Password Reset Username Rules
2024-12-18 20:09:14 +00:00
spencerrlongg
5042c2b30a oops, rm dump 2024-12-18 13:58:18 -06:00
spencerrlongg
b45cf6124f add note 2024-12-18 13:57:18 -06:00
Godfrey M
c8f280ac90 change barcode column names in seeder 2024-12-18 09:50:55 -08:00
spencerrlongg
7eb936883a rm note 2024-12-17 16:38:29 -06:00
akemidx
c3b5a92a48 added builder 2024-12-17 17:11:52 -05:00
spencerrlongg
eb054897d6 Remove leftover testing logic and fix checkout permissions
Removed console logs and temporary testing code from the bootstrap-table view. Updated the transformer to correctly check checkout permissions against the Asset class instead of PredefinedKit.
2024-12-17 16:06:10 -06:00
akemidx
f6dbda4056 fixing count to not used trashed 2024-12-17 17:03:46 -05:00
akemidx
5992d2a1d2 adding to controller 2024-12-17 15:43:13 -05:00
Marcus Moore
a53976967a Use correct id 2024-12-17 10:31:35 -08:00
Marcus Moore
1be7508340 Add simple tests to ensure views render 2024-12-16 17:45:10 -08:00
Marcus Moore
af135fa42c Add permissions property to group factory 2024-12-16 17:37:58 -08:00
Marcus Moore
7aa5195e87 Add tests for license checkin 2024-12-16 14:39:24 -08:00
snipe
04c3481734 Merge pull request #15973 from uberbrady/next_try_accessories_to_locations_rebased
Next try accessories to locations rebased
2024-12-16 20:25:25 +00:00
Brady Wetherington
582b462326 Removed inadvertently added files. 2024-12-16 16:55:54 +00:00
snipe
b233715796 Fixed property
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
b93fc80011 Fixed admin -> adminuser property
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
41a6b34768 Added new test
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
17a6a871ae Updated comment
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
5fceef1dc3 Updated routes with new endpoints
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
9b04d2e51c Updated route in view
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
51b426f0b4 Added accessory transformer to assets transformer
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
021c4f2598 Added assets endpoint
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
ac96b8d4ae Added assignedAccessories method
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
bc8719e336 Specify created_by in the API call as well
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
bd38d64e66 Removed create() method
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
74b5d6d12b Updated transformer
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
ddd11939a5 No need to include assigned_to
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
3caa5f2042 Fixed user_id to created_by
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
5226d507d4 Updated route
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
274b659905 Added new route
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
a0b9714d72 Updated user_id to created_by
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
36aea52ae0 Fixed variable in test
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:34 +00:00
snipe
e0643cd744 Added currency and history icons
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:35:30 +00:00
snipe
27fab0f573 Use history icon
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:34:51 +00:00
snipe
183a4d49d8 Refactor of #15235 - added accessory checkout to locations, assets
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 16:34:49 +00:00
snipe
edacc4eb54 Merge pull request #15446 from Godmartinz/barcode_settings_hide
Refactored Barcode Settings into Label Settings
2024-12-16 16:09:34 +00:00
snipe
1e7d7a147b Merge pull request #15972 from snipe/upgrade_fontawesome
Updated font awesome
2024-12-16 15:55:14 +00:00
snipe
8ee549efbf Bumped font-awesome to 6.7.0
Signed-off-by: snipe <snipe@snipe.net>
2024-12-16 15:51:23 +00:00
Marcus Moore
699476da90 Scaffold and implement some license checkin tests 2024-12-12 17:33:34 -08:00
Marcus Moore
d763feb803 Add consumable ui tests 2024-12-12 16:41:46 -08:00
Marcus Moore
d3bfa75251 Add accessory ui tests 2024-12-12 14:30:58 -08:00
Godfrey M
833af55806 Merge branch 'develop' into barcode_settings_hide 2024-12-12 12:05:29 -08:00
snipe
d1246c65bd Merge pull request #15963 from snipe/localization/updated_strings_2024-12-12 2024-12-12 19:24:09 +00:00
snipe
38db890660 Updated strings
Signed-off-by: snipe <snipe@snipe.net>
2024-12-12 19:22:56 +00:00
snipe
01c18f6303 Merge pull request #15962 from snipe/fixes_rb-18772 2024-12-12 17:40:40 +00:00
snipe
c5e2aed164 Check for assigned (not assigned_to) before trying to present() the name on bulk delete assets
Signed-off-by: snipe <snipe@snipe.net>
2024-12-12 17:37:08 +00:00
snipe
4d1d2fedb7 Merge pull request #15960 from uberbrady/improve_restore_cleaner_utf8 2024-12-12 17:05:10 +00:00
snipe
ab6363a124 Merge pull request #15959 from snipe/remove_settings_api_endpoints 2024-12-12 16:53:15 +00:00
Brady Wetherington
a0e7dcf4ff Fixes to 'clean' mode to better handle character sets and zero-values 2024-12-12 16:50:36 +00:00
snipe
da33f1815a Removed index and destroy settinga API endpoints
Signed-off-by: snipe <snipe@snipe.net>
2024-12-12 16:46:19 +00:00
snipe
f089d1f0a4 Merge pull request #15944 from marcusmoore/bug/required-display-for-selects 2024-12-12 01:42:26 +00:00
spencerrlongg
72f58b0405 leaving this here for notes etc 2024-12-11 19:13:51 -06:00
snipe
4bd6c2171c Merge pull request #15957 from Godmartinz/fix-checkout-notif-parameters 2024-12-12 00:38:21 +00:00
Marcus Moore
67494c1b0b Fix validation messages for select2 inputs 2024-12-11 16:26:23 -08:00
Godfrey M
3264149a2c fix other mailables 2024-12-11 16:26:18 -08:00
Marcus Moore
8417fcb604 Revert "Show frontend "required" validation for model and status selects"
This reverts commit 10a7ae8d47.
2024-12-11 16:25:45 -08:00
Godfrey M
331fbb66bd fix other mailables 2024-12-11 16:25:04 -08:00
Godfrey M
400833f834 reversed order of the acceptance and note paramter 2024-12-11 16:14:06 -08:00
snipe
a7e6b8ea3f Merge pull request #15956 from marcusmoore/bug/sc-27731 2024-12-11 23:52:33 +00:00
Marcus Moore
6e31d0f2c3 Use appropriate category for licenses when seeding 2024-12-11 15:39:41 -08:00
snipe
b9a660683c Merge pull request #15955 from ubc-cpsc/bugfix/GHSA-c2pc-g5qf-rfrf 2024-12-11 20:42:13 +00:00
Joël Pittet
014350a26b Fix league/commonmark's quadratic complexity bugs may lead to a denial of service 2024-12-11 12:04:45 -08:00
snipe
06a0ac895d Fixed #15928 - updated method name to setCreatedBy from SetUserId
Signed-off-by: snipe <snipe@snipe.net>
2024-12-11 18:34:18 +00:00
snipe
34a47e9e44 Add @sgross-emlix as a contributor 2024-12-11 18:09:53 +00:00
snipe
f5a9e4bafa Fixes #15952 - fixed typo in content-type
Signed-off-by: snipe <snipe@snipe.net>
2024-12-11 18:09:19 +00:00
snipe
66339481cf Merge pull request #15954 from Godmartinz/null_fix-for-reminder-command 2024-12-11 17:38:59 +00:00
Godfrey M
2e97b56deb add null safe operator to acceptance reminder 2024-12-11 09:27:27 -08:00
akemidx
8adb62fd3a width fix 2024-12-10 19:17:01 -05:00
akemidx
43537d414b removed redundancy. 2024-12-10 16:42:55 -05:00
snipe
5014a95d9a Updated supported versions
Signed-off-by: snipe <snipe@snipe.net>
2024-12-10 13:27:59 +00:00
akemidx
f2fab57187 cancel button pull to left.
added pull right to save
2024-12-09 19:28:57 -05:00
Marcus Moore
10a7ae8d47 Show frontend "required" validation for model and status selects 2024-12-09 16:09:51 -08:00
snipe
5f0efd8cdb Merge pull request #15940 from snipe/reove_gh_templates 2024-12-09 19:35:31 +00:00
spencerrlongg
03c90d7b60 note, will come back to this once question is answered 2024-12-09 13:29:06 -06:00
snipe
31155b5351 Removed issue/PR templates
Signed-off-by: snipe <snipe@snipe.net>
2024-12-09 19:28:51 +00:00
snipe
cc8f72c3f9 Merge pull request #15914 from Godmartinz/send_reminder_emails 2024-12-09 19:23:13 +00:00
snipe
a0bab70def Merge pull request #15939 from uberbrady/fix_ldap_asset_location_updater 2024-12-09 17:56:23 +00:00
Brady Wetherington
b5c8251539 Only update asset locations to assets checked out to users. 2024-12-09 17:40:10 +00:00
snipe
0149ed75fd Merge pull request #15565 from spencerrlongg/bug/sc-25921 2024-12-09 17:00:10 +00:00
snipe
f72635955d Merge pull request #15925 from Godmartinz/refactor-unaccepted-assets-reminder-notif 2024-12-09 16:40:21 +00:00
Godfrey M
5120cddda4 delete notification version of reminder 2024-12-05 16:02:55 -08:00
Godfrey M
97398f1e68 adds testing to unaccepted reminder command 2024-12-05 12:41:33 -08:00
Godfrey M
281ff6ad5d wrap comment 2024-12-05 11:52:34 -08:00
Godfrey M
9d49b01958 cleans up the Unaccepted Asset reminder variables 2024-12-05 11:46:56 -08:00
Godfrey M
3f8916ea2e fix duplicate emails being sent in unaccepted reminder command 2024-12-05 11:37:03 -08:00
Godfrey M
f6b9ae6aee missed a spot 2024-12-05 10:18:35 -08:00
Godfrey M
de41def2b3 fixed conditionals 2024-12-05 10:15:57 -08:00
Godfrey M
52b051e940 add mail class for unaccepted assets reminders 2024-12-05 10:08:39 -08:00
snipe
a137e31797 Merge pull request #15920 from akemidx/asset-category-in-emails 2024-12-04 21:46:43 +00:00
akemidx
ca0f8ace99 fixed 2024-12-03 16:57:28 -05:00
akemidx
6252f0ac5e layout / added category id 2024-12-03 16:10:42 -05:00
Godfrey M
b85b4b1e1b remove table-responsive div from several index bladees 2024-12-03 10:26:14 -08:00
snipe
43d66a8fcc Merge pull request #15918 from Godmartinz/mail_name_fix
Adds `MAIL_FROM_NAME` to mail envelope
2024-12-03 17:17:26 +00:00
Godfrey M
e5284c03e2 fix typo 2024-12-03 09:10:50 -08:00
Godfrey M
16283b8fc0 adds mail from name to mail envelope 2024-12-03 09:08:39 -08:00
Godfrey M
983b78edd9 add property check 2024-12-02 14:27:04 -08:00
snipe
2ad1407d51 Merge pull request #15909 from Godmartinz/checkin-out-notifications
Fixed general webhook option notifications not firing
2024-12-02 22:03:32 +00:00
Godfrey M
ab67c48352 fix unaccepted assets report resend acceptance 2024-12-02 13:03:09 -08:00
Marcus Moore
1d0d14876c Harden asset checkout validation 2024-12-02 12:49:02 -08:00
snipe
14730184c9 Fixed sr-only blank text
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 18:48:02 +00:00
snipe
8ff099e802 Merge pull request #15910 from marcusmoore/chore/include-testing-variable
Included MAIL_FROM_ADDR in phpunit configuration
2024-12-02 18:34:08 +00:00
Marcus Moore
ea1f8328b9 Include MAIL_FROM_ADDR in phpunit configuration 2024-12-02 10:23:02 -08:00
Godfrey M
99464fcd86 fixes general webhook not firing 2024-12-02 10:19:24 -08:00
snipe
93d4d24194 Fixed funky layout in importer
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:56:15 +00:00
snipe
1bbe9bf6c9 Merge remote-tracking branch 'origin/develop' 2024-12-02 17:43:03 +00:00
snipe
6874f703bf Some CSS twiddling
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:42:26 +00:00
snipe
a7d5b3944e Merge pull request #15908 from snipe/revert-15892-bug/harden-checkout-validation
Revert "Hardened asset checkout validation by requiring integer"
2024-12-02 17:42:02 +00:00
Marcus Moore
b5e83899c6 Revert "Hardened asset checkout validation by requiring integer" 2024-12-02 09:37:21 -08:00
snipe
dcd586e3cd Merge remote-tracking branch 'origin/develop' 2024-12-02 17:17:20 +00:00
snipe
1246ab1de7 Fixed bulk user form in user detail view
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:14:36 +00:00
snipe
5a4a3aa8f3 Merge remote-tracking branch 'origin/develop' 2024-12-02 15:59:25 +00:00
snipe
2220828b00 Merge pull request #15904 from snipe/fixes/#15901
Fixed #15901 - re-added required indicator on text and select custom fields
2024-12-02 15:51:32 +00:00
snipe
716c67d4b8 Fixed #15901 - re-added required indicator on text and select custom fields
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 15:45:07 +00:00
snipe
ee4a54be24 Fixed order by notes for users
Signed-off-by: snipe <snipe@snipe.net>
2024-11-27 15:48:13 +00:00
snipe
46be1ada60 Merge pull request #15892 from marcusmoore/bug/harden-checkout-validation
Hardened asset checkout validation by requiring integer
2024-11-27 14:55:11 +00:00
snipe
e1c0a80c20 Merge remote-tracking branch 'origin/develop' 2024-11-27 13:52:03 +00:00
snipe
2cb1b6d462 Use transformer on API update
Signed-off-by: snipe <snipe@snipe.net>
2024-11-27 13:51:53 +00:00
snipe
37ac7fe25c Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2024-11-26 20:38:46 +00:00
snipe
11f83b4cd9 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2024-11-26 20:12:14 +00:00
snipe
0f1d10bd69 Dev assets
Signed-off-by: snipe <snipe@snipe.net>
2024-11-26 20:12:09 +00:00
snipe
82108f8a18 Merge pull request #15208 from akemidx/feature/sc-26415
FEATURE: Option for Notes to be Required on Asset Checkin/Checkout
2024-11-26 20:11:04 +00:00
akemidx
a7dae10a82 fixing line 2024-11-26 15:06:25 -05:00
akemidx
2727210c78 requested changes from pr 2024-11-26 15:04:54 -05:00
Marcus Moore
fcbd5dcae5 Harden asset checkout validation by requiring ints 2024-11-26 11:33:17 -08:00
snipe
fa80716320 Merge pull request #15890 from Godmartinz/mobile-view-fix
Mobile view fix
2024-11-26 19:13:39 +00:00
Godfrey M
15c9df0ab1 fix mobile view for asset and user 2024-11-26 10:59:31 -08:00
snipe
6ee0c93c87 Merge remote-tracking branch 'origin/develop' 2024-11-26 18:47:22 +00:00
snipe
37e091adbb Merge pull request #15889 from Godmartinz/Fix-user-API-location-update
Fixed Users API `update` from clearing `location_id` unnecessarily
2024-11-26 18:45:20 +00:00
Godfrey M
021e82927e fixed mobile view of assets 2024-11-26 10:42:59 -08:00
Godfrey M
60642cd902 fix user api update from clearing location_id unnecessarily 2024-11-26 09:58:17 -08:00
snipe
fd07550803 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2024-11-25 14:19:05 +00:00
snipe
30b481bc93 Updated version
Signed-off-by: snipe <snipe@snipe.net>
2024-11-25 14:18:31 +00:00
snipe
25bfd3e845 Merge remote-tracking branch 'origin/develop' 2024-11-25 13:05:28 +00:00
snipe
0f18ee86f7 Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2024-11-25 13:05:05 +00:00
snipe
4a5f65616f Merge remote-tracking branch 'origin/develop' 2024-11-25 12:56:08 +00:00
snipe
f125c90c94 Merge pull request #15870 from marcusmoore/bug/sc-26857
Always call resizeCanvas
2024-11-22 00:59:36 +00:00
akemidx
0d608552ef orange bar for requirednessness 2024-11-21 19:09:54 -05:00
akemidx
dd223fc215 commit for testing 2024-11-21 19:01:27 -05:00
Marcus Moore
87756cac66 Remove debugging 2024-11-21 14:30:17 -08:00
Marcus Moore
9f49c25401 WIP: move signature pad creation up and add debugging 2024-11-21 13:12:13 -08:00
Marcus Moore
d0378070c8 Always call resizeCanvas 2024-11-21 12:38:53 -08:00
snipe
9f395d1f6a Merge remote-tracking branch 'origin/develop' 2024-11-21 19:58:49 +00:00
snipe
feba860a90 Merge pull request #15850 from akemidx/bug/sc-27448
Adding colon to password translation string
2024-11-21 19:58:04 +00:00
akemidx
424316df9f fixing colon placements 2024-11-21 14:32:22 -05:00
snipe
b7aedb7dcb Merge pull request #15818 from spencerrlongg/bug/sc-27213
Resolves Validation for Encrypted Numeric and Alpha Custom Fields
2024-11-21 19:25:27 +00:00
snipe
8b38dc83f6 Merge pull request #15851 from marcusmoore/testing/api-component-checkin
Fixed bug in component checkins via api
2024-11-21 19:24:12 +00:00
snipe
ad2b85393b Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/js/build/app.js
#	public/js/build/vendor.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2024-11-21 19:23:10 +00:00
snipe
85c1b0ec57 [Snyk] Upgrade jquery-ui from 1.14.0 to 1.14.1 #15868
Signed-off-by: snipe <snipe@snipe.net>
2024-11-21 19:22:08 +00:00
snipe
b99b56b32b Merge remote-tracking branch 'origin/develop' 2024-11-21 14:41:42 +00:00
snipe
2a21cce4f5 Merge pull request #15839 from Godmartinz/fix-mail-envelope-variable
replace env with config variable for from address
2024-11-21 14:41:04 +00:00
snipe
3a683d84a7 Merge pull request #15859 from Godmartinz/remove_name_null_warning
Adds a ternary for blank emails for mail notifications
2024-11-21 14:38:03 +00:00
snipe
e05f443886 Merge pull request #15864 from marcusmoore/testing/api-component-checkout
Added tests for component checkout api endpoint
2024-11-21 14:37:45 +00:00
Marcus Moore
d65254c4ec Add tests for component checkout api endpoint 2024-11-20 15:34:08 -08:00
Godfrey M
6969b8be47 remove the changes to mail.from.address default 2024-11-20 14:25:57 -08:00
snipe
0a3bca078c Merge pull request #15860 from marcusmoore/tests/switch-to-file-for-sqlite-in-action
Improved speed of sqlite tests in GitHub Actions
2024-11-20 22:09:11 +00:00
Marcus Moore
9bbd802fce Use database file for sqlite in GitHub Action for tests 2024-11-20 12:33:58 -08:00
Marcus Moore
f932a4fc76 Always run validation on checkin 2024-11-20 12:25:06 -08:00
Godfrey M
f7c1fc852d no checked out to email doesnt trigger a 500 2024-11-20 08:29:28 -08:00
snipe
5522def054 Merge pull request #15854 from NebelKreis/feature/add-category-to-asset-mails
Added #15803: Category Field to Asset Related Mails
2024-11-20 13:51:34 +00:00
snipe
8cd4b3303e Merge pull request #15847 from NebelKreis/feature/hardware-label-generation-api
Added #15677: API Route for Generating Asset Labels
2024-11-20 11:14:28 +00:00
NebelKreis
d5c141dc59 Fix: Removed setting validation as it is not strictly necessary 2024-11-20 11:55:48 +01:00
NebelKreis
7618169d79 Feature: Added category to asset requested email 2024-11-20 11:22:19 +01:00
NebelKreis
eb026248a8 Feature: Added category to asset acceptance/decline email 2024-11-20 10:59:59 +01:00
NebelKreis
4304af0508 Feature: Added category to asset checkin email 2024-11-20 10:27:37 +01:00
NebelKreis
61eff02fcb Feature: Added category to asset checkout email 2024-11-20 10:27:22 +01:00
snipe
cca76005a2 Merge pull request #15852 from marcusmoore/testing/ui-delete-component
Added tests around deleting component via ui
2024-11-20 01:27:19 +00:00
Marcus Moore
54f5f46e31 Improve check for image existence 2024-11-19 16:44:42 -08:00
Marcus Moore
51a0767be9 Add tests around deleting component via ui 2024-11-19 16:44:19 -08:00
Marcus Moore
da4c877ed8 Use === over == 2024-11-19 16:13:53 -08:00
Marcus Moore
e8cad0df69 Formatting 2024-11-19 16:13:30 -08:00
Marcus Moore
59fe53842d Add tests around component check ins via api 2024-11-19 16:11:23 -08:00
akemidx
1e513312eb adding colon to password translation 2024-11-19 16:58:10 -05:00
Godfrey M
99de639be4 fix mail.from.address variables in config, simplify envelop[ variable 2024-11-19 09:32:11 -08:00
Godfrey M
b513575643 add testing example var MAIL_FROM_ADDR 2024-11-19 09:10:52 -08:00
spencerrlongg
22602c7997 use existing validation strings 2024-11-19 08:59:47 -06:00
snipe
d10f118128 Merge remote-tracking branch 'origin/develop' 2024-11-19 13:55:55 +00:00
snipe
5a5f1082fb Merge pull request #15848 from snipe/chore/sc-27531
Indicate requiredness on status select on bulk user checkin+delete
2024-11-19 13:55:22 +00:00
snipe
30a6a678d4 Indicate requiredness on status select on bulk user checkin+delete
Signed-off-by: snipe <snipe@snipe.net>
2024-11-19 13:50:18 +00:00
snipe
4384f8dcbb Merge remote-tracking branch 'origin/develop' 2024-11-19 13:32:02 +00:00
snipe
d576016a1c Merge pull request #15845 from snipe/bug/sc-27523
Fixed variable name to correctly log created_by and date for bulk user delete/checkin
2024-11-19 13:29:21 +00:00
snipe
15a31bd141 Fixed variable name
Signed-off-by: snipe <snipe@snipe.net>
2024-11-19 13:22:44 +00:00
NebelKreis
5639a84d90 Feature: Added API route for asset label generation 2024-11-19 14:16:58 +01:00
NebelKreis
66d6b01307 Feature: Added translations for label generation API endpoint 2024-11-19 14:16:06 +01:00
NebelKreis
889aff43c2 Feature: Added API endpoint for generating asset labels 2024-11-19 14:13:05 +01:00
snipe
8f7223128f Merge pull request #15757 from akemidx/bug/sc-26944
FIXED: Bulk Delete page not showing full information
2024-11-19 12:34:57 +00:00
snipe
53cfcb7143 Merge pull request #15843 from snipe/viclou-patch-1
Update README.md - small typo
2024-11-19 10:50:28 +00:00
snipe
d60ac0c78d Merge pull request #15840 from marcusmoore/fixes/migration-rollbacks
Fixed a couple migrate:rollback issues
2024-11-19 10:20:58 +00:00
victoria
a328c7a64c Update README.md
typo mosrt to most under Join our Community links
2024-11-18 23:03:31 -10:00
Godfrey M
f533cdc07a update default from address to alternate config variable 2024-11-18 12:39:28 -08:00
Marcus Moore
f5206078d4 Fix rollback for webhook_endpoint by using correct column type 2024-11-18 12:25:02 -08:00
Marcus Moore
0209d27081 Fix rollback for created_by 2024-11-18 12:24:47 -08:00
Godfrey M
d14f43fbbe replace env with config variable for from address 2024-11-18 10:54:03 -08:00
spencerrlongg
b71a90a3c5 this should be all it takes to fix this, i think 2024-11-18 12:44:24 -06:00
snipe
8d321edca8 Merge remote-tracking branch 'origin/develop' 2024-11-15 18:59:40 +00:00
snipe
3fb543bf03 Merge pull request #15828 from snipe/bug/sc-27500
Fixed admin user not showing in download all activity report
2024-11-15 18:59:07 +00:00
snipe
e8d1093376 Fixed admin user not showing in download all activity report
Signed-off-by: snipe <snipe@snipe.net>
2024-11-15 18:51:45 +00:00
snipe
66e82a345a Merge remote-tracking branch 'origin/develop' 2024-11-15 13:18:25 +00:00
snipe
187e231c0e Fixed #15825
Signed-off-by: snipe <snipe@snipe.net>
2024-11-15 13:18:15 +00:00
snipe
caf92748fc Merge remote-tracking branch 'origin/develop' 2024-11-14 20:57:18 +00:00
snipe
09b7db5103 Additional fix for #15817
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 20:57:09 +00:00
snipe
5381c4e1e3 Merge remote-tracking branch 'origin/develop' 2024-11-14 20:53:17 +00:00
snipe
add3edba20 Merge pull request #15822 from snipe/add_model_number_to_view_assets
Fixed #15820 - added model number to view-assets
2024-11-14 20:52:44 +00:00
snipe
b4a2a4e1ec Fixed #15820 - added model number to view-assets
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 20:51:18 +00:00
snipe
f2faf58786 Merge pull request #15817 from Godmartinz/label_fix
Removes asset tag if 2D code is not present
2024-11-14 20:49:31 +00:00
spencerrlongg
124f9c84c2 oops, something went wrong translation 2024-11-14 13:45:47 -06:00
snipe
96a4391d5c Merge remote-tracking branch 'origin/develop' 2024-11-14 19:05:08 +00:00
snipe
7b7a848a43 Merge pull request #15819 from snipe/fixes_asset_file_deletion
Corrected route name for delete action on filestable on assets
2024-11-14 18:01:56 +00:00
snipe
636e466b8a Fixed delete model file route
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 17:58:20 +00:00
snipe
838a6d03bd Corrected route name for filestable on assets
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 17:51:49 +00:00
spencerrlongg
8465806536 rm unnecessary imports 2024-11-14 11:51:41 -06:00
Godfrey M
0f1f6bc6a5 fixed rest of dymos 2024-11-14 09:45:52 -08:00
spencerrlongg
95cd779334 translations and attribute names 2024-11-14 11:42:35 -06:00
snipe
0a7d1a27ee Merge remote-tracking branch 'origin/develop' 2024-11-14 17:10:16 +00:00
snipe
306c97b5b5 Merge pull request #15816 from Godmartinz/Teams_fix
replace the via() channel for MS Teams (deprecated)
2024-11-14 17:09:47 +00:00
Godfrey M
23038ab209 removes tag if qr code is not present 2024-11-14 08:54:12 -08:00
Godfrey M
702621ec57 replace the via channel class 2024-11-14 08:38:18 -08:00
snipe
0be123b115 Merge remote-tracking branch 'origin/develop' 2024-11-14 14:47:18 +00:00
snipe
c093b4531d Fixed anchor links
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 14:47:09 +00:00
snipe
517e923f70 Merge remote-tracking branch 'origin/develop' 2024-11-14 14:42:44 +00:00
snipe
a0b8483878 Removed extra sort
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 14:41:11 +00:00
snipe
075e3503b1 Removed Twitter links
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 14:38:51 +00:00
snipe
d427d9e5af Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 12:25:10 +00:00
snipe
940f1c2ecc Fixed #15812 - regression in action_type search
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 12:24:15 +00:00
snipe
3e131d515d Add @maciej-poleszczyk as a contributor 2024-11-14 11:06:55 +00:00
snipe
364410a586 Merge pull request #15558 from maciej-poleszczyk/develop
Fixing #15064 - to not fail ldap sync on single data issue with ldap …
2024-11-14 11:06:18 +00:00
snipe
70d9ef428c Merge pull request #15811 from snipe/small_fix_for_embiguous_created_by
Specify table name in select for models controller
2024-11-14 10:20:16 +00:00
snipe
4e28650ca9 Specify table name in select
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 10:18:39 +00:00
spencerrlongg
8c6869fd34 note and rm dump and comment 2024-11-13 21:59:32 -06:00
spencerrlongg
7e7cbc4cc8 seems to work just fine now, needs translations 2024-11-13 21:54:25 -06:00
spencerrlongg
25163d1756 working except for null 🤔 2024-11-13 21:46:23 -06:00
spencerrlongg
3982201d0e this should work in theory - local is screwy though 2024-11-13 21:38:09 -06:00
snipe
647ef6a4b7 Versioning stuff is hard - fixed v7.0.14 to v7.1.14
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 01:11:29 +00:00
snipe
a060f603b5 Versioning stuff is hard - fixed v7.0.14 to v7.1.14
Signed-off-by: snipe <snipe@snipe.net>
2024-11-14 01:11:01 +00:00
snipe
f7dcfca66c Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2024-11-13 23:46:04 +00:00
akemidx
efeef5cdc4 removing || parts of if else 2024-11-13 18:29:58 -05:00
akemidx
9cb411c500 removing required form div container 2024-11-13 18:06:04 -05:00
Marcus Moore
92f33c08f7 Merge branch 'develop' into saving_custom_report_template 2024-11-13 14:54:00 -08:00
spencerrlongg
4841b89109 scratch code for this issue 2024-11-13 12:45:39 -06:00
Marcus Moore
53de97db4e Merge branch 'develop' into saving_custom_report_template 2024-11-12 10:36:07 -08:00
Marcus Moore
5574c5fa49 Improve comments 2024-11-12 10:35:22 -08:00
Marcus Moore
7373e2019c Improve comment 2024-11-12 10:01:32 -08:00
Marcus Moore
4bb19152c4 Move test 2024-11-07 17:01:47 -08:00
Marcus Moore
dc0b8c7572 Inline route helpers in tests 2024-11-07 16:54:55 -08:00
Marcus Moore
b8265d54bb Improve comment 2024-11-07 16:42:55 -08:00
Marcus Moore
0e3efdfe87 Add string to name validation 2024-11-07 16:40:54 -08:00
Marcus Moore
363ec841d1 Re-introduce soft deletes 2024-11-07 16:40:37 -08:00
Marcus Moore
f8d0ddb3f7 Improve template name input 2024-11-07 14:17:20 -08:00
Marcus Moore
4aa5961860 Update page titles 2024-11-07 12:13:20 -08:00
Marcus Moore
7862b74e99 Inline fields when updating 2024-11-07 11:03:03 -08:00
Marcus Moore
c5710b858e Add test validation test for update method and remove name uniqueness constraint 2024-11-07 11:02:10 -08:00
Marcus Moore
8873137ed0 Scaffold updating template name 2024-11-06 12:29:31 -08:00
Marcus Moore
37d792352d Update page title dynamically 2024-11-06 12:11:35 -08:00
NebelKreis
52e4414bc5 Feature: Added asset ID option to 2D barcode in label generation 2024-11-05 13:41:34 +01:00
NebelKreis
f96b7c5056 Feature: Added data type context for select options with "URL:" and "Data:" labels, including translations 2024-11-05 13:40:33 +01:00
akemidx
82c38a8b18 or statement for if/else 2024-11-04 20:43:18 -05:00
akemidx
2f5e468b3c this fixes the assigned to piece 2024-11-04 19:48:34 -05:00
Marcus Moore
4daa8e7c63 Indenting 2024-11-04 12:49:44 -08:00
Marcus Moore
41f25341fd Populate select with selected template 2024-11-04 12:32:05 -08:00
Godfrey M
de77eda33d another line indent 2024-10-31 14:38:32 -07:00
Godfrey M
ee58fcc898 moved lines in for labels 2024-10-31 14:37:08 -07:00
Godfrey M
ae3cb7b37b adds migration to update new with old if blank and remove old barcod variables 2024-10-31 14:35:13 -07:00
Marcus Moore
5f83cb6a14 Use route model binding 2024-10-31 12:34:06 -07:00
Marcus Moore
ae24b73b32 Fix action_type from "created" to" create" 2024-10-31 12:19:41 -07:00
Marcus Moore
4aedbb52fa Implement activity logging transformer tests 2024-10-31 12:15:05 -07:00
Marcus Moore
20dab4d89d Simplify tests 2024-10-30 16:50:55 -07:00
Marcus Moore
e871481042 Add group 2024-10-30 16:48:42 -07:00
akemidx
f10807661d fixing bulk delete table 2024-10-30 19:26:52 -04:00
Marcus Moore
d727b03d95 Implement activity log test 2024-10-30 14:25:07 -07:00
Marcus Moore
0cc3031864 Implement deleted log 2024-10-30 14:05:27 -07:00
Marcus Moore
59e6874a4a Scaffold test cases 2024-10-30 13:07:51 -07:00
Marcus Moore
930ef3fd11 Work on tests for activity logging 2024-10-30 12:22:59 -07:00
Marcus Moore
35f8a71c71 Test organization 2024-10-29 16:17:12 -07:00
Marcus Moore
86762c5e90 Remove marker test case 2024-10-29 16:12:34 -07:00
Marcus Moore
3c75fc2ced Move test 2024-10-29 16:12:13 -07:00
Marcus Moore
702edf7908 Remove x from select dropdown 2024-10-29 15:34:00 -07:00
Marcus Moore
54b4db86d2 Revert "Add soft deletes"
This reverts commit 0eadab49f1.
2024-10-29 15:33:49 -07:00
Marcus Moore
0eadab49f1 Add soft deletes 2024-10-29 15:26:18 -07:00
Marcus Moore
45e98e0282 Merge branch 'develop' into saving_custom_report_template 2024-10-29 15:20:52 -07:00
Marcus Moore
b97c54a54b Improve handling of old data for text inputs 2024-10-29 11:25:54 -07:00
NebelKreis
e22296fd79 Feature: Added serial number option to 2D barcode in label generation 2024-10-29 13:49:05 +01:00
NebelKreis
6c17d7d732 Fix: Corrected path in translation function
Updated the translation function to use the correct path, ensuring that instead of displaying the translation path as plain text, the appropriate translation is now shown as intended.
2024-10-29 11:45:29 +01:00
Marcus Moore
0d58ac61bc Make options required in model 2024-10-28 14:48:19 -07:00
Marcus Moore
7c08fbe144 Update test name 2024-10-28 14:43:05 -07:00
Marcus Moore
7777147af5 Add custom-reporting group 2024-10-28 14:42:44 -07:00
Marcus Moore
2042733dc0 Switch to array syntax 2024-10-28 14:37:52 -07:00
Marcus Moore
d4cf392387 Organize tests 2024-10-28 14:24:47 -07:00
Marcus Moore
c881727747 Add redirect for missing template 2024-10-28 14:12:19 -07:00
Marcus Moore
ee00699cb3 Remove unused strings 2024-10-28 13:07:29 -07:00
Marcus Moore
b45749af16 Update translation strings 2024-10-28 13:06:19 -07:00
Marcus Moore
54dec8d30d Add translations 2024-10-28 12:56:42 -07:00
NebelKreis
6f3fb47e4a Feature: Added asset tag option to barcode in label generation 2024-10-28 15:39:04 +01:00
Marcus Moore
7238238d1f Use created_by instead of user_id 2024-10-23 16:40:03 -07:00
Marcus Moore
84f6638f50 Add authorization check 2024-10-23 16:11:10 -07:00
Marcus Moore
e390a95bd3 Improve back button behavior 2024-10-23 15:42:09 -07:00
Marcus Moore
3616c92148 Reflash template name 2024-10-23 15:40:54 -07:00
Marcus Moore
2fcc7e1188 Make template name required 2024-10-23 15:24:58 -07:00
Marcus Moore
8a06f4ad82 Improve label 2024-10-23 15:21:02 -07:00
Marcus Moore
853e14f369 Fix width 2024-10-23 13:24:06 -07:00
Marcus Moore
c1aa33862d Add error message for eol start and end fields 2024-10-23 13:22:21 -07:00
Marcus Moore
30dc5fa0cf Allow retrieving eol start and end dates 2024-10-23 13:21:44 -07:00
Marcus Moore
94e168fa15 Fix label 2024-10-23 13:17:20 -07:00
Marcus Moore
4217d7402b Improve tests 2024-10-22 17:50:40 -07:00
Marcus Moore
3d28a9090c Improve test 2024-10-22 17:38:22 -07:00
Marcus Moore
271de1eceb Improve tests 2024-10-22 17:37:23 -07:00
Marcus Moore
b6aae1f8a9 Group routes 2024-10-22 17:30:34 -07:00
Marcus Moore
c313a78c3c Remove outdated @since annotations 2024-10-22 17:04:23 -07:00
Marcus Moore
37d65dac3d Add todo 2024-10-22 17:01:37 -07:00
Marcus Moore
5ebf013d4e Add comment 2024-10-22 16:54:38 -07:00
Marcus Moore
02c22c9efb Add test case 2024-10-22 16:52:59 -07:00
Marcus Moore
d953519db6 Merge branch 'develop' into saving_custom_report_template 2024-10-22 15:11:18 -07:00
Marcus Moore
dd97e4ea82 Update permission tests 2024-10-22 14:22:19 -07:00
Marcus Moore
a20e03fce9 Merge branch 'develop' into saving_custom_report_template 2024-10-21 17:13:15 -07:00
akemidx
f0d3a6e2d3 removing some comments/merging in develop 2024-10-16 18:35:31 -04:00
akemidx
5cb940c2ee Merge branch 'refs/heads/upstream/dev' into feature/sc-26415 2024-10-16 18:32:36 -04:00
Marcus Moore
5306e1cd15 Merge branch 'develop' into saving_custom_report_template
# Conflicts:
#	resources/views/partials/forms/edit/location-select.blade.php
#	resources/views/partials/forms/edit/manufacturer-select.blade.php
#	resources/views/partials/forms/edit/model-select.blade.php
#	resources/views/partials/forms/edit/supplier-select.blade.php
#	resources/views/reports/custom.blade.php
2024-10-15 12:32:01 -07:00
akemidx
299e743848 weird requesting 2024-10-08 15:51:53 -04:00
akemidx
0c84904bf9 un'required'ing the rule. not sure what's breaking here but looking 2024-10-02 18:56:41 -04:00
akemidx
e00a1aec02 note box is now missing when unchecking setting 2024-10-02 18:52:33 -04:00
akemidx
06e3bb7fd1 requested changes 2024-10-02 18:37:11 -04:00
akemidx
492e686b7a Merge remote-tracking branch 'origin/feature/sc-26415' into feature/sc-26415
# Conflicts:
#	app/Http/Controllers/Assets/AssetCheckinController.php
#	app/Http/Controllers/Assets/AssetCheckoutController.php
#	app/Http/Requests/AssetCheckinRequest.php
2024-10-02 18:17:38 -04:00
akemidx
17706f150e requested changes 2024-10-02 18:15:53 -04:00
akemidx
6fef127cd1 missing closing ) 2024-10-02 18:15:53 -04:00
akemidx
f45b836010 backend form validation. +cleanup 2024-10-02 18:15:53 -04:00
akemidx
925aea8531 back to having tests pass. needed to comment out the notes rules() i added 2024-10-02 18:15:53 -04:00
akemidx
bd6698de2a fixing some formatting 2024-10-02 18:15:53 -04:00
akemidx
515f59fed9 more test work 2024-10-02 18:15:52 -04:00
akemidx
5e74b109d9 front end changes/updates from gh 2024-10-02 18:15:52 -04:00
akemidx
8b643cb3b9 note field optional 2024-10-02 18:15:52 -04:00
akemidx
700647c53f setting created 2024-10-02 18:15:52 -04:00
akemidx
081c5706c4 required, but not optional 2024-10-02 18:15:52 -04:00
akemidx
73a059c9ac missing closing ) 2024-10-01 16:37:30 -04:00
spencerrlongg
4f9f035c69 added a way to manipulate validator attribute names 2024-09-30 20:09:00 -05:00
spencerrlongg
cd3059f790 rm numbers too. :( 2024-09-30 13:38:52 -05:00
Marcus Moore
e974c96eda Update to created_by 2024-09-26 12:41:16 -07:00
Marcus Moore
2004e58b53 Merge branch 'develop' into updated_ad_hoc_notes 2024-09-26 12:29:28 -07:00
Marcus Moore
eb2d7b1f4f Move comment location 2024-09-26 12:14:00 -07:00
Marcus Moore
47763d1e1a Add tests for adding notes to assets 2024-09-26 12:13:36 -07:00
Godfrey M
44447b85c9 Merge branch 'develop' into barcode_settings_hide 2024-09-26 11:27:05 -07:00
akemidx
b813dcd9d0 requested changes from github 2024-09-25 18:54:30 -04:00
spencerrlongg
b69596a261 rm dump 2024-09-25 16:21:33 -05:00
spencerrlongg
d02b9933e5 rm comment 2024-09-25 15:38:40 -05:00
spencerrlongg
852a56fa9b should be all good now 2024-09-25 15:30:41 -05:00
spencerrlongg
9df78a9ed0 working! need formatting for error messages etc 2024-09-25 14:00:50 -05:00
spencerrlongg
f3ad89931f hm ok this kind of works 2024-09-25 13:00:35 -05:00
spencerrlongg
21906d8c27 world set up and idea in place 2024-09-25 12:47:56 -05:00
Maciej Poleszczyk
d1e2f9db34 Fixing #15064 - to not fail ldap sync on single data issue with ldap manager 2024-09-25 17:56:49 +02:00
akemidx
3964296ae6 no payload 2024-09-18 17:06:21 -04:00
akemidx
da9a61c28a no payload 2024-09-18 17:01:37 -04:00
Marcus Moore
6e16f589bd Remove reference to old trait 2024-09-17 17:00:06 -07:00
Marcus Moore
6b70443515 Formatting 2024-09-17 16:58:49 -07:00
Godfrey M
c9854d43a5 fixes selection order 2024-09-17 16:56:02 -07:00
Godfrey M
04708ae2b2 only allows PDF417 for new label engine 2024-09-17 16:54:50 -07:00
Godfrey M
bbb9babf27 one more finger 2024-09-17 16:40:18 -07:00
Godfrey M
e5daf35f65 fat finger fix 2024-09-17 16:39:55 -07:00
Godfrey M
87c72953b2 udpated translation 2024-09-17 16:37:17 -07:00
Marcus Moore
89e2d03a81 Fix the deleted assets radio 2024-09-17 16:24:18 -07:00
Godfrey M
fa5651f335 remove getbarcodes, postbarcodes, barcodes settings blade 2024-09-17 16:21:26 -07:00
akemidx
443447a068 added icon to button 2024-09-17 19:16:44 -04:00
Godfrey M
b33c0fc4dd updates getbarcode method and labels view 2024-09-17 16:15:56 -07:00
Marcus Moore
8e0b718a4a Scope Select All checkbox to fields on left side of page 2024-09-17 16:00:32 -07:00
Marcus Moore
b51c505d9e Fix department, manufacturer, and category multi-selects 2024-09-17 15:29:38 -07:00
Marcus Moore
8bba11e1bb Merge branch 'develop' into saving_custom_report_template
# Conflicts:
#	app/Http/Controllers/ReportsController.php
#	resources/views/partials/forms/edit/category-select.blade.php
#	resources/views/partials/forms/edit/company-select.blade.php
#	resources/views/partials/forms/edit/location-select.blade.php
#	resources/views/partials/forms/edit/manufacturer-select.blade.php
#	resources/views/partials/forms/edit/model-select.blade.php
#	resources/views/partials/forms/edit/status-select.blade.php
#	resources/views/reports/custom.blade.php
2024-09-17 15:26:35 -07:00
akemidx
4b54e980e2 added button 2024-09-17 17:56:22 -04:00
Godfrey M
033a56fe6d change variables in hardware labels edit post methods 2024-09-17 12:22:05 -07:00
Godfrey M
3682d9fa6c removed duplicate line from post labels 2024-09-17 11:23:59 -07:00
Godfrey M
ce987b4f6d added front end barcodes to labels 2024-09-17 11:23:38 -07:00
akemidx
74efd850af redoing branch. old branch merge onflicts were waaay too gnarly 2024-09-16 19:30:26 -04:00
akemidx
d262638a63 backend form validation. +cleanup 2024-09-04 18:23:36 -04:00
akemidx
bebb72a04f back to having tests pass. needed to comment out the notes rules() i added 2024-09-04 17:53:19 -04:00
akemidx
77c5035cac fixing some formatting 2024-09-04 17:50:52 -04:00
Godfrey M
df8b1c0240 hides barcode settings if new label engine enabled 2024-09-03 12:29:52 -07:00
akemidx
2901ecbf43 more test work 2024-09-03 15:02:27 -04:00
akemidx
27c120a55e front end changes/updates from gh 2024-08-20 19:07:47 -04:00
akemidx
4e43fa6b9f Merge remote-tracking branch 'upstream/develop' into feature/sc-26415 2024-08-19 21:06:18 -04:00
akemidx
0f0baa207d note field optional 2024-08-01 17:02:35 -04:00
akemidx
3ff1745f56 setting created 2024-08-01 16:44:02 -04:00
akemidx
552f90ae2c required, but not optional 2024-08-01 16:06:47 -04:00
Marcus Moore
d65c0c8bea Remove comma 2024-01-23 15:22:00 -08:00
Marcus Moore
2d5631284b Indent 2024-01-23 15:19:36 -08:00
Marcus Moore
3952fc10a6 Re-render order number properly 2024-01-23 15:18:02 -08:00
Marcus Moore
0881301b6d Fix language strings 2024-01-23 13:55:20 -08:00
Marcus Moore
530ea474d1 Merge branch 'develop' into saving_custom_report_template
# Conflicts:
#	resources/lang/en/admin/reports/general.php
#	resources/views/reports/custom.blade.php
2024-01-23 13:55:07 -08:00
Marcus Moore
219540281f Add trailing commas 2024-01-22 12:42:01 -08:00
Marcus Moore
b74115b604 Add docblocks 2024-01-18 15:42:52 -08:00
Marcus Moore
1630392953 Uncheck a couple boxes by default to match existing behavior 2024-01-18 13:30:51 -08:00
Marcus Moore
861ef6312e Use is_iterable for checks instead of is_array 2024-01-18 13:04:41 -08:00
Marcus Moore
f64aa4dfd4 Improve variable name 2024-01-18 11:58:34 -08:00
Marcus Moore
786c41ad79 Add some type hints 2024-01-17 17:41:03 -08:00
Marcus Moore
b24d80680e Add clarifying comments 2024-01-17 17:32:35 -08:00
Marcus Moore
1a1f417633 Change variable name to keep foreach scoped 2024-01-17 17:00:03 -08:00
Marcus Moore
bbfee27fd3 Implement test 2024-01-17 16:40:05 -08:00
Marcus Moore
691e81d827 Implement some tests 2024-01-17 16:25:28 -08:00
Marcus Moore
0ac1dd314a Organize tests 2024-01-17 13:19:54 -08:00
Marcus Moore
82f4cc799b Improve variable name 2024-01-17 11:54:37 -08:00
Marcus Moore
4d8d069bbc Update placeholder 2024-01-17 11:43:34 -08:00
Marcus Moore
5a396cc997 Add assertion 2024-01-17 11:24:50 -08:00
Marcus Moore
0883321d9e Only limit template creator scope when authenticated 2024-01-17 11:24:38 -08:00
akemidx
2768f19b7c code cleanup 2024-01-16 18:56:29 -05:00
akemidx
20bd83232e standardizing naming to use Template 2024-01-11 19:41:19 -05:00
Marcus Moore
d72970b5b6 Add global scope to limit template to current user 2024-01-11 13:59:51 -08:00
Marcus Moore
9c1bea00ad Add failing test 2024-01-11 13:51:18 -08:00
Marcus Moore
8d8bf73c1b Scaffold additional tests 2024-01-11 13:51:10 -08:00
Marcus Moore
e5fb888d67 Implement test 2024-01-11 13:34:20 -08:00
Marcus Moore
82df7a66ec Add form label and remove info box from show and edit pages 2024-01-11 13:19:36 -08:00
Marcus Moore
0202a97e97 Add missing tag 2024-01-11 13:08:08 -08:00
Marcus Moore
b34886ead6 Move box header into box 2024-01-11 13:04:46 -08:00
akemidx
6f6341bc09 about saved reports box 2024-01-10 16:33:35 -05:00
akemidx
5f8e91455f clarifying name box 2024-01-10 15:39:32 -05:00
akemidx
a5099b5163 translations/messages on report template controller 2024-01-10 15:23:42 -05:00
akemidx
27103124bf messages/translations 2024-01-09 16:49:56 -05:00
Marcus Moore
f2d34b2c03 Remove inline javascript 2024-01-02 18:23:57 -08:00
Marcus Moore
25cba65c6e Remove marker test case 2024-01-02 18:14:50 -08:00
Marcus Moore
a756d2b765 Implement test 2024-01-02 18:14:17 -08:00
Marcus Moore
740d46a50e Repopulate report after validation error 2024-01-02 17:59:30 -08:00
Marcus Moore
d8d92a6d2c Scaffold test case 2024-01-02 17:47:52 -08:00
Marcus Moore
137193ab12 Remove duplicate test 2024-01-02 17:44:22 -08:00
Marcus Moore
fcef60445c Implement radioValue properly 2023-12-21 18:07:46 -08:00
Marcus Moore
9e0897b2cb Remove old comments 2023-12-21 17:40:26 -08:00
Marcus Moore
a23a3b95d6 Partially implement test 2023-12-21 17:15:36 -08:00
Marcus Moore
1ac0be50a7 Remove test case 2023-12-21 17:06:40 -08:00
Marcus Moore
618bbc4bda Mark test as incomplete 2023-12-21 17:03:10 -08:00
Marcus Moore
8c434c7862 Implement and scaffold tests 2023-12-21 17:02:44 -08:00
Marcus Moore
7f153b32e4 Always return an array from selectValues method 2023-12-21 16:38:51 -08:00
Marcus Moore
4fc8e8dd61 Add todo 2023-12-21 14:27:31 -08:00
Marcus Moore
48e5ee2310 Add filtering to remaining selects 2023-12-21 14:19:16 -08:00
Marcus Moore
92e3a1e69e Update variable names 2023-12-21 13:46:27 -08:00
Marcus Moore
7f0e3e288e Scaffold additional test 2023-12-21 13:43:44 -08:00
Marcus Moore
dc27e67b19 Remove edit and delete buttons from edit template view 2023-12-21 13:13:28 -08:00
Marcus Moore
4c62e8ade9 Add guard against attempting to access property on unsaved template 2023-12-21 13:11:44 -08:00
Marcus Moore
87853921c3 Formatting 2023-12-21 13:07:11 -08:00
Marcus Moore
8a496ccebc Formatting 2023-12-21 13:05:56 -08:00
Marcus Moore
3e5f804791 Display "Save" on the update template page button 2023-12-21 13:05:49 -08:00
Marcus Moore
62f8353bd7 Implement model filtering for selectValue method 2023-12-21 13:03:43 -08:00
Marcus Moore
1dd9273f70 Improve readability 2023-12-21 12:50:21 -08:00
Marcus Moore
2eeaef00e1 Improve readability 2023-12-21 12:15:00 -08:00
Marcus Moore
5c0c60a5b9 Formatting 2023-12-21 12:02:54 -08:00
Marcus Moore
6fcbb108c6 Implement test for filtering out invalid models from selectValues 2023-12-21 12:02:48 -08:00
Marcus Moore
71761a48ad A few more small clean ups 2023-12-20 17:50:55 -08:00
Marcus Moore
495d74f7c9 Formatting 2023-12-20 17:11:03 -08:00
Marcus Moore
fda77179a3 Simplify url 2023-12-20 16:48:25 -08:00
Marcus Moore
578495bab6 Update tests 2023-12-20 16:37:27 -08:00
Marcus Moore
9a5c8c4ce6 Formatting and clean ups 2023-12-20 16:24:48 -08:00
Marcus Moore
0504c09a9a Implement ability to delete templates 2023-12-20 16:19:04 -08:00
Marcus Moore
9d062f9849 Make control statements more explicit 2023-12-20 16:08:25 -08:00
Marcus Moore
0527201ae5 Allow templates to be updated 2023-12-20 14:41:23 -08:00
Marcus Moore
cf5c78029c Only show template name input on default screen 2023-12-20 14:12:42 -08:00
Marcus Moore
26cc4497eb Use dedicated show route for report templates 2023-12-20 14:01:46 -08:00
Marcus Moore
c35179b098 Use existing class in place of inline styling 2023-12-20 13:48:31 -08:00
Marcus Moore
b2d0cbb264 Display validation error for report name 2023-12-20 13:44:47 -08:00
akemidx
ebf760a477 translations, UI fixes 2023-12-19 18:01:19 -05:00
Marcus Moore
9fcb1a2d0e Rename SavedReport to ReportTemplate 2023-12-18 12:55:48 -08:00
Marcus Moore
27bb938d9e WIP: add dedicated edit report page 2023-12-13 18:09:42 -08:00
Marcus Moore
75bd056bbe Display saved template name in header 2023-12-13 14:49:38 -08:00
akemidx
ad202be374 update method (WIP ver) 2023-12-13 17:31:58 -05:00
akemidx
ca35b66597 frontys 2023-12-12 19:03:09 -05:00
akemidx
e9e68171bb frontend stuff 2023-12-12 18:56:21 -05:00
Marcus Moore
e791ebbe76 Display report name in input 2023-12-11 16:36:32 -08:00
Marcus Moore
d92893b2c7 Remove old javascript 2023-12-11 16:30:44 -08:00
Marcus Moore
c68a2a36fa Add test case for saving custom reports 2023-12-11 16:25:36 -08:00
Marcus Moore
89c47c1879 Add validation for saving reports 2023-12-11 16:20:36 -08:00
Marcus Moore
b9cda88363 Alphabetize saved reports list 2023-12-11 16:20:17 -08:00
Marcus Moore
52028ddef2 Add authorization to saving saved reports route 2023-12-11 15:34:17 -08:00
Marcus Moore
c3845f4393 Add tests around loading saved reports 2023-12-11 14:29:33 -08:00
Marcus Moore
c9157dc55d Update docblock 2023-12-11 14:20:33 -08:00
Marcus Moore
c3b53b28e3 Allow saving custom reports 2023-12-11 14:19:03 -08:00
akemidx
e636d7b9d5 multiple saved reports, beginning of dynamic dropdown listing/queried url 2023-12-11 15:28:34 -05:00
Marcus Moore
bd86c5430c Add a few typehints 2023-12-11 11:27:56 -08:00
Marcus Moore
4a0bb31866 Scaffold two more test cases 2023-12-07 11:00:26 -08:00
akemidx
b7f6c7df06 html work, code comments for tomorrow's tasks 2023-12-06 18:05:58 -05:00
Marcus Moore
327d27591f Merge branch 'saved-custom-reports' into saving_custom_report_template 2023-11-30 18:15:35 -08:00
Marcus Moore
5041c07c7e WIP: implement restoring checkbox inputs 2023-11-30 18:15:04 -08:00
Marcus Moore
505d601488 Implement restoring date ranges 2023-11-30 17:07:39 -08:00
Marcus Moore
bca7f208a6 Implement restoring select values 2023-11-30 16:57:21 -08:00
Marcus Moore
4f031149e8 Scaffold a couple test cases 2023-11-30 12:12:57 -08:00
Marcus Moore
b7011d853a WIP: add methods to restore settings from saved report 2023-11-02 17:10:50 -07:00
Marcus Moore
06186c9b12 WIP: Simply post the form to a different controller 2023-10-30 16:30:53 -07:00
Marcus Moore
7a5faa9619 Adjust margin on custom report page 2023-10-30 12:03:50 -07:00
akemidx
e5792fd415 api/savereports controller 2023-10-25 14:42:23 -04:00
akemidx
45dbc02868 save commit 2023-09-05 15:01:20 -04:00
akemidx
f1cc2c8d8b submitting form after capturing/formatting fixes 2023-08-29 16:55:21 -04:00
akemidx
734af87f2f template structuring 2023-08-28 19:26:31 -04:00
akemidx
78d589fe78 beginning of migrations 2023-08-24 14:32:37 -04:00
akemidx
c9fcc906fb create saved reports migration 2023-08-23 19:32:19 -04:00
akemidx
f9fc2a44cd alphatyping for layout 2023-08-22 15:19:12 -04:00
1164 changed files with 290850 additions and 8270 deletions

View File

@@ -3226,6 +3226,51 @@
"contributions": [
"code"
]
},
{
"login": "maciej-poleszczyk",
"name": "maciej-poleszczyk",
"avatar_url": "https://avatars.githubusercontent.com/u/133033121?v=4",
"profile": "https://github.com/maciej-poleszczyk",
"contributions": [
"code"
]
},
{
"login": "sgross-emlix",
"name": "Sebastian Groß",
"avatar_url": "https://avatars.githubusercontent.com/u/143394709?v=4",
"profile": "https://github.com/sgross-emlix",
"contributions": [
"code"
]
},
{
"login": "AnouarTouati",
"name": "Anouar Touati",
"avatar_url": "https://avatars.githubusercontent.com/u/41107778?v=4",
"profile": "https://github.com/AnouarTouati",
"contributions": [
"code"
]
},
{
"login": "aHVzY2g",
"name": "aHVzY2g",
"avatar_url": "https://avatars.githubusercontent.com/u/25596663?v=4",
"profile": "https://github.com/aHVzY2g",
"contributions": [
"code"
]
},
{
"login": "brlin-tw",
"name": "林博仁 Buo-ren Lin",
"avatar_url": "https://avatars.githubusercontent.com/u/13408130?v=4",
"profile": "https://brlin.me",
"contributions": [
"code"
]
}
]
}

View File

@@ -11,7 +11,7 @@ MYSQL_ROOT_PASSWORD=changeme1234
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=develop
APP_DEBUG=false
APP_DEBUG=true
# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
@@ -158,7 +158,7 @@ RESET_PASSWORD_LINK_EXPIRES=900
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=stderr
LOG_CHANNEL=single
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC

View File

@@ -1,7 +1,7 @@
# --------------------------------------------
# REQUIRED: DOCKER SPECIFIC SETTINGS
# --------------------------------------------
APP_VERSION=v6.4.1
APP_VERSION=
APP_PORT=8000
# --------------------------------------------
@@ -9,7 +9,7 @@ APP_PORT=8000
# --------------------------------------------
APP_ENV=production
APP_DEBUG=false
# Please regenerate the APP_KEY value by calling `docker compose run --rm snipeit php artisan key:generate --show`. Copy paste the value here
# Please regenerate the APP_KEY value by calling `docker compose run --rm app php artisan key:generate --show`. Copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier

View File

@@ -80,6 +80,12 @@ MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
ALLOW_BACKUP_DELETE=false
ALLOW_DATA_PURGE=false
ALL_BACKUP_KEEP_DAYS=7
DAILY_BACKUP_KEEP_DAYS=16
WEEKLY_BACKUP_KEEP_WEEKS=8
MONTHLY_BACKUP_KEEP_MONTHS=4
YEARLY_BACKUP_KEEP_YEARS=2
BACKUP_PURGE_OLDEST_AT_MEGS=5000
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS

View File

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

View File

@@ -1,129 +0,0 @@
name: Bug Report
description: Create a report to help us improve
body:
- type: checkboxes
attributes:
label: Debug mode
description: Please confirm you have done the following before posting your bug report
options:
- label: I have enabled debug mode
required: true
- label: I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Reproduction steps
description: Steps to reproduce the behavior.
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Screenshots
description: 'If applicable, add screenshots to help explain your problem.'
- type: markdown
attributes:
value: "### Server"
- type: input
attributes:
label: Snipe-IT Version
validations:
required: true
- type: input
id: server_operatingSystem
attributes:
label: Operating System
description: 'e.g. Ubuntu, Windows'
validations:
required: true
- type: input
attributes:
label: Web Server
description: 'e.g. Apache, IIS'
validations:
required: true
- type: input
attributes:
label: PHP Version
validations:
required: true
- type: markdown
attributes:
value: "### Desktop"
- type: input
id: desktop_operatingSystem
attributes:
label: Operating System
description: 'e.g. Ubuntu, Windows'
- type: input
id: desktop_browser
attributes:
label: Browser
description: 'e.g. Google Chrome, Safari'
- type: input
id: desktop_version
attributes:
label: Version
description: 'e.g. 93'
- type: markdown
attributes:
value: "### Mobile"
- type: input
attributes:
label: Device
description: 'e.g. iPhone 6, Pixel 4a'
- type: input
id: mobile_operatingSystem
attributes:
label: Operating System
description: 'e.g. iOS 8.1, Android 9'
- type: input
id: mobile_browser
attributes:
label: Browser
description: 'e.g. Google Chrome, Safari'
- type: input
id: mobile_version
attributes:
label: Version
description: 'e.g. 93'
- type: textarea
attributes:
label: Error messages
description: |
WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
If a stacktrace is provided in the error, include that too.
Any errors that appear in your browser's error console.
Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
Include any additional information you can find in `storage/logs` and your webserver's logs.
Include the output from `php -m` (this should display what modules you have enabled.)
render: shell
- type: textarea
attributes:
label: Additional context
description: |
Is this a fresh install or an upgrade?
What OS and web server you're running Snipe-IT on
What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
Include what you've done so far in the installation, and if you got any error messages along the way.
Indicate whether or not you've manually edited any data directly in the database
Add any other context about the problem here.
- type: markdown
attributes:
value: Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.

View File

@@ -1 +0,0 @@
blank_issues_enabled: false

View File

@@ -1,25 +0,0 @@
name: Feature Request
description: Suggest an idea for this project
title: "[Feature Request]: "
labels: ["feature request"]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. The more information you can provide about your use-case, the more liklely we are to consider your feature.
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

View File

@@ -1,40 +0,0 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B
**Test Configuration**:
* PHP version:
* MySQL version
* Webserver version
* OS version
# Checklist:
- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

View File

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

View File

@@ -1,7 +0,0 @@
# Configuration for weekly-digest - https://github.com/apps/weekly-digest
publishDay: sun
canPublishIssues: true
canPublishPullRequests: true
canPublishContributors: true
canPublishStargazers: true
canPublishCommits: true

View File

@@ -43,6 +43,9 @@ jobs:
cp -v .env.testing.example .env
cp -v .env.testing.example .env.testing
- name: Create database file
run: touch database/database.sqlite
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
@@ -57,5 +60,5 @@ jobs:
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite_testing
DB_CONNECTION: sqlite
run: php artisan test

View File

@@ -52,7 +52,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<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/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") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -73,7 +73,7 @@ RUN mkdir -p /var/www/.composer && chown apache /var/www/.composer
# Install dependencies
USER apache
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html
RUN COMPOSER_CACHE_DIR=/dev/null composer install --working-dir=/var/www/html
USER root

View File

@@ -1,6 +1,6 @@
![snipe-it-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributing) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
## Snipe-IT - Open Source Asset Management System
@@ -14,6 +14,21 @@ Snipe-IT is actively developed and we [release quite frequently](https://github.
> [!TIP]
> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
-----
### Table of Contents
* [Installation](#installation)
* [User's Manual](#users-manual)
* [Bug Reports & Feature Requests](#bug-reports--feature-requests)
* [Security](#security)
* [Upgrading](#upgrading)
* [Translations!](#translations-)
* [Libraries, Modules & Related Projects](#libraries-modules--related-projects)
* [Join the Community!](#join-the-community)
* [Contributing](#contributing)
* [Announcement List](#announcement-list)
-----
### Installation
@@ -22,8 +37,6 @@ For instructions on installing and configuring Snipe-IT on your server, check ou
If you're having trouble with the installation, please check the [Common Issues](https://snipe-it.readme.io/docs/common-issues) and [Getting Help](https://snipe-it.readme.io/docs/getting-help) documentation, and search this repository's open *and* closed issues for help.
<!-- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) -->
-----
### User's Manual
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
@@ -35,20 +48,21 @@ Feel free to check out the [GitHub Issues for this project](https://github.com/s
> [!IMPORTANT]
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
>
-----
### Security
> [!IMPORTANT]
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**
-----
### Upgrading
Please see the [upgrading documentation](https://snipe-it.readme.io/docs/upgrading) for instructions on upgrading Snipe-IT.
------
### Announcement List
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
------
### Translations!
Please see the [translations documentation](https://snipe-it.readme.io/docs/translations) for information about available languages and how to add translations to Snipe-IT.
@@ -82,23 +96,33 @@ Since the release of the JSON REST API, several third-party developers have been
-----
### Contributing
### Join the Community!
Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.
Ideally, contributions should follow from a human-to-human discussion in the form of an issue.
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
The ERD is available [online here](https://drawsql.app/templates/snipe-it).
[Here is a list](CONTRIBUTORS.md) of the wonderful people that have contributed to the Snipe-IT.
- **[Join our Discord](https://discord.gg/yZFtShAcKk)!** Its full of great people. We even wrote about it [here](https://grokstar.dev/culture/2024/06/the-unlikely-rise-of-discord-as-a-support-channel/)!
- **Follow us on Bluesky** at [@snipeitapp.com](https://bsky.app/profile/snipeitapp.com)
- **Follow us on Mastodon** at [hachyderm.io/@grokability](https://hachyderm.io/@grokability)
- **Follow our blog** at [Grokstar.Dev](https://grokstar.dev)
- **Subscribe here** on Github for notifications about new releases. (We recommend selecting "Releases" only for most users - this repo can get noisy.)
-----
### Security
### Contributing
**Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.**
Contributions should follow from a human-to-human discussion in the form of an issue for the best chances of being merged into the core project. (Sometimes we might already be working on that feature, sometimes we've decided against )
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
This project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
The ERD is available [online here](https://drawsql.app/templates/snipe-it).
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
------
### Announcement List
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
> [!IMPORTANT]
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**

View File

@@ -10,10 +10,12 @@ however there are times when library dependencies and/or PHP/MySQL dependencies
make it impossible to backport security fixes on older versions.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
|---------| ------------------ |
| 7.x | :white_check_mark: |
| 6.x | :x: |
| 5.1.x | :x: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| 4.0.x | :x: |
| < 4.0 | :x: |
## Reporting a Vulnerability

View File

@@ -323,22 +323,29 @@ class LdapSync extends Command
]
];
}
$add_manager_to_cache = true;
if ($ldap_manager["count"] > 0) {
try {
// Get the Manager's username
// PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array.
$ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0];
// Get the Manager's username
// PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array.
$ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0];
// Get User from Manager username.
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
// Get User from Manager username.
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
if ($ldap_manager && isset($ldap_manager->id)) {
// Link user to manager id.
$user->manager_id = $ldap_manager->id;
if ($ldap_manager && isset($ldap_manager->id)) {
// Link user to manager id.
$user->manager_id = $ldap_manager->id;
}
} catch (\Exception $e) {
$add_manager_to_cache = false;
\Log::warning('Handling ldap manager ' . $item['manager'] . ' caused an exception: ' . $e->getMessage() . '. Continuing synchronization.');
}
}
$manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed'
if ($add_manager_to_cache) {
$manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed'
}
}
}
@@ -420,7 +427,13 @@ class LdapSync extends Command
$user->groups()->attach($ldap_default_group);
}
//updates assets location based on user's location
Asset::where('assigned_to', '=', $user->id)->update(['location_id' => $user->location_id]);
if ($user->wasChanged('location_id')) {
foreach ($user->assets as $asset) {
$asset->location_id = $user->location_id;
// TODO: somehow add note? "Asset Location Changed because of thing"
$asset->save();
}
}
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {

View File

@@ -59,7 +59,7 @@ class ObjectImportCommand extends Command
$classString = "App\\Importer\\{$class}Importer";
$importer = new $classString($filename);
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
->setUserId($this->option('user_id'))
->setCreatedBy($this->option('user_id'))
->setUpdating($this->option('update'))
->setShouldNotify($this->option('send-welcome'))
->setUsernameFormat($this->option('username_format'));

View File

@@ -50,12 +50,12 @@ class ResetDemoSettings extends Command
$settings->alert_email = 'service@snipe-it.io';
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
$settings->header_color = null;
$settings->barcode_type = 'QRCODE';
$settings->label2_2d_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 2;
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 0;
$settings->alt_barcode = 'C128';
$settings->label2_1d_type = 'C128';
$settings->skin = '';
$settings->email_domain = 'snipeitapp.com';
$settings->email_format = 'filastname';
@@ -65,7 +65,7 @@ class ResetDemoSettings extends Command
$settings->thumbnail_max_h = '30';
$settings->locale = 'en-US';
$settings->version_footer = 'on';
$settings->support_footer = null;
$settings->support_footer = 'on';
$settings->saml_enabled = '0';
$settings->saml_sp_x509cert = null;
$settings->saml_idp_metadata = null;

View File

@@ -51,6 +51,8 @@ class SQLStreamer {
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
"<^/\*!40101 SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, //using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
"<^/\*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' \*/;$>" => false, //same, now handle zero-values
];
foreach($allowed_statements as $statement => $statechange) {
@@ -370,7 +372,7 @@ class RestoreFromBackup extends Command
if ($this->option('sanitize-guess-prefix')) {
$prefix = SQLStreamer::guess_prefix($sql_contents);
$this->line($prefix);
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
}
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness

View File

@@ -2,15 +2,15 @@
namespace App\Console\Commands;
use App\Mail\UnacceptedAssetReminderMail;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CurrentInventory;
use App\Notifications\UnacceptedAssetReminderNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Mail;
class SendAcceptanceReminder extends Command
{
@@ -65,42 +65,29 @@ class SendAcceptanceReminder extends Command
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
});
$no_mail_address = [];
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.');
}
$item_count = $unacceptedAssetGroup->count();
foreach ($unacceptedAssetGroup as $unacceptedAsset) {
// if ($unacceptedAsset['acceptance']->assignedTo->email == ''){
// $no_mail_address[] = $unacceptedAsset['checkoutable']->assignedTo->present()->fullName;
// }
if ($unacceptedAsset['acceptance']->assignedTo) {
if (!$unacceptedAsset['acceptance']->assignedTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
$unacceptedAsset['acceptance']->assignedTo,
new UnacceptedAssetReminderNotification($unacceptedAsset['assetItem'], $count)
);
} else {
Notification::send(
$unacceptedAsset['acceptance']->assignedTo,
new UnacceptedAssetReminderNotification($unacceptedAsset, $item_count)
);
}
$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++;
}
if (!empty($no_mail_address)) {
foreach($no_mail_address as $user) {
return $user.' has no email.';
}
}
$this->info($count.' users notified.');
return 0;
}
}

28
app/Events/NoteAdded.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NoteAdded
{
use Dispatchable, SerializesModels;
public $itemNoteAddedOn;
public $note;
public $noteAddedBy;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($itemNoteAddedOn, User $noteAddedBy, $note)
{
$this->itemNoteAddedOn = $itemNoteAddedOn;
$this->note = $note;
$this->noteAddedBy = $noteAddedBy;
}
}

View File

@@ -184,7 +184,9 @@ class IconHelper
return 'fa-regular fa-id-card';
case 'department' :
return 'fa-solid fa-building-user';
case 'note':
case 'notes':
return 'fas fa-sticky-note';
}
}
}

View File

@@ -31,7 +31,7 @@ class AccessoriesController extends Controller
public function index() : View
{
$this->authorize('index', Accessory::class);
return view('accessories/index');
return view('accessories.index');
}
/**
@@ -100,7 +100,7 @@ class AccessoriesController extends Controller
if ($item = Accessory::find($accessoryId)) {
$this->authorize($item);
return view('accessories/edit', compact('item'))->with('category_type', 'accessory');
return view('accessories.edit', compact('item'))->with('category_type', 'accessory');
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
@@ -236,7 +236,7 @@ class AccessoriesController extends Controller
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
$this->authorize('view', $accessory);
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
return view('accessories.view', compact('accessory'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryID]));

View File

@@ -51,7 +51,7 @@ class AccessoriesFilesController extends Controller
}
return redirect()->route('accessories.show', $accessory->id)->with('success', trans('general.file_upload_success'));
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
@@ -90,8 +90,7 @@ class AccessoriesFilesController extends Controller
$log->delete();
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
// Redirect to the licence management page

View File

@@ -75,20 +75,23 @@ class AccessoryCheckoutController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
$accessory_checkout = new AccessoryCheckout([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'created_by' => auth()->id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
// Set this as user since we only allow checkout to user for this item type
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
$request->request->add(['assigned_user' => $target->id]);
$request->request->add(['assigned_to' => $target->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);

View File

@@ -40,15 +40,16 @@ class ActionlogController extends Controller
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$this->authorize('view', \App\Models\Asset::class);
$file = config('app.private_uploads').'/eula-pdfs/'.$filename;
if (Storage::exists($file)) {
return response()->download($file);
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View File

@@ -13,6 +13,7 @@ use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
@@ -184,39 +185,33 @@ class AccessoriesController extends Controller
/**
* Display the specified resource.
* Get the list of checkouts for a specific accessory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
* @return | array
*/
public function checkedout($id, Request $request)
public function checkedout(Request $request, $id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::with('lastCheckout')->findOrFail($id);
$offset = request('offset', 0);
$limit = request('limit', 50);
$accessory_checkouts = $accessory->checkouts;
$total = $accessory_checkouts->count();
if ($total < $offset) {
$offset = 0;
}
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
// Total count of all checkouts for this asset
$accessory_checkouts = $accessory->checkouts();
// Check for search text in the request
if ($request->filled('search')) {
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
->get();
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->TextSearch($request->input('search'));
}
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_checkouts, $total);
}
@@ -227,7 +222,7 @@ class AccessoriesController extends Controller
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @param int $id
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function update(ImageUploadRequest $request, $id)
{
@@ -249,7 +244,7 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($id)
{
@@ -284,14 +279,17 @@ class AccessoriesController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
$accessory_checkout = new AccessoryCheckout([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'created_by' => auth()->id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
// Set this value to be able to pass the qty through to the event

View File

@@ -122,7 +122,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function store(Request $request) : JsonResponse
public function store(Request $request) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// create a new model instance
@@ -149,7 +149,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function update(Request $request, $id) : JsonResponse
public function update(Request $request, $id) : JsonResponse | array
{
$this->authorize('update', Asset::class);
@@ -186,7 +186,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function destroy($assetMaintenanceId) : JsonResponse
public function destroy($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
@@ -208,7 +208,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function show($assetMaintenanceId) : JsonResponse
public function show($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);

View File

@@ -9,6 +9,7 @@ use App\Http\Controllers\Controller;
use App\Models\AssetModel;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\AssetModelsTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -68,37 +69,15 @@ class AssetModelFilesController extends Controller
/**
* List the files for an asset.
*
* @param int $assetModelId
* @param int $assetmodel
* @since [v7.0.12]
* @author [r-xyz]
*/
public function list($assetModelId = null) : JsonResponse
public function list($assetmodel_id) : JsonResponse | array
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('view', $assetModel);
// Check that there are some uploads on this asset that can be listed
if ($assetModel->uploads->count() > 0) {
$files = array();
foreach ($assetModel->uploads as $upload) {
array_push($files, $upload);
}
// Give the list of files back to the user
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/models/message.upload.success')));
}
// There are no files.
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/models/message.upload.success')));
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error')), 500);
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
$this->authorize('view', $assetmodel);
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
}
/**

View File

@@ -56,17 +56,17 @@ class AssetModelsController extends Controller
'models.id',
'models.image',
'models.name',
'model_number',
'min_amt',
'eol',
'created_by',
'requestable',
'models.model_number',
'models.min_amt',
'models.eol',
'models.created_by',
'models.requestable',
'models.notes',
'models.created_at',
'category_id',
'manufacturer_id',
'depreciation_id',
'fieldset_id',
'models.category_id',
'models.manufacturer_id',
'models.depreciation_id',
'models.fieldset_id',
'models.deleted_at',
'models.updated_at',
])

View File

@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Models\AccessoryCheckout;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use Illuminate\Database\Eloquent\Builder;
@@ -26,13 +27,13 @@ use App\Models\License;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use App\View\Label;
use Illuminate\Support\Facades\Storage;
/**
@@ -80,10 +81,10 @@ class AssetsController extends Controller
$this->authorize('reports.view');
} else {
$transformer = 'App\Http\Transformers\AssetsTransformer';
$this->authorize('index', Asset::class);
$this->authorize('index', Asset::class);
}
$settings = Setting::getSettings();
$allowed_columns = [
@@ -126,8 +127,20 @@ class AssetsController extends Controller
}
$assets = Asset::select('assets.*')
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo', 'adminuser','model.depreciation',
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
->with(
'model',
'location',
'assetstatus',
'company',
'defaultLoc',
'assignedTo',
'adminuser',
'model.depreciation',
'model.category',
'model.manufacturer',
'model.fieldset',
'supplier'
); // it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
if ($filter_non_deprecable_assets) {
@@ -159,8 +172,8 @@ class AssetsController extends Controller
* Handle due and overdue audits and checkin dates
*/
switch ($action) {
// Audit (singular) is left over from earlier legacy APIs
case 'audits' :
// Audit (singular) is left over from earlier legacy APIs
case 'audits':
switch ($upcoming_status) {
case 'due':
$assets->DueForAudit($settings);
@@ -187,7 +200,7 @@ class AssetsController extends Controller
break;
}
break;
}
}
/**
* End handling due and overdue audits and checkin dates
@@ -265,7 +278,6 @@ class AssetsController extends Controller
$join->on('status_alias.id', '=', 'assets.status_id');
});
}
}
@@ -345,7 +357,7 @@ class AssetsController extends Controller
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
@@ -399,7 +411,6 @@ class AssetsController extends Controller
} else {
$assets->orderBy($sort_override, $order);
}
} else {
$assets->orderBy($column_sort, $order);
}
@@ -413,11 +424,11 @@ class AssetsController extends Controller
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
/**
* Include additional associated relationships
*/
*/
if ($request->input('components')) {
$assets->loadMissing(['components' => function ($query) {
$query->orderBy('created_at', 'desc');
@@ -441,7 +452,7 @@ class AssetsController extends Controller
* @since [v4.2.1]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function showByTag(Request $request, $tag) : JsonResponse | array
public function showByTag(Request $request, $tag): JsonResponse | array
{
$this->authorize('index', Asset::class);
$assets = Asset::where('asset_tag', $tag)->with('assetstatus')->with('assignedTo');
@@ -463,12 +474,10 @@ class AssetsController extends Controller
} else {
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
}
// If there are 0 results, return the "no such asset" response
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
@@ -479,7 +488,7 @@ class AssetsController extends Controller
* @since [v4.2.1]
* @return \Illuminate\Http\JsonResponse
*/
public function showBySerial(Request $request, $serial) : JsonResponse | array
public function showBySerial(Request $request, $serial): JsonResponse | array
{
$this->authorize('index', Asset::class);
$assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo');
@@ -488,14 +497,13 @@ class AssetsController extends Controller
if ($request->input('deleted', 'false') == 'true') {
$assets = $assets->withTrashed();
}
if (($assets = $assets->get()) && ($assets->count()) > 0) {
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
// If there are 0 results, return the "no such asset" response
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
@@ -506,20 +514,20 @@ class AssetsController extends Controller
* @since [v4.0]
* @return \Illuminate\Http\JsonResponse
*/
public function show(Request $request, $id) : JsonResponse | array
public function show(Request $request, $id): JsonResponse | array
{
if ($asset = Asset::with('assetstatus')
->with('assignedTo')->withTrashed()
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)) {
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)
) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset, $request->input('components') );
return (new AssetsTransformer)->transformAsset($asset, $request->input('components'));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
public function licenses(Request $request, $id) : array
public function licenses(Request $request, $id): array
{
$this->authorize('view', Asset::class);
$this->authorize('view', License::class);
@@ -527,7 +535,7 @@ class AssetsController extends Controller
$licenses = $asset->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
}
/**
@@ -537,7 +545,7 @@ class AssetsController extends Controller
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*/
public function selectlist(Request $request) : array
public function selectlist(Request $request): array
{
$assets = Asset::select([
@@ -548,7 +556,7 @@ class AssetsController extends Controller
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id',
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
@@ -570,12 +578,12 @@ class AssetsController extends Controller
$asset->use_text = $asset->present()->fullName;
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
$asset->use_text .= ' → ' . $asset->assigned->getFullNameAttribute();
}
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
$asset->use_text .= '(' . $asset->assetstatus->getStatuslabelType() . ')';
}
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
@@ -601,12 +609,12 @@ class AssetsController extends Controller
$asset->created_by = auth()->id();
/**
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
if ($request->has('image_source')) {
$request->offsetSet('image', $request->offsetGet('image_source'));
}
}
$asset = $request->handleImages($asset);
@@ -623,9 +631,9 @@ class AssetsController extends Controller
// If input value is null, use custom field's default value
if ($field_val == null) {
Log::debug('Field value for '.$field->db_column.' is null');
Log::debug('Field value for ' . $field->db_column . ' is null');
$field_val = $field->defaultValue($request->get('model_id'));
Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
@@ -643,7 +651,7 @@ class AssetsController extends Controller
}
}
if ($field->element == 'checkbox') {
if(is_array($field_val)) {
if (is_array($field_val)) {
$field_val = implode(',', $field_val);
}
}
@@ -702,64 +710,64 @@ class AssetsController extends Controller
}
/**
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
if ($request->has('image_source')) {
$request->offsetSet('image', $request->offsetGet('image_source'));
}
$asset = $request->handleImages($asset);
$model = $asset->model;
// Update custom fields
$problems_updating_encrypted_custom_fields = false;
if (($model) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
$field_val = $request->input($field->db_column, null);
if ($request->has($field->db_column)) {
if ($field->element == 'checkbox') {
if(is_array($field_val)) {
$field_val = implode(',', $field_val);
}
// Update custom fields
$problems_updating_encrypted_custom_fields = false;
if (($model) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
$field_val = $request->input($field->db_column, null);
if ($request->has($field->db_column)) {
if ($field->element == 'checkbox') {
if (is_array($field_val)) {
$field_val = implode(',', $field_val);
}
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
$field_val = Crypt::encrypt($field_val);
} else {
$problems_updating_encrypted_custom_fields = true;
continue;
}
}
$asset->{$field->db_column} = $field_val;
}
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
$field_val = Crypt::encrypt($field_val);
} else {
$problems_updating_encrypted_custom_fields = true;
continue;
}
}
$asset->{$field->db_column} = $field_val;
}
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id;
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
$location = $target->location_id;
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id;
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
$location = $target->location_id;
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
->update(['location_id' => $target->location_id]);
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
$location = $target->id;
}
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
->update(['location_id' => $target->location_id]);
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
$location = $target->id;
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
}
if ($asset->image) {
$asset->image = $asset->getImageUrl();
}
if ($asset->image) {
$asset->image = $asset->getImageUrl();
}
if ($problems_updating_encrypted_custom_fields) {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning')));
} else {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
@@ -773,7 +781,7 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v4.0]
*/
public function destroy($id) : JsonResponse
public function destroy($id): JsonResponse
{
$this->authorize('delete', Asset::class);
@@ -799,7 +807,7 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Restore a soft-deleted asset.
@@ -808,7 +816,7 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v5.1.18]
*/
public function restore(Request $request, $assetId = null) : JsonResponse
public function restore(Request $request, $assetId = null): JsonResponse
{
if ($asset = Asset::withTrashed()->find($assetId)) {
@@ -827,7 +835,6 @@ class AssetsController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
@@ -837,7 +844,7 @@ class AssetsController extends Controller
* @param string $tag
* @since [v6.0.5]
*/
public function checkoutByTag(AssetCheckoutRequest $request, $tag) : JsonResponse
public function checkoutByTag(AssetCheckoutRequest $request, $tag): JsonResponse
{
if ($asset = Asset::where('asset_tag', $tag)->first()) {
return $this->checkout($request, $asset->id);
@@ -852,13 +859,13 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v4.0]
*/
public function checkout(AssetCheckoutRequest $request, $asset_id) : JsonResponse
public function checkout(AssetCheckoutRequest $request, $asset_id): JsonResponse
{
$this->authorize('checkout', Asset::class);
$asset = Asset::findOrFail($asset_id);
if (! $asset->availableForCheckout()) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
}
$this->authorize('checkout', $asset);
@@ -875,14 +882,12 @@ class AssetsController extends Controller
$asset->location_id = ($target) ? $target->id : '';
$error_payload['target_id'] = $request->input('assigned_location');
$error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type') == 'asset') {
$target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset'));
// Override with the asset's location_id if it has one
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_asset');
$error_payload['target_type'] = 'asset';
} elseif (request('checkout_to_type') == 'user') {
// Fetch the target and set the asset's new location_id
$target = User::find(request('assigned_user'));
@@ -896,7 +901,7 @@ class AssetsController extends Controller
}
if (! isset($target)) {
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset ' . e($asset->asset_tag) . ' is invalid - ' . $error_payload['target_type'] . ' does not exist.'));
}
$checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
@@ -910,15 +915,15 @@ class AssetsController extends Controller
// TODO: Follow up here. WTF. Commented out for now.
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
// $asset->location_id = $target->rtd_location_id;
// }
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
// $asset->location_id = $target->rtd_location_id;
// }
if ($asset->checkOut($target, auth()->user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
return response()->json(Helper::formatStandardApiResponse('success', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
}
@@ -929,7 +934,7 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v4.0]
*/
public function checkin(Request $request, $asset_id) : JsonResponse
public function checkin(Request $request, $asset_id): JsonResponse
{
$asset = Asset::with('model')->findOrFail($asset_id);
$this->authorize('checkin', $asset);
@@ -937,7 +942,7 @@ class AssetsController extends Controller
$target = $asset->assignedTo;
if (is_null($target)) {
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag'=> e($asset->asset_tag),
'asset_tag' => e($asset->asset_tag),
'model' => e($asset->model->name),
'model_number' => e($asset->model->model_number)
], trans('admin/hardware/message.checkin.already_checked_in')));
@@ -960,7 +965,7 @@ class AssetsController extends Controller
if ($request->filled('location_id')) {
$asset->location_id = $request->input('location_id');
if ($request->input('update_default_location')){
if ($request->input('update_default_location')) {
$asset->rtd_location_id = $request->input('location_id');
}
}
@@ -968,8 +973,8 @@ class AssetsController extends Controller
if ($request->filled('status_id')) {
$asset->status_id = $request->input('status_id');
}
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at').' '. date('H:i:s') : date('Y-m-d H:i:s');
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at') . ' ' . date('H:i:s') : date('Y-m-d H:i:s');
$originalValues = $asset->getRawOriginal();
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
@@ -987,7 +992,8 @@ class AssetsController extends Controller
[Asset::class],
function (Builder $query) use ($asset) {
$query->where('id', $asset->id);
})
}
)
->get()
->map(function ($acceptance) {
$acceptance->delete();
@@ -997,13 +1003,13 @@ class AssetsController extends Controller
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag'=> e($asset->asset_tag),
'asset_tag' => e($asset->asset_tag),
'model' => e($asset->model->name),
'model_number' => e($asset->model->model_number)
], trans('admin/hardware/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
}
/**
@@ -1012,7 +1018,7 @@ class AssetsController extends Controller
* @author [A. Janes] [<ajanes@adagiohealth.org>]
* @since [v6.0]
*/
public function checkinByTag(Request $request, $tag = null) : JsonResponse
public function checkinByTag(Request $request, $tag = null): JsonResponse
{
$this->authorize('checkin', Asset::class);
if (null == $tag && null !== ($request->input('asset_tag'))) {
@@ -1025,8 +1031,8 @@ class AssetsController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('error', [
'asset'=> e($tag)
], 'Asset with tag '.e($tag).' not found'));
'asset' => e($tag)
], 'Asset with tag ' . e($tag) . ' not found'));
}
@@ -1037,7 +1043,7 @@ class AssetsController extends Controller
* @param int $id
* @since [v4.0]
*/
public function audit(Request $request) : JsonResponse
public function audit(Request $request): JsonResponse
{
$this->authorize('audit', Asset::class);
@@ -1048,8 +1054,8 @@ class AssetsController extends Controller
// No tag passed - return an error
if (!$request->filled('asset_tag')) {
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag'=> '',
'error'=> trans('admin/hardware/message.no_tag'),
'asset_tag' => '',
'error' => trans('admin/hardware/message.no_tag'),
], trans('admin/hardware/message.no_tag')), 200);
}
@@ -1097,28 +1103,25 @@ class AssetsController extends Controller
$asset->logAudit(request('note'), request('location_id'));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag'=> e($asset->asset_tag),
'note'=> e($request->input('note')),
'asset_tag' => e($asset->asset_tag),
'note' => e($request->input('note')),
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
], trans('admin/hardware/message.audit.success')));
}
// Asset failed validation or was not able to be saved
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag'=> e($asset->asset_tag),
'error'=> $asset->getErrors()->first(),
'asset_tag' => e($asset->asset_tag),
'error' => $asset->getErrors()->first(),
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
}
// No matching asset for the asset tag that was passed.
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag'=> e($request->input('asset_tag')),
'error'=> trans('admin/hardware/message.audit.error'),
'asset_tag' => e($request->input('asset_tag')),
'error' => trans('admin/hardware/message.audit.error'),
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
}
@@ -1129,7 +1132,7 @@ class AssetsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function requestable(Request $request) : JsonResponse | array
public function requestable(Request $request): JsonResponse | array
{
$this->authorize('viewRequestable', Asset::class);
@@ -1150,8 +1153,18 @@ class AssetsController extends Controller
}
$assets = Asset::select('assets.*')
->with('location', 'assetstatus', 'assetlog', 'company','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests');
->with(
'location',
'assetstatus',
'assetlog',
'company',
'assignedTo',
'model.category',
'model.manufacturer',
'model.fieldset',
'supplier',
'requests'
);
@@ -1159,7 +1172,7 @@ class AssetsController extends Controller
if ($request->filled('search')) {
$assets->TextSearch($request->input('search'));
}
// Search custom fields by column name
foreach ($all_custom_fields as $field) {
if ($request->filled($field->db_column_name())) {
@@ -1200,4 +1213,110 @@ class AssetsController extends Controller
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
public function assignedAssets(Request $request, Asset $asset) : JsonResponse | array
{
return [];
// to do
}
public function assignedAccessories(Request $request, Asset $asset) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* Generate asset labels by tag
*
* @author [Nebelkreis] [https://github.com/NebelKreis]
*
* @param Request $request Contains asset_tags array of asset tags to generate labels for
* @return JsonResponse Returns base64 encoded PDF on success, error message on failure
*/
public function getLabels(Request $request): JsonResponse
{
try {
$this->authorize('view', Asset::class);
// Validate that asset tags were provided in the request
if (!$request->filled('asset_tags')) {
return response()->json(Helper::formatStandardApiResponse('error', null,
trans('admin/hardware/message.no_assets_selected')), 400);
}
// Convert asset tags from request into collection and fetch matching assets
$asset_tags = collect($request->input('asset_tags'));
$assets = Asset::whereIn('asset_tag', $asset_tags)->get();
// Return error if no assets were found for the provided tags
if ($assets->isEmpty()) {
return response()->json(Helper::formatStandardApiResponse('error', null,
trans('admin/hardware/message.does_not_exist')), 404);
}
try {
$settings = Setting::getSettings();
// Check if logo file exists in storage and disable logo if not found
// This prevents errors when trying to include a non-existent logo in the PDF
$settings->label_logo = ($original_logo = $settings->label_logo) && !Storage::disk('public')->exists('/' . $original_logo) ? null : $settings->label_logo;
$label = new Label();
if (!$label) {
throw new \Exception('Label object could not be created');
}
// Configure label with assets and settings
// bulkedit=false and count=0 are default values for label generation
$label = $label->with('assets', $assets)
->with('settings', $settings)
->with('bulkedit', false)
->with('count', 0);
// Generate PDF using callback function
// The callback captures the PDF content in $pdf_content variable
$pdf_content = '';
$label->render(function($pdf) use (&$pdf_content) {
$pdf_content = $pdf->Output('', 'S');
return $pdf;
});
// Verify PDF was generated successfully
if (empty($pdf_content)) {
throw new \Exception('PDF content is empty');
}
$encoded_content = base64_encode($pdf_content);
return response()->json(Helper::formatStandardApiResponse('success', [
'pdf' => $encoded_content
], trans('admin/hardware/message.labels_generated')));
} catch (\Exception $e) {
return response()->json(Helper::formatStandardApiResponse('error', [
'error_message' => $e->getMessage(),
'error_line' => $e->getLine(),
'error_file' => $e->getFile()
], trans('admin/hardware/message.error_generating_labels')), 500);
}
} catch (\Exception $e) {
return response()->json(Helper::formatStandardApiResponse('error', [
'error_message' => $e->getMessage(),
'error_line' => $e->getLine(),
'error_file' => $e->getFile()
], $e->getMessage()), 500);
}
}
}

View File

@@ -42,7 +42,7 @@ class CompaniesController extends Controller
$companies = Company::withCount(['assets as assets_count' => function ($query) {
$query->AssetsForShow();
}])->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
}])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
if ($request->filled('search')) {
$companies->TextSearch($request->input('search'));
@@ -62,10 +62,11 @@ class CompaniesController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value');
$offset = ($request->input('offset') > $companies->count()) ? 0 : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {

View File

@@ -309,9 +309,7 @@ class ComponentsController extends Controller
public function checkin(Request $request, $component_asset_id) : JsonResponse
{
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
if (is_null($component = Component::find($component_assets->component_id))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.not_found')));
}
@@ -319,17 +317,13 @@ class ComponentsController extends Controller
$max_to_checkin = $component_assets->assigned_qty;
if ($max_to_checkin > 1) {
$validator = Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and '.$max_to_checkin));
}
$validator = Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and ' . $max_to_checkin));
}
// Validation passed, so let's figure out what we have to do here.
$qty_remaining_in_checkout = ($component_assets->assigned_qty - (int)$request->input('checkin_qty', 1));
@@ -339,28 +333,23 @@ class ComponentsController extends Controller
$component_assets->assigned_qty = $qty_remaining_in_checkout;
Log::debug($component_asset_id.' - '.$qty_remaining_in_checkout.' remaining in record '.$component_assets->id);
DB::table('components_assets')->where('id',
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
DB::table('components_assets')->where('id', $component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
// If the checked-in qty is exactly the same as the assigned_qty,
// we can simply delete the associated components_assets record
if ($qty_remaining_in_checkout == 0) {
if ($qty_remaining_in_checkout === 0) {
DB::table('components_assets')->where('id', '=', $component_asset_id)->delete();
}
$asset = Asset::find($component_assets->asset_id);
event(new CheckoutableCheckedIn($component, $asset, auth()->user(), $request->input('note'), Carbon::now()));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No matching checkouts for that component join record'));
}
}

View File

@@ -40,7 +40,7 @@ class ConsumablesController extends Controller
}
if ($request->filled('company_id')) {
$consumables->where('company_id', '=', $request->input('company_id'));
$consumables->where('consumables.company_id', '=', $request->input('company_id'));
}
if ($request->filled('category_id')) {

View File

@@ -72,6 +72,9 @@ class DepartmentsController extends Controller
case 'manager':
$departments->OrderManager($order);
break;
case 'company':
$departments->OrderCompany($order);
break;
default:
$departments->orderBy($sort, $order);
break;

View File

@@ -9,12 +9,14 @@ use App\Http\Transformers\ImportsTransformer;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Import;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Database\Eloquent\JsonEncodingException;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use League\Csv\Reader;
use Onnov\DetectEncoding\EncodingDetector;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\JsonResponse;
@@ -45,6 +47,8 @@ class ImportController extends Controller
$path = config('app.private_uploads').'/imports';
$results = [];
$import = new Import;
$detector = new EncodingDetector();
foreach ($files as $file) {
if (! in_array($file->getMimeType(), [
'application/vnd.ms-excel',
@@ -55,7 +59,6 @@ class ImportController extends Controller
'text/comma-separated-values',
'text/tsv', ])) {
$results['error'] = 'File type must be CSV. Uploaded file is '.$file->getMimeType();
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 422);
}
@@ -63,7 +66,25 @@ 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) {
$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;
}
}
}
}
$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

@@ -3,17 +3,20 @@
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\Location;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Http\JsonResponse;
class LocationsController extends Controller
{
@@ -28,26 +31,28 @@ class LocationsController extends Controller
{
$this->authorize('view', Location::class);
$allowed_columns = [
'id',
'name',
'accessories_count',
'address',
'address2',
'assets_count',
'assets_count',
'assigned_accessories_count',
'assigned_assets_count',
'assigned_assets_count',
'city',
'state',
'country',
'zip',
'created_at',
'updated_at',
'manager_id',
'image',
'assigned_assets_count',
'users_count',
'assets_count',
'assigned_assets_count',
'assets_count',
'rtd_assets_count',
'currency',
'id',
'image',
'ldap_ou',
'manager_id',
'name',
'rtd_assets_count',
'state',
'updated_at',
'users_count',
'zip',
];
$locations = Location::with('parent', 'manager', 'children')->select([
@@ -68,8 +73,11 @@ class LocationsController extends Controller
'locations.image',
'locations.ldap_ou',
'locations.currency',
])->withCount('assignedAssets as assigned_assets_count')
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count');
@@ -224,7 +232,17 @@ class LocationsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
public function assets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
$assets = Asset::where('location_id', '=', $location->id)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
$assets = $assets->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAssets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
@@ -233,6 +251,20 @@ class LocationsController extends Controller
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAccessories(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Accessory::class);
$this->authorize('view', $location);
$accessory_checkouts = AccessoryCheckout::LocationAssigned()->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new LocationsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* Remove the specified resource from storage.
*

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Api;
use App\Events\NoteAdded;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
class NotesController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'note' => 'required|string|max:500',
'type' => [
'required',
Rule::in(['asset']),
],
]);
// This can be made dynamic by using $request->input('type') to determine which model type to add the note to.
// For now, we are only placing this on Assets
$item = Asset::findOrFail($request->input("id"));
$this->authorize('update', $item);
event(new NoteAdded($item, Auth::user(), $validated['note']));
return response()->json(Helper::formatStandardApiResponse('success'));
}
public function update(Request $request)
{
}
public function destroy(Request $request)
{
}
}

View File

@@ -44,9 +44,25 @@ class ReportsController extends Controller
});
}
if ($request->filled('action_type')) {
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'));
}
if ($request->filled('created_by')) {
$actionlogs = $actionlogs->where('created_by', '=', $request->input('created_by'));
}
if ($request->filled('action_source')) {
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'));
}
if ($request->filled('remote_ip')) {
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'));
}
if ($request->filled('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
$actionlogs = $actionlogs->whereNotNull('filename');
}
$allowed_columns = [
@@ -78,7 +94,7 @@ class ReportsController extends Controller
$actionlogs->OrderByCreatedBy($order);
break;
default:
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'action_logs.created_at';
$actionlogs = $actionlogs->orderBy($sort, $order);
break;
}

View File

@@ -20,6 +20,7 @@ use App\Models\License;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
@@ -46,7 +47,6 @@ class UsersController extends Controller
'users.address',
'users.avatar',
'users.city',
'users.company_id',
'users.country',
'users.created_by',
'users.created_at',
@@ -79,8 +79,17 @@ class UsersController extends Controller
'users.autoassign_licenses',
'users.website',
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'managesUsers as manages_users_count', 'managedLocations as manages_locations_count');
])->with('groups', 'userloc', 'companies', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations', 'manager')
->withCount([
'assets as assets_count' => function(Builder $query) {
$query->withoutTrashed();
},
'licenses as licenses_count',
'accessories as accessories_count',
'consumables as consumables_count',
'managesUsers as manages_users_count',
'managedLocations as manages_locations_count'
]);
if ($request->filled('search') != '') {
@@ -92,7 +101,7 @@ class UsersController extends Controller
}
if ($request->filled('company_id')) {
$users = $users->where('users.company_id', '=', $request->input('company_id'));
$users = $users->ByCompany($request->get('company_id'));
}
if ($request->filled('location_id')) {
@@ -233,9 +242,9 @@ class UsersController extends Controller
case 'created_by':
$users = $users->OrderByCreatedBy($order);
break;
case 'company':
$users = $users->OrderCompany($order);
break;
// case 'company':
// $users = $users->OrderCompany($order);
// break;
case 'first_name':
$users->orderBy('first_name', $order);
$users->orderBy('last_name', $order);
@@ -283,6 +292,7 @@ class UsersController extends Controller
'autoassign_licenses',
'website',
'locale',
'notes',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
@@ -401,6 +411,8 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
$user->companies()->sync($request->input('companies'));
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
}
@@ -480,10 +492,11 @@ class UsersController extends Controller
$user->permissions = $permissions_array;
}
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
if($request->has('location_id')) {
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
}
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
if ($user->save()) {
@@ -500,6 +513,7 @@ class UsersController extends Controller
// Sync the groups since the user is a superuser and the groups pass validation
$user->groups()->sync($request->input('groups'));
$user->companies()->sync($request->input('companies'));
}
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
}

View File

@@ -18,6 +18,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\MessageBag;
/**
@@ -29,6 +30,7 @@ use \Illuminate\Http\RedirectResponse;
*/
class AssetModelsController extends Controller
{
protected MessageBag $validatorErrors;
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the accessories listing, which is generated in getDatatable.
@@ -158,7 +160,7 @@ class AssetModelsController extends Controller
if ($this->shouldAddDefaultValues($request->input())) {
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))) {
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
return redirect()->back()->withInput()->withErrors($this->validatorErrors);
}
}
@@ -481,9 +483,15 @@ class AssetModelsController extends Controller
$rules[$field] = $validation;
}
$validator = Validator::make($data, $rules);
$attributes = [];
foreach ($model->fieldset->fields as $field) {
$attributes[$field->db_column] = trim(preg_replace('/_+|snipeit|\d+/', ' ', $field->db_column));
}
$validator = Validator::make($data, $rules)->setAttributeNames($attributes);
if($validator->fails()){
$this->validatorErrors = $validator->errors();
return false;
}

View File

@@ -44,10 +44,10 @@ class AssetModelsFilesController extends Controller
$model->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->with('success', trans('general.file_upload_success'));
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
@@ -119,11 +119,10 @@ class AssetModelsFilesController extends Controller
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
// Redirect to the hardware management page

View File

@@ -45,7 +45,7 @@ class AssetFilesController extends Controller
$asset->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
@@ -97,25 +97,19 @@ class AssetFilesController extends Controller
*/
public function destroy($assetId = null, $fileId = null) : RedirectResponse
{
$asset = Asset::find($assetId);
$this->authorize('update', $asset);
$rel_path = 'private_uploads/assets';
// the asset is valid
if (isset($asset->id)) {
if ($asset = Asset::find($assetId)) {
$this->authorize('update', $asset);
$log = Actionlog::find($fileId);
if ($log) {
$rel_path = 'private_uploads/assets';
if ($log = Actionlog::find($fileId)) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()
->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.index')->with('error', trans('admin/hardware/message.does_not_exist'));

View File

@@ -538,7 +538,7 @@ class AssetsController extends Controller
if ($settings->qr_code == '1') {
$asset = Asset::withTrashed()->find($assetId);
if ($asset) {
$size = Helper::barcodeDimensions($settings->barcode_type);
$size = Helper::barcodeDimensions($settings->label2_2d_type);
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png';
if (isset($asset->id, $asset->asset_tag)) {
@@ -548,7 +548,7 @@ class AssetsController extends Controller
return response()->file($qr_file, $header);
} else {
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj($settings->barcode_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
$barcode_obj = $barcode->getBarcodeObj($settings->label2_2d_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
file_put_contents($qr_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
@@ -573,7 +573,7 @@ class AssetsController extends Controller
{
$settings = Setting::getSettings();
if ($asset = Asset::withTrashed()->find($assetId)) {
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->alt_barcode).'-'.str_slug($asset->asset_tag).'.png';
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->label2_1d_type).'-'.str_slug($asset->asset_tag).'.png';
if (isset($asset->id, $asset->asset_tag)) {
if (file_exists($barcode_file)) {
@@ -586,7 +586,7 @@ class AssetsController extends Controller
$barcode = new \Com\Tecnick\Barcode\Barcode();
try {
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
$barcode_obj = $barcode->getBarcodeObj($settings->label2_1d_type, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
@@ -865,8 +865,8 @@ class AssetsController extends Controller
public function quickScan()
{
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths(12)->toDateString();
$settings = Setting::getSettings();
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
return view('hardware/quickscan')->with('next_audit_date', $dt);
}
@@ -883,7 +883,6 @@ class AssetsController extends Controller
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$asset = Asset::findOrFail($id);
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
}

View File

@@ -50,14 +50,14 @@ class ForgotPasswordController extends Controller
*/
public function sendResetLinkEmail(Request $request)
{
/**
* Let's set a max character count here to prevent potential
* buffer overflow issues with attackers sending very large
* payloads through.
* payloads through. The addition of the string rule prevents attackers
* sending arrays through and causing 500s
*/
$request->validate([
'username' => ['required', 'max:255'],
'username' => ['required', 'max:255', 'string'],
]);
/**

View File

@@ -103,22 +103,24 @@ class ResetPasswordController extends Controller
], $messages);
}
if ($user->ldap_import != '1') {
// set the response
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
});
// set the response
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
});
// Check if the password reset above actually worked
if ($response == \Password::PASSWORD_RESET) {
Log::debug('Password reset for '.$user->username.' worked');
return redirect()->guest('login')->with('success', trans('passwords.reset'));
// Check if the password reset above actually worked
if ($response == \Password::PASSWORD_RESET) {
Log::debug('Password reset for ' . $user->username . ' worked');
return redirect()->guest('login')->with('success', trans('passwords.reset'));
}
Log::debug('Password reset for ' . $user->username . ' FAILED - this user exists but the token is not valid');
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
}
Log::debug('Password reset for '.$user->username.' FAILED - this user exists but the token is not valid');
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
}

View File

@@ -193,7 +193,7 @@ class ComponentsController extends Controller
$this->authorize('delete', $component);
// Remove the image if one exists
if (Storage::disk('public')->exists('components/'.$component->image)) {
if ($component->image && Storage::disk('public')->exists('components/' . $component->image)) {
try {
Storage::disk('public')->delete('components/'.$component->image);
} catch (\Exception $e) {

View File

@@ -50,7 +50,7 @@ class ComponentsFilesController extends Controller
}
return redirect()->route('components.show', $component->id)->with('success', trans('general.file_upload_success'));
return redirect()->route('components.show', $component->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
@@ -91,7 +91,7 @@ class ComponentsFilesController extends Controller
$log->delete();
return redirect()->back()
return redirect()->back()->withFragment('files')
->with('success', trans('admin/hardware/message.deletefile.success'));
}

View File

@@ -50,7 +50,7 @@ class ConsumablesController extends Controller
{
$this->authorize('create', Consumable::class);
return view('consumables/edit')->with('category_type', 'consumable')
return view('consumables.edit')->with('category_type', 'consumable')
->with('item', new Consumable);
}

View File

@@ -48,7 +48,7 @@ class ConsumablesFilesController extends Controller
}
return redirect()->route('consumables.show', $consumable->id)->with('success', trans('general.file_upload_success'));
return redirect()->route('consumables.show', $consumable->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
@@ -89,7 +89,7 @@ class ConsumablesFilesController extends Controller
$log->delete();
return redirect()->back()
return redirect()->back()->withFragment('files')
->with('success', trans('admin/hardware/message.deletefile.success'));
}

View File

@@ -104,7 +104,7 @@ class CustomFieldsController extends Controller
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->get("show_in_listview", 0),
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
"user_id" => auth()->id()
"created_by" => auth()->id()
]);

View File

@@ -71,7 +71,7 @@ class LicenseCheckinController extends Controller
if (! $license->reassignable) {
// Not allowed to checkin
Session::flash('error', 'License not reassignable.');
Session::flash('error', trans('admin/licenses/message.checkin.not_reassignable') . '.');
return redirect()->back()->withInput();
}

View File

@@ -32,7 +32,8 @@ class ModalController extends Controller
'statuslabel',
'supplier',
'upload-file',
'user',
'user',
'add-note',
];

View File

@@ -99,9 +99,13 @@ class ProfileController extends Controller
* User change email page.
*
*/
public function password() : View
public function password() : View | RedirectResponse
{
$user = auth()->user();
if ($user->ldap_import=='1') {
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
}
return view('account/change-password', compact('user'));
}
@@ -116,7 +120,7 @@ class ProfileController extends Controller
$user = auth()->user();
if ($user->ldap_import == '1') {
return redirect()->route('account.password.index')->with('error', trans('admin/users/message.error.password_ldap'));
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
}
$rules = [

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers;
use App\Models\CustomField;
use App\Models\ReportTemplate;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class ReportTemplatesController extends Controller
{
public function store(Request $request): RedirectResponse
{
$this->authorize('reports.view');
// Ignore "options" rules since data does not come in under that key...
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
$report = $request->user()->reportTemplates()->create([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
]);
session()->flash('success', trans('admin/reports/message.create.success'));
return redirect()->route('report-templates.show', $report->id);
}
public function show(ReportTemplate $reportTemplate)
{
$this->authorize('reports.view');
$customfields = CustomField::get();
$report_templates = ReportTemplate::orderBy('name')->get();
return view('reports/custom', [
'customfields' => $customfields,
'report_templates' => $report_templates,
'template' => $reportTemplate,
]);
}
public function edit(ReportTemplate $reportTemplate)
{
$this->authorize('reports.view');
return view('reports/custom', [
'customfields' => CustomField::get(),
'template' => $reportTemplate,
]);
}
public function update(Request $request, ReportTemplate $reportTemplate): RedirectResponse
{
$this->authorize('reports.view');
// Ignore "options" rules since data does not come in under that key...
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
$reportTemplate->update([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
]);
session()->flash('success', trans('admin/reports/message.update.success'));
return redirect()->route('report-templates.show', $reportTemplate->id);
}
public function destroy(ReportTemplate $reportTemplate): RedirectResponse
{
$this->authorize('reports.view');
$reportTemplate->delete();
return redirect()->route('reports/custom')
->with('success', trans('admin/reports/message.delete.success'));
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Mail\CheckoutAssetMail;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
@@ -10,14 +11,18 @@ use App\Models\AssetModel;
use App\Models\Category;
use App\Models\AssetMaintenance;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\Depreciation;
use App\Models\License;
use App\Models\ReportTemplate;
use App\Models\Setting;
use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use \Illuminate\Contracts\View\View;
use League\Csv\Reader;
@@ -259,7 +264,7 @@ class ReportsController extends Controller
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
Log::debug('Added headers: '.$executionTime);
$actionlogs = Actionlog::with('item', 'user', 'target', 'location')
$actionlogs = Actionlog::with('item', 'user', 'target', 'location', 'adminuser')
->orderBy('created_at', 'DESC')
->chunk(20, function ($actionlogs) use ($handle) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
@@ -286,7 +291,7 @@ class ReportsController extends Controller
$row = [
$actionlog->created_at,
($actionlog->admin) ? e($actionlog->admin->getFullNameAttribute()) : '',
($actionlog->adminuser) ? e($actionlog->adminuser->getFullNameAttribute()) : '',
$actionlog->present()->actionType(),
e($actionlog->itemType()),
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
@@ -392,12 +397,27 @@ class ReportsController extends Controller
* @see ReportsController::postCustomReport() method that generates the CSV
* @since [v1.0]
*/
public function getCustomReport() : View
public function getCustomReport(Request $request) : View
{
$this->authorize('reports.view');
$customfields = CustomField::get();
$report_templates = ReportTemplate::orderBy('name')->get();
return view('reports/custom')->with('customfields', $customfields);
// The view needs a template to render correctly, even if it is empty...
$template = new ReportTemplate;
// Set the report's input values in the cases we were redirected back
// with validation errors so the report is populated as expected.
if ($request->old()) {
$template->name = $request->old('name');
$template->options = $request->old();
}
return view('reports/custom', [
'customfields' => $customfields,
'report_templates' => $report_templates,
'template' => $template,
]);
}
/**
@@ -1091,28 +1111,31 @@ class ReportsController extends Controller
$this->authorize('reports.view');
$showDeleted = $deleted == 'deleted';
/**
* Get all assets with pending checkout acceptances
*/
if($showDeleted) {
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->withTrashed()->with(['assignedTo' , 'checkoutable.assignedTo', 'checkoutable.model'])->get();
} else {
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->with(['assignedTo' => function ($query) {
$query->withTrashed();
}, 'checkoutable.assignedTo', 'checkoutable.model'])->get();
$query = CheckoutAcceptance::pending()
->where('checkoutable_type', 'App\Models\Asset')
->with([
'checkoutable' => function (MorphTo $query) {
$query->morphWith([
AssetModel::class => ['model'],
Company::class => ['company'],
Asset::class => ['assignedTo'],
])->with('model.category');
},
'assignedTo' => function($query){
$query->withTrashed();
}
]);
if ($showDeleted) {
$query->withTrashed();
}
$assetsForReport = $acceptances
->filter(function ($acceptance) {
$acceptance_checkoutable_flag = false;
if ($acceptance->checkoutable){
$acceptance_checkoutable_flag = $acceptance->checkoutable->checkedOutToUser();
}
return $acceptance->checkoutable_type == 'App\Models\Asset' && $acceptance_checkoutable_flag;
})
->map(function($acceptance) {
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
$assetsForReport = $query->get()
->map(function ($acceptance) {
return [
'assetItem' => $acceptance->checkoutable,
'acceptance' => $acceptance,
];
});
return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
@@ -1150,24 +1173,17 @@ class ReportsController extends Controller
}
$logItem = $logItem_res[0];
}
$email = $assetItem->assignedTo?->email;
$locale = $assetItem->assignedTo?->locale;
// Only send notification if assigned
if ($assetItem->assignedTo) {
if ($locale && $email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note))->locale($locale));
if (!$assetItem->assignedTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
$assetItem->assignedTo,
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
);
} else {
Notification::send(
$assetItem->assignedTo,
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
);
} elseif ($email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)));
}
}
if ($assetItem->assignedTo->email == ''){
if ($email == ''){
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
}

View File

@@ -192,6 +192,7 @@ class SettingsController extends Controller
$settings->next_auto_tag_base = 1;
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
$settings->zerofill_count = $request->input('zerofill_count') ?: 0;
if ((! $user->isValid()) || (! $settings->isValid())) {
return redirect()->back()->withInput()->withErrors($user->getErrors())->withErrors($settings->getErrors());
@@ -334,6 +335,8 @@ class SettingsController extends Controller
$setting->depreciation_method = $request->input('depreciation_method');
$setting->dash_chart_type = $request->input('dash_chart_type');
$setting->profile_edit = $request->input('profile_edit', 0);
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
if ($request->input('per_page') != '') {
$setting->per_page = $request->input('per_page');
@@ -693,48 +696,6 @@ class SettingsController extends Controller
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}
/**
* Return a form to allow a super admin to update settings.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v1.0]
*/
public function getBarcodes() : View
{
$setting = Setting::getSettings();
$is_gd_installed = extension_loaded('gd');
return view('settings.barcodes', compact('setting'))->with('is_gd_installed', $is_gd_installed);
}
/**
* Saves settings from form.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v1.0]
*/
public function postBarcodes(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
$setting->qr_code = $request->input('qr_code', '0');
$setting->alt_barcode = $request->input('alt_barcode');
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
$setting->barcode_type = $request->input('barcode_type');
$setting->qr_text = $request->input('qr_text');
if ($setting->save()) {
return redirect()->route('settings.index')
->with('success', trans('admin/settings/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}
/**
* Return a form to allow a super admin to update settings.
*
@@ -760,8 +721,11 @@ class SettingsController extends Controller
*/
public function getLabels() : View
{
$is_gd_installed = extension_loaded('gd');
return view('settings.labels')
->with('setting', Setting::getSettings())
->with('is_gd_installed', $is_gd_installed)
->with('customFields', CustomField::where('field_encrypted', '=', 0)->get());
}
@@ -797,9 +761,13 @@ class SettingsController extends Controller
$setting->labels_pagewidth = $request->input('labels_pagewidth');
$setting->labels_pageheight = $request->input('labels_pageheight');
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
//Barcodes
$setting->qr_code = $request->input('qr_code', '0');
//1D-Barcode
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
//QR-Code
$setting->qr_text = $request->input('qr_text');
if ($request->filled('labels_display_name')) {
$setting->labels_display_name = 1;

View File

@@ -70,7 +70,7 @@ class BulkUsersController extends Controller
// bulk password reset, just do the thing
} elseif ($request->input('bulk_actions') == 'bulkpasswordreset') {
foreach ($users as $user) {
if (($user->activated == '1') && ($user->email != '')) {
if (($user->activated == '1') && ($user->email != '') && ($user->ldap_import != '1')) {
$credentials = ['email' => $user->email];
Password::sendResetLink($credentials/* , function (Message $message) {
$message->subject($this->getEmailSubject()); // TODO - I'm not sure if we still need this, but this second parameter is no longer accepted in later Laravel versions.
@@ -323,7 +323,7 @@ class BulkUsersController extends Controller
$logAction->item_type = $itemType;
$logAction->target_id = $item->assigned_to;
$logAction->target_type = User::class;
$logAction->created_at = auth()->id();
$logAction->created_by = auth()->id();
$logAction->note = 'Bulk checkin items';
$logAction->logaction('checkin from');
}

View File

@@ -56,7 +56,7 @@ class UserFilesController extends Controller
$logActions[] = $logAction;
}
// dd($logActions);
return redirect()->back()->with('success', trans('admin/users/message.upload.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
@@ -87,7 +87,7 @@ class UserFilesController extends Controller
if (Storage::exists($rel_path.'/'.$filename)) {
Storage::delete($rel_path.'/'.$filename);
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.deletefile.success'));
}
}

View File

@@ -62,6 +62,7 @@ class UsersController extends Controller
{
$this->authorize('create', User::class);
$groups = Group::pluck('name', 'id');
$companies = Company::pluck('name', 'id');
$userGroups = collect();
@@ -75,7 +76,7 @@ class UsersController extends Controller
$user = new User;
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions', 'companies'))
->with('user', $user);
}
@@ -142,6 +143,8 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
$user->companies()->sync($request->input('companies'));
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
// Send the credentials through email
$data = [];
@@ -192,13 +195,14 @@ class UsersController extends Controller
$permissions = config('permissions');
$groups = Group::pluck('name', 'id');
$companies = Company::pluck('name', 'id');
$userGroups = $user->groups()->pluck('name', 'id');
$user->permissions = $user->decodePermissions();
$userPermissions = Helper::selectedPermissionsArray($permissions, $user->permissions);
$permissions = $this->filterDisplayable($permissions);
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))->with('item', $user);
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions', 'companies'))->with('item', $user);
}
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
@@ -302,6 +306,7 @@ class UsersController extends Controller
}
$user->permissions = json_encode($permissions_array);
$user->companies()->sync($request->input('companies'));
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');

View File

@@ -21,9 +21,14 @@ class AssetCheckinRequest extends Request
*/
public function rules()
{
return [
$settings = \App\Models\Setting::getSettings();
];
$rules = [];
if($settings->require_checkinout_notes) {
$rules['note'] = 'string|required';
}
return $rules;
}
public function response(array $errors)

View File

@@ -21,10 +21,12 @@ class AssetCheckoutRequest extends Request
*/
public function rules()
{
$settings = \App\Models\Setting::getSettings();
$rules = [
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
'assigned_user' => 'numeric|nullable|required_without_all:assigned_asset,assigned_location',
'assigned_asset' => 'numeric|nullable|required_without_all:assigned_user,assigned_location',
'assigned_location' => 'numeric|nullable|required_without_all:assigned_user,assigned_asset',
'status_id' => 'exists:status_labels,id,deployable,1',
'checkout_to_type' => 'required|in:asset,location,user',
'checkout_at' => [
@@ -35,7 +37,11 @@ class AssetCheckoutRequest extends Request
'nullable',
'date'
],
];
];
if($settings->require_checkinout_notes) {
$rules['note'] = 'required|string';
}
return $rules;
}

View File

@@ -46,8 +46,6 @@ class UploadFileRequest extends Request
$extension = $file->getClientOriginalExtension();
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
Log::debug("Your filetype IS: ".$file->getMimeType());
// Check for SVG and sanitize it
if ($file->getMimeType() === 'image/svg+xml') {
Log::debug('This is an SVG');
@@ -66,7 +64,6 @@ class UploadFileRequest extends Request
} else {
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
Log::debug("Here are the '$put_results' (should be 0 or 1 or true or false or something?)");
}
return $file_name;
}

View File

@@ -69,7 +69,7 @@ class AccessoriesTransformer
return $array;
}
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
public function transformCheckedoutAccessory($accessory_checkouts, $total)
{
$array = [];
@@ -77,9 +77,13 @@ class AccessoriesTransformer
$array[] = [
'id' => $checkout->id,
'assigned_to' => $this->transformAssignedTo($checkout),
'checkout_notes' => e($checkout->note),
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
'available_actions' => ['checkin' => true],
'note' => $checkout->note ? e($checkout->note) : null,
'created_by' => $checkout->adminuser ? [
'id' => (int) $checkout->adminuser->id,
'name'=> e($checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
'available_actions' => Gate::allows('checkout', Accessory::class) ? ['checkin' => true] : ['checkin' => false],
];
}
@@ -89,22 +93,11 @@ class AccessoriesTransformer
public function transformAssignedTo($accessoryCheckout)
{
if ($accessoryCheckout->checkedOutToUser()) {
return [
'id' => (int) $accessoryCheckout->assigned->id,
'username' => e($accessoryCheckout->assigned->username),
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
'first_name'=> e($accessoryCheckout->assigned->first_name),
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
'type' => 'user',
];
return (new UsersTransformer)->transformUserCompact($accessoryCheckout->assigned);
} elseif ($accessoryCheckout->checkedOutToLocation()) {
return (new LocationsTransformer())->transformLocationCompact($accessoryCheckout->assigned);
} elseif ($accessoryCheckout->checkedOutToAsset()) {
return (new AssetsTransformer())->transformAssetCompact($accessoryCheckout->assigned);
}
return $accessoryCheckout->assigned ? [
'id' => $accessoryCheckout->assigned->id,
'name' => e($accessoryCheckout->assigned->display_name),
'type' => $accessoryCheckout->assignedType(),
] : null;
}
}

View File

@@ -29,14 +29,15 @@ class AssetMaintenancesTransformer
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
'asset_tag'=> e($assetmaintenance->asset->asset_tag),
'serial'=> e($assetmaintenance->asset->serial),
'deleted_at'=> e($assetmaintenance->asset->deleted_at),
'created_at'=> e($assetmaintenance->asset->created_at),
'deleted_at'=> Helper::getFormattedDateObject($assetmaintenance->asset->deleted_at, 'datetime'),
'created_at' => Helper::getFormattedDateObject($assetmaintenance->asset->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->asset->updated_at, 'datetime'),
] : null,
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
'id' => (int) $assetmaintenance->asset->model->id,
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
] : null,
'status_label' => ($assetmaintenance->asset->assetstatus) ? [
'status_label' => (($assetmaintenance->asset) && ($assetmaintenance->asset->assetstatus)) ? [
'id' => (int) $assetmaintenance->asset->assetstatus->id,
'name'=> e($assetmaintenance->asset->assetstatus->name),
'status_type'=> e($assetmaintenance->asset->assetstatus->getStatuslabelType()),
@@ -79,7 +80,7 @@ class AssetMaintenancesTransformer
];
$permissions_array['available_actions'] = [
'update' => (Gate::allows('update', Asset::class) && ($assetmaintenance->asset->deleted_at=='')) ? true : false,
'update' => (Gate::allows('update', Asset::class) && ((($assetmaintenance->asset) && $assetmaintenance->asset->deleted_at==''))) ? true : false,
'delete' => Gate::allows('delete', Asset::class),
];

View File

@@ -87,6 +87,41 @@ class AssetModelsTransformer
return $array;
}
public function transformAssetModelFiles($assetmodel, $total)
{
$array = [];
foreach ($assetmodel->uploads as $file) {
$array[] = self::transformAssetModelFile($file, $assetmodel);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformAssetModelFile($file, $assetmodel)
{
$array = [
'id' => (int) $file->id,
'filename' => e($file->filename),
'url' => route('show/modelfile', [$assetmodel->id, $file->id]),
'created_by' => ($file->adminuser) ? [
'id' => (int) $file->adminuser->id,
'name'=> e($file->adminuser->present()->fullName),
] : null,
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'delete' => (Gate::allows('update', AssetModel::class) && ($assetmodel->deleted_at == '')),
];
$array += $permissions_array;
return $array;
}
public function transformAssetModelsDatatable($assetmodels)
{
return (new DatatablesTransformer)->transformDatatables($assetmodels);

View File

@@ -3,12 +3,14 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\Setting;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class AssetsTransformer
{
@@ -225,7 +227,7 @@ class AssetsTransformer
public function transformRequestedAsset(Asset $asset)
{
$array = [
'id' => (int) $asset->id,
'id' => (int)$asset->id,
'name' => e($asset->name),
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
@@ -234,7 +236,7 @@ class AssetsTransformer
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'location' => ($asset->location) ? e($asset->location->name) : null,
'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
'status' => ($asset->assetstatus) ? $asset->present()->statusMeta : null,
'assigned_to_self' => ($asset->assigned_to == auth()->id()),
];
@@ -244,7 +246,7 @@ class AssetsTransformer
foreach ($asset->model->fieldset->fields as $field) {
// Only display this if it's allowed via the custom field setting
if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) {
if (($field->field_encrypted == '0') && ($field->show_in_requestable_list == '1')) {
$value = $asset->{$field->db_column};
if (($field->format == 'DATE') && (!is_null($value)) && ($value != '')) {
@@ -268,7 +270,61 @@ class AssetsTransformer
$array += $permissions_array;
return $array;
}
public function transformAssetCompact(Asset $asset)
{
$array = [
'id' => (int) $asset->id,
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
'type' => 'asset',
'name' => e($asset->present()->fullName()),
'model' => ($asset->model) ? e($asset->model->name) : null,
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
];
return $array;
}
public function transformCheckedoutAccessories($accessory_checkouts, $total)
{
$array = [];
foreach ($accessory_checkouts as $checkout) {
$array[] = self::transformCheckedoutAccessory($checkout);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
{
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int) $accessory_checkout->adminuser->id,
'name'=> e($accessory_checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$array += $permissions_array;
return $array;
}
}

View File

@@ -61,7 +61,7 @@ class DepreciationReportTransformer
/**
* Override the previously set null values if there is a valid model and associated depreciation
*/
if (($asset->model) && ($asset->model->depreciation)) {
if (($asset->model) && ($asset->model->depreciation) && ($asset->model->depreciation->months !== 0)) {
$depreciated_value = Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$monthly_depreciation =Helper::formatCurrencyOutput($asset->purchase_cost / $asset->model->depreciation->months);
$diff = Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));

View File

@@ -3,6 +3,8 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Location;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
@@ -45,6 +47,8 @@ class LocationsTransformer
'zip' => ($location->zip) ? e($location->zip) : null,
'phone' => ($location->phone!='') ? e($location->phone): null,
'fax' => ($location->fax!='') ? e($location->fax): null,
'accessories_count' => (int) $location->accessories_count,
'assigned_accessories_count' => (int) $location->assigned_accessories_count,
'assigned_assets_count' => (int) $location->assigned_assets_count,
'assets_count' => (int) $location->assets_count,
'rtd_assets_count' => (int) $location->rtd_assets_count,
@@ -76,4 +80,75 @@ class LocationsTransformer
return $array;
}
}
}
public function transformCheckedoutAccessories($accessory_checkouts, $total)
{
$array = [];
foreach ($accessory_checkouts as $checkout) {
$array[] = self::transformCheckedoutAccessory($checkout);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
{
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int) $accessory_checkout->adminuser->id,
'name'=> e($accessory_checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$array += $permissions_array;
return $array;
}
/**
* This gives a compact view of the location data without any additional relational queries,
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
* have not been easy/lazy loaded already
*
* @param Location $location
* @return array
* @throws \Exception
*/
public function transformLocationCompact(Location $location = null)
{
if ($location) {
$array = [
'id' => (int) $location->id,
'image' => ($location->image) ? Storage::disk('public')->url('locations/'.e($location->image)) : null,
'type' => "location",
'name' => e($location->name),
'created_by' => $location->adminuser ? [
'id' => (int) $location->adminuser->id,
'name'=> e($location->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
];
return $array;
}
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\PredefinedKit;
use App\Models\SnipeModel;
use Illuminate\Support\Facades\Gate;
@@ -42,7 +43,7 @@ class PredefinedKitsTransformer
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', PredefinedKit::class),
'delete' => Gate::allows('delete', PredefinedKit::class),
'checkout' => Gate::allows('checkout', PredefinedKit::class),
'checkout' => Gate::allows('checkout', Asset::class),
// 'clone' => Gate::allows('create', PredefinedKit::class),
// 'restore' => Gate::allows('create', PredefinedKit::class),
];

View File

@@ -4,8 +4,8 @@ namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Gate;
class UsersTransformer
{
@@ -55,7 +55,6 @@ class UsersTransformer
'name'=> e($user->userloc->name),
] : null,
'notes'=> Helper::parseEscapedMarkedownInline($user->notes),
'permissions' => $user->decodePermissions(),
'activated' => ($user->activated == '1') ? true : false,
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
'ldap_import' => ($user->ldap_import == '1') ? true : false,
@@ -67,7 +66,9 @@ class UsersTransformer
'consumables_count' => (int) $user->consumables_count,
'manages_users_count' => (int) $user->manages_users_count,
'manages_locations_count' => (int) $user->manages_locations_count,
'company' => ($user->company) ? ['id' => (int) $user->company->id, 'name'=> e($user->company->name)] : null,
'company' => ($user->company) ? ['id' => (int) $user->company->id, 'name'=> e($user->company->name)] : null, // Legacy
'companies' => ($user->companies) ? [$user->companies->pluck('name', 'id')] : [],
'permissions' => $user->decodePermissions(),
'created_by' => ($user->createdBy) ? [
'id' => (int) $user->createdBy->id,
'name'=> e($user->createdBy->present()->fullName),
@@ -106,6 +107,37 @@ class UsersTransformer
return $array;
}
/**
* This gives a compact view of the user data without any additional relational queries,
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
* have not been easy/lazy loaded already
*
* @param User $user
* @return array
* @throws \Exception
*/
public function transformUserCompact(User $user) : array
{
$array = [
'id' => (int) $user->id,
'image' => e($user->present()->gravatar) ?? null,
'type' => 'user',
'name' => e($user->getFullNameAttribute()),
'first_name' => e($user->first_name),
'last_name' => e($user->last_name),
'username' => e($user->username),
'created_by' => $user->adminuser ? [
'id' => (int) $user->adminuser->id,
'name'=> e($user->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
return $array;
}
public function transformUsersDatatable($users)
{
return (new DatatablesTransformer)->transformDatatables($users);

View File

@@ -39,6 +39,7 @@ abstract class Importer
* @var array
*/
private $defaultFieldMap = [
'id' => 'id',
'asset_tag' => 'asset tag',
'activated' => 'activated',
'category' => 'category',

View File

@@ -456,14 +456,13 @@ class ItemImporter extends Importer
{
if (empty($asset_location)) {
$this->log('No location given, so none created.');
return null;
}
$location = Location::where(['name' => $asset_location])->first();
if ($location) {
$this->log('Location '.$asset_location.' already exists');
return $location->id;
}
// No matching locations in the collection, create a new one.

View File

@@ -38,8 +38,16 @@ class LocationImporter extends ItemImporter
{
$editingLocation = false;
$location = Location::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
if ($this->findCsvMatch($row, 'id')!='') {
// Override location if an ID was given
\Log::debug('Finding location by ID: '.$this->findCsvMatch($row, 'id'));
$location = Location::find($this->findCsvMatch($row, 'id'));
}
if ($location) {
if (! $this->updating) {
$this->log('A matching Location '.$this->item['name'].' already exists');
@@ -95,6 +103,7 @@ class LocationImporter extends ItemImporter
} else {
Log::debug($location->getErrors());
$this->logError($location, 'Location "'.$this->item['name'].'"');
return $location->errors;
}

View File

@@ -46,7 +46,7 @@ class CheckoutableListener
*/
public function onCheckedOut($event)
{
if ($this->shouldNotSendAnyNotifications($event->checkoutable)){
if ($this->shouldNotSendAnyNotifications($event->checkoutable)) {
return;
}
@@ -57,7 +57,7 @@ class CheckoutableListener
$acceptance = $this->getCheckoutAcceptance($event);
$adminCcEmailsArray = [];
if($settings->admin_cc_email !== '') {
if ($settings->admin_cc_email !== '') {
$adminCcEmail = $settings->admin_cc_email;
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
}
@@ -65,7 +65,7 @@ class CheckoutableListener
$mailable = $this->getCheckoutMailType($event, $acceptance);
$notifiable = $this->getNotifiables($event);
if (!$event->checkedOutTo->locale){
if ($event->checkedOutTo->locale) {
$mailable->locale($event->checkedOutTo->locale);
}
// Send email notifications
@@ -77,41 +77,50 @@ class CheckoutableListener
* 3. The item should send an email at check-in/check-out
*/
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
$this->checkoutableShouldSendEmail($event)) {
Log::info('Sending checkout email, Locale: ' . ($event->checkedOutTo->locale ?? 'default'));
if (!empty($notifiable)) {
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
} elseif (!empty($ccEmails)) {
Mail::cc($ccEmails)->send($mailable);
}
Log::info('Checkout Mail sent.');
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
$this->checkoutableShouldSendEmail($event)) {
Log::info('Sending checkout email, Locale: ' . ($event->checkedOutTo->locale ?? 'default'));
if (!empty($notifiable)) {
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
} elseif (!empty($ccEmails)) {
Mail::cc($ccEmails)->send($mailable);
}
Log::info('Checkout Mail sent.');
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout email: " . $e->getMessage());
} catch (Exception $e) {
Log::debug("Exception caught during checkout email: " . $e->getMessage());
}
// Send Webhook notification
try{
if ($this->shouldSendWebhookNotification()) {
if ($this->newMicrosoftTeamsWebhookEnabled()) {
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
}
try {
if ($this->shouldSendWebhookNotification()) {
if ($this->newMicrosoftTeamsWebhookEnabled()) {
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
}
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail') );
} catch (Exception $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
'error' => $e->getMessage(),
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
'event' => $event,
]);
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
}
}
/**
* Notify the user and post to webhook about the checked in checkoutable
*/
@@ -147,7 +156,7 @@ class CheckoutableListener
$ccEmails = array_filter($adminCcEmailsArray);
$mailable = $this->getCheckinMailType($event);
$notifiable = $this->getNotifiables($event);
if (!$event->checkedOutTo->locale){
if ($event->checkedOutTo->locale){
$mailable->locale($event->checkedOutTo->locale);
}
// Send email notifications
@@ -178,18 +187,24 @@ class CheckoutableListener
try {
if ($this->shouldSendWebhookNotification()) {
if ($this->newMicrosoftTeamsWebhookEnabled()) {
$message = $this->getCheckinNotification($event)->toMicrosoftTeams();
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
$message = $this->getCheckinNotification($event)->toMicrosoftTeams();
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
}
} catch (ClientException $e) {
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail'));
} catch (Exception $e) {
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
'error' => $e->getMessage(),
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
'event' => $event,
]);
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail'));
}
}
@@ -282,7 +297,7 @@ class CheckoutableListener
];
$mailable= $lookup[get_class($event->checkoutable)];
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $event->note, $acceptance);
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note);
}
private function getCheckinMailType($event){
@@ -307,9 +322,16 @@ class CheckoutableListener
return $event->checkedOutTo->manager?->email ?? '';
}
else{
return $event->checkedOutTo->email;
return $event->checkedOutTo?->email ?? '';
}
}
private function webhookSelected(){
if(Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general'){
return 'slack';
}
return Setting::getSettings()->webhook_selected;
}
/**
* Register the listeners for the subscriber.

View File

@@ -17,6 +17,7 @@ use App\Events\ItemAccepted;
use App\Events\ItemDeclined;
use App\Events\LicenseCheckedIn;
use App\Events\LicenseCheckedOut;
use App\Events\NoteAdded;
use App\Models\Actionlog;
use App\Models\User;
use App\Models\LicenseSeat;
@@ -128,6 +129,23 @@ class LogListener
}
/**
* Note is added to action log
*
*/
public function onNoteAdded(NoteAdded $event)
{
$logaction = new Actionlog();
$logaction->item_id = $event->itemNoteAddedOn->id;
$logaction->item_type = get_class($event->itemNoteAddedOn);
$logaction->note = $event->note; //this is the received alphanumeric text from the box
$logaction->created_by = $event->noteAddedBy->id;
$logaction->action_type = 'note_added';
$logaction->save();
}
/**
* Register the listeners for the subscriber.
*
@@ -141,6 +159,7 @@ class LogListener
'CheckoutAccepted',
'CheckoutDeclined',
'UserMerged',
'NoteAdded',
];
foreach ($list as $event) {

View File

@@ -24,7 +24,15 @@ class CustomFieldSetDefaultValuesForModel extends Component
$this->fieldset_id = $this->model?->fieldset_id;
$this->add_default_values = ($this->model?->defaultValues->count() > 0);
$this->initializeSelectedValuesArray();
if (session()->has('errors')) {
$errors = session('errors')->keys();
$selectedValuesKeys = array_keys($this->selectedValues);
if (count(array_intersect($selectedValuesKeys, $errors)) > 0) {
$this->add_default_values = true;
};
}
$this->populatedSelectedValuesArray();
}

View File

@@ -329,6 +329,7 @@ class Importer extends Component
];
$this->locations_fields = [
'id' => trans('general.id'),
'name' => trans('general.item_name_var', ['item' => trans('general.location')]),
'address' => trans('general.address'),
'address2' => trans('general.importer.address2'),
@@ -400,7 +401,6 @@ class Importer extends Component
'requestable',
'Requestable',
],
'gravatar' =>
[
'gravatar',

View File

@@ -159,7 +159,7 @@ class SlackSettingsForm extends Component
]);
try {
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload]);
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, ['headers' => ['Content-Type' => 'application/json']]]);
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
@@ -224,7 +224,7 @@ class SlackSettingsForm extends Component
try {
$response = Http::withHeaders([
'content-type' => 'applications/json',
'content-type' => 'application/json',
])->post($this->webhook_endpoint,
$payload)->throw();
@@ -259,7 +259,7 @@ class SlackSettingsForm extends Component
"text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
];
$response = Http::withHeaders([
'content-type' => 'applications/json',
'content-type' => 'application/json',
])->post($this->webhook_endpoint,
$payload)->throw();
}
@@ -269,7 +269,7 @@ class SlackSettingsForm extends Component
$notification->success()->sendMessage($message);
$response = Http::withHeaders([
'content-type' => 'applications/json',
'content-type' => 'application/json',
])->post($this->webhook_endpoint);
}

View File

@@ -34,7 +34,7 @@ class CheckinAccessoryMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -43,7 +43,7 @@ class CheckinAssetMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -34,7 +34,7 @@ class CheckinLicenseMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -21,7 +21,7 @@ class CheckoutAccessoryMail extends Mailable
/**
* Create a new message instance.
*/
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy,$note, $acceptance)
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $accessory;
$this->admin = $checkedOutBy;
@@ -37,7 +37,7 @@ class CheckoutAccessoryMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -23,7 +23,7 @@ class CheckoutAssetMail extends Mailable
/**
* Create a new message instance.
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note, $acceptance)
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
@@ -52,7 +52,7 @@ class CheckoutAssetMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR', 'service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -38,7 +38,7 @@ class CheckoutConsumableMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -27,7 +27,6 @@ class CheckoutLicenseMail extends Mailable
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
}
@@ -36,7 +35,7 @@ class CheckoutLicenseMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -0,0 +1,67 @@
<?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 UnacceptedAssetReminderMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct($checkout_info, $count)
{
$this->count = $count;
$this->target = $checkout_info['acceptance']?->assignedTo;
$this->acceptance = $checkout_info['acceptance'];
}
/**
* 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('mail.unaccepted_asset_reminder'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$accept_url = route('account.accept');
return new Content(
markdown: 'notifications.markdown.asset-reminder',
with: [
'count' => $this->count,
'assigned_to' => $this->target?->present()->fullName,
'link' => route('account.accept'),
'accept_url' => $accept_url,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -22,7 +22,14 @@ class AccessoryCheckout extends Model
{
use Searchable;
protected $fillable = ['created_by', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
protected $fillable = [
'accessory_id',
'assigned_to',
'assigned_type',
'note'
];
protected $presenter = \App\Presenters\AccessoryPresenter::class;
protected $table = 'accessories_checkout';
/**
@@ -34,9 +41,13 @@ class AccessoryCheckout extends Model
*/
public function accessory()
{
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
return $this->hasOne(Accessory::class, 'id', 'accessory_id');
}
public function accessories()
{
return $this->hasMany(Accessory::class, 'id', 'accessory_id');
}
/**
* Establishes the accessory checkout -> user relationship
*
@@ -44,9 +55,9 @@ class AccessoryCheckout extends Model
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
public function adminuser()
{
return $this->hasOne(\App\Models\User::class, 'user_id');
return $this->hasOne(\App\Models\User::class, 'created_by');
}
/**
@@ -76,7 +87,7 @@ class AccessoryCheckout extends Model
/**
* Determines whether the accessory is checked out to a user
*
* Even though we allow allow for checkout to things beyond users
* Even though we allow for checkout to things beyond users
* this method is an easy way of seeing if we are checked out to a user.
*
* @author [A. Kroeger]
@@ -84,7 +95,17 @@ class AccessoryCheckout extends Model
*/
public function checkedOutToUser(): bool
{
return $this->assignedType() === Asset::USER;
return $this->assigned_type == User::class;
}
public function checkedOutToLocation(): bool
{
return $this->assigned_type == Location::class;
}
public function checkedOutToAsset(): bool
{
return $this->assigned_type == Asset::class;
}
public function scopeUserAssigned(Builder $query): void
@@ -92,6 +113,16 @@ class AccessoryCheckout extends Model
$query->where('assigned_type', '=', User::class);
}
public function scopeLocationAssigned(Builder $query): void
{
$query->where('assigned_type', '=', Location::class);
}
public function scopeAssetAssigned(Builder $query): void
{
$query->where('assigned_type', '=', Asset::class);
}
/**
* Run additional, advanced searches.
*

View File

@@ -69,7 +69,7 @@ class Actionlog extends SnipeModel
'company' => ['name'],
'adminuser' => ['first_name','last_name','username', 'email'],
'user' => ['first_name','last_name','username', 'email'],
'assets' => ['asset_tag','name'],
'assets' => ['asset_tag','name', 'serial'],
];
/**

View File

@@ -35,7 +35,7 @@ class CheckoutAcceptance extends Model
/**
* The resource that was is out
*
* @return Illuminate\Database\Eloquent\Relations\MorphTo
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function checkoutable()
{

View File

@@ -211,10 +211,15 @@ final class Company extends SnipeModel
}
}
public function users()
{
return $this->hasMany(User::class, 'company_id');
public function users() {
return $this->hasManyThrough(
User::class,
UserCompany::class,
'path_id',
'course_id',
'id',
'course_id'
);
}
public function assets()
@@ -249,11 +254,6 @@ final class Company extends SnipeModel
/**
* Scoping table queries, determining if a logged in user is part of a company, and only allows the user to access items associated with that company if FMCS is enabled.
*
* This method is the one that the CompanyableTrait uses to contrain queries automatically, however that trait CANNOT be
* applied to the user's model, since it causes an infinite loop against the authenticated user.
*
* @todo - refactor that trait to handle the user's model as well.
*
* @author [A. Gianotto] <snipe@snipe.net>
* @param $query
* @param $column
@@ -279,11 +279,13 @@ final class Company extends SnipeModel
private static function scopeCompanyablesDirectly($query, $column = 'company_id', $table_name = null)
{
// Get the company ID of the logged-in user, or set it to null if there is no company associated with the user
// Get the company IDs of the logged-in user, or set it to null if there is no company associated with the user
if (Auth::hasUser()) {
$company_id = auth()->user()->company_id;
$companies = auth()->user()->companies()->pluck('companies.id');
\Log::debug(auth()->user()->id);
\Log::debug(print_r($companies, true));
} else {
$company_id = null;
$companies = [];
}
@@ -293,7 +295,7 @@ final class Company extends SnipeModel
// Dynamically get the table name if it's not passed in, based on the model we're querying against
$table = ($table_name) ? $table_name."." : $query->getModel()->getTable().".";
return $query->where($table.$column, '=', $company_id);
return $query->whereIn($table.$column, $companies);
}
}

View File

@@ -2,6 +2,8 @@
namespace App\Models;
use App\Rules\AlphaEncrypted;
use App\Rules\NumericEncrypted;
use Gate;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -95,6 +97,19 @@ class CustomFieldset extends Model
array_push($rule, $field->attributes['format']);
$rules[$field->db_column_name()] = $rule;
// these are to replace the standard 'numeric' and 'alpha' rules if the custom field is also encrypted.
// the values need to be decrypted first, because encrypted strings are alphanumeric
if ($field->format === 'NUMERIC' && $field->field_encrypted) {
$numericKey = array_search('numeric', $rules[$field->db_column_name()]);
$rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
}
if ($field->format === 'ALPHA' && $field->field_encrypted) {
$alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
$rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
}
// add not_array to rules for all fields but checkboxes
if ($field->element != 'checkbox') {
$rules[$field->db_column_name()][] = 'not_array';

View File

@@ -141,4 +141,17 @@ class Department extends SnipeModel
{
return $query->leftJoin('users as department_user', 'departments.manager_id', '=', 'department_user.id')->orderBy('department_user.first_name', $order)->orderBy('department_user.last_name', $order);
}
/**
* Query builder scope to order on company
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderCompany($query, $order)
{
return $query->leftJoin('companies as company_sort', 'departments.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace App\Models\Labels\Tapes\Brother;
class TZe_24mm_D extends TZe_24mm
{
private const BARCODE_MARGIN = 1.40;
private const TAG_SIZE = 2.80;
private const TITLE_SIZE = 2.80;
private const TITLE_MARGIN = 0.50;
private const LABEL_SIZE = 2.50;
private const LABEL_MARGIN = - 0.35;
private const FIELD_SIZE = 2.50;
private const FIELD_MARGIN = 0.35;
private const BARCODE1D_SIZE = 3.00; // Size for the C128 barcode at bottom
public function getUnit() { return 'mm'; }
public function getWidth() { return 65.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function preparePDF($pdf) {}
public function write($pdf, $record) {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$usableWidth = $pa->w;
// Reserve space at bottom for 1D barcode
$usableHeight = $pa->h - self::BARCODE1D_SIZE;
$barcodeSize = $usableHeight - self::TAG_SIZE;
if ($record->has('barcode2d')) {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
'freemono', 'b', self::TAG_SIZE, 'C',
$barcodeSize, self::TAG_SIZE, true, 0
);
static::write2DBarcode(
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
$currentX, $currentY,
$barcodeSize, $barcodeSize
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
'freemono', 'b', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', '', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
// Write label and value on the same line
// Calculate label width with proportional character spacing
$labelWidth = $pdf->GetStringWidth($field['label'], 'freemono', '', self::LABEL_SIZE);
$charCount = strlen($field['label']);
$spacingPerChar = 0.5;
$totalSpacing = $charCount * $spacingPerChar;
$adjustedWidth = $labelWidth + $totalSpacing;
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freemono', 'B', self::LABEL_SIZE, 'L',
$adjustedWidth, self::LABEL_SIZE, true, 0, $spacingPerChar
);
static::writeText(
$pdf, $field['value'],
$currentX + $adjustedWidth + 2, $currentY,
'freemono', 'B', self::FIELD_SIZE, 'L',
$usableWidth - $adjustedWidth - 2, self::FIELD_SIZE, true, 0, 0.3
);
$currentY += max(self::LABEL_SIZE, self::FIELD_SIZE) + self::FIELD_MARGIN;
}
// Add C128 barcode at the bottom
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
$pa->x1, $pa->y2 - self::BARCODE1D_SIZE,
$pa->w, self::BARCODE1D_SIZE
);
}
}
}

View File

@@ -49,14 +49,7 @@ class LabelWriter_1933081 extends LabelWriter
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE,
'freesans', 'b', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
}
if ($record->has('title')) {
static::writeText(

View File

@@ -49,13 +49,6 @@ class LabelWriter_2112283 extends LabelWriter
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE,
'freesans', 'b', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
if ($record->has('title')) {

View File

@@ -50,13 +50,6 @@ class LabelWriter_30252 extends LabelWriter
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE,
'freemono', 'b', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
if ($record->has('title')) {

View File

@@ -253,6 +253,18 @@ class Location extends SnipeModel
return $this->morphMany(\App\Models\Asset::class, 'assigned', 'assigned_type', 'assigned_to')->withTrashed();
}
/**
* Establishes the accessory -> location assignment relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedAccessories()
{
return $this->morphMany(\App\Models\AccessoryCheckout::class, 'assigned', 'assigned_type', 'assigned_to');
}
public function setLdapOuAttribute($ldap_ou)
{
return $this->attributes['ldap_ou'] = empty($ldap_ou) ? null : $ldap_ou;

View File

@@ -0,0 +1,241 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
class ReportTemplate extends Model
{
use HasFactory;
use SoftDeletes;
use ValidatingTrait;
protected $casts = [
'options' => 'array',
];
protected $fillable = [
'created_by',
'name',
'options',
];
protected $rules = [
'name' => [
'required',
'string',
],
'options' => [
'required',
'array',
],
];
protected static function booted()
{
// Scope to current user
static::addGlobalScope('current_user', function (Builder $builder) {
if (auth()->check()) {
$builder->where('created_by', auth()->id());
}
});
static::created(function (ReportTemplate $reportTemplate) {
$logAction = new Actionlog([
'item_type' => ReportTemplate::class,
'item_id' => $reportTemplate->id,
'created_by' => auth()->id(),
]);
$logAction->logaction('create');
});
static::updated(function (ReportTemplate $reportTemplate) {
$changed = [];
foreach ($reportTemplate->getDirty() as $key => $value) {
$changed[$key] = [
'old' => $reportTemplate->getOriginal($key),
'new' => $reportTemplate->getAttribute($key),
];
}
$logAction = new Actionlog();
$logAction->item_type = ReportTemplate::class;
$logAction->item_id = $reportTemplate->id;
$logAction->created_by = auth()->id();
$logAction->log_meta = json_encode($changed);
$logAction->logaction('update');
});
static::deleted(function (ReportTemplate $reportTemplate) {
$logAction = new Actionlog([
'item_type' => ReportTemplate::class,
'item_id' => $reportTemplate->id,
'created_by' => auth()->id(),
]);
$logAction->logaction('delete');
});
}
/**
* Establishes the report template -> creator relationship.
*
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
/**
* Get the value of a checkbox field for the given field name.
*
* @param string $fieldName
* @param string $fallbackValue The value to return if the report template is not saved yet.
*
*/
public function checkmarkValue(string $fieldName, string $fallbackValue = '1'): string
{
// Assuming we're using the null object pattern, and an empty model
// was passed to the view when showing the default report page,
// return the fallback value so that checkboxes are checked by default.
if (is_null($this->id)) {
return $fallbackValue;
}
// If the model does exist then return the value of the field
// or return 0 so the checkbox is unchecked.
// Falling back to 0 here is because checkboxes are not sent
// in the request when unchecked so they are not
// actually saved in the model's options.
return $this->options[$fieldName] ?? '0';
}
/**
* Get the value of a radio field for the given field name.
*
* @param string $fieldName
* @param string $value The value to check against.
* @param bool $isDefault Whether the radio input being checked is the default.
*
*/
public function radioValue(string $fieldName, string $value, bool $isDefault = false): bool
{
$fieldExists = array_has($this->options, $fieldName);
// If the field doesn't exist but the radio input
// being checked is the default then return true.
if (!$fieldExists && $isDefault) {
return true;
}
// If the field exists and matches what we're checking then return true.
if ($fieldExists && $this->options[$fieldName] === $value) {
return true;
}
// Otherwise return false.
return false;
}
/**
* Get the value of a select field for the given field name.
*
* @param string $fieldName
* @param string|null $model The Eloquent model to check against.
*
* @return mixed|null
*
*/
public function selectValue(string $fieldName, string $model = null)
{
// If the field does not exist then return null.
if (!isset($this->options[$fieldName])) {
return null;
}
$value = $this->options[$fieldName];
// If the value was stored as an array, most likely
// due to a previously being a multi-select,
// then return the first value.
if (is_array($value)) {
$value = $value[0];
}
// If a model is provided then we should ensure we only return
// the value if the model still exists.
// Note: It is possible $value is an id that no longer exists and this will return null.
if ($model) {
$foundModel = $model::find($value);
return $foundModel ? $foundModel->id : null;
}
return $value;
}
/**
* Get the values of a multi-select field for the given field name.
*
* @param string $fieldName
* @param string|null $model The Eloquent model to check against.
*
* @return iterable
*
*/
public function selectValues(string $fieldName, string $model = null): iterable
{
// If the field does not exist then return an empty array.
if (!isset($this->options[$fieldName])) {
return [];
}
// If a model is provided then we should ensure we only return
// the ids of models that exist and are not deleted.
if ($model) {
return $model::findMany($this->options[$fieldName])->pluck('id');
}
// Wrap the value in an array if needed. This is to ensure
// values previously stored as a single value,
// most likely from a single select, are returned as an array.
if (!is_array($this->options[$fieldName])) {
return [$this->options[$fieldName]];
}
return $this->options[$fieldName];
}
/**
* Get the value of a text field for the given field name.
*
* @param string $fieldName
* @param string|null $fallbackValue
*
* @return string
*/
public function textValue(string $fieldName, string|null $fallbackValue = ''): string
{
// Assuming we're using the null object pattern,
// return the default value if the object is not saved yet.
if (is_null($this->id)) {
return (string) $fallbackValue;
}
// Return the field's value if it exists
// and return the default value if not.
return $this->options[$fieldName] ?? '';
}
public function getDisplayNameAttribute()
{
return $this->name;
}
}

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