Compare commits
896 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 251080d5b8 | |||
| 3a3f26ba8b | |||
| 58656fbaec | |||
| 873d260f55 | |||
| 48fd6f3f26 | |||
| 05a68afab9 | |||
| 21ff0e748c | |||
| d2b7828569 | |||
| e19d2d6ec9 | |||
| 871255f28c | |||
| de7b8940fe | |||
| ea7c3c8485 | |||
| bc36c8074e | |||
| fa083098fc | |||
| 7688c437c0 | |||
| 697ac83040 | |||
| a742105c21 | |||
| 7a76ec9e02 | |||
| e98c3f92c1 | |||
| d202dfc225 | |||
| 6202f6157a | |||
| b1e8e5389b | |||
| 95f6381da4 | |||
| b07e3b4a91 | |||
| 86bd9a2674 | |||
| 29a36839aa | |||
| 85cb7c92e7 | |||
| a6f19a1657 | |||
| 6229d0c616 | |||
| bc6c969f21 | |||
| f767a94c84 | |||
| 89e50b94d3 | |||
| c4d97d095f | |||
| 67fb3f10d5 | |||
| ea8d390596 | |||
| cff382605c | |||
| 52af8afac2 | |||
| 03b0268dc2 | |||
| c09e93e288 | |||
| 694e3b7f3a | |||
| 46be0f6610 | |||
| dd0a16c3d5 | |||
| a15ed6eaee | |||
| 3005565bba | |||
| cf45e7536f | |||
| 1d97d95c10 | |||
| 4a1b1675cb | |||
| 8c5249ba4b | |||
| b936591240 | |||
| 76e664d647 | |||
| 0253c2a756 | |||
| d27613f55c | |||
| ca8d478e87 | |||
| c63c17d49f | |||
| 45cfec5b29 | |||
| ee8931f886 | |||
| 1a1b891309 | |||
| 0981cfa902 | |||
| 37fbcfa22b | |||
| 0ee441a976 | |||
| cf8de3baa9 | |||
| 404ee238a3 | |||
| 5a5d171ba2 | |||
| aadee068b3 | |||
| a46e2ebfd8 | |||
| f710b1aac3 | |||
| f6fe6e70b9 | |||
| bed8861179 | |||
| 180d7b1b5b | |||
| 386416ee5d | |||
| 6b5aa91f3d | |||
| 12e107a71b | |||
| fba72e1e8d | |||
| 7bbee053c6 | |||
| 6ea1a3bacf | |||
| 603afe4466 | |||
| 037ee8c07a | |||
| df1cef59d5 | |||
| 3fcd196c04 | |||
| 8ee5e7cd0a | |||
| 1ec85063ee | |||
| 92ba889cae | |||
| f17162c2e2 | |||
| fb233c0aa4 | |||
| 369c819a27 | |||
| f1eb4bd3a5 | |||
| 2f828683da | |||
| 13db151297 | |||
| bb95c231e5 | |||
| dcea14fb99 | |||
| 282fed27da | |||
| cf3ffe9c74 | |||
| 5d01a06245 | |||
| 02cdda56e2 | |||
| ffecd97665 | |||
| 779cf9418e | |||
| 34b4452645 | |||
| da1c4c23e6 | |||
| a5d0a21757 | |||
| 29454f7288 | |||
| e271711c5b | |||
| 4c78da3bbf | |||
| 71bab2c315 | |||
| 7d2af61989 | |||
| 03b5c2e246 | |||
| 33d5d5e24f | |||
| 3df2a4a70b | |||
| 6b41970e97 | |||
| 92eb6eb1b6 | |||
| 206238e83f | |||
| df882bbcbc | |||
| 6c1c3a99dd | |||
| 54067ec449 | |||
| 4c11e97922 | |||
| 8df9007a8e | |||
| 172d24fd3c | |||
| 28b450fd3c | |||
| 485caf1d92 | |||
| 29cd75a6d2 | |||
| b3fe10baa8 | |||
| 093cc9faf0 | |||
| be4e809254 | |||
| dc5e15f919 | |||
| ad7b291062 | |||
| b54c1caa74 | |||
| c3cf123189 | |||
| 3a2900621c | |||
| 639cbf6d53 | |||
| e03b5754a8 | |||
| 4ce86a061f | |||
| fa6e170feb | |||
| 51f72daba1 | |||
| a130033480 | |||
| ef95ede833 | |||
| d06e5f25b0 | |||
| 2777ac96cc | |||
| 8b3ff5a82d | |||
| 482ebfbb68 | |||
| 67c4fa2966 | |||
| a3389a31cd | |||
| 8d74a976a1 | |||
| 6d104251b3 | |||
| f16c79bb9a | |||
| 1fe22e4b7b | |||
| c7fa2c04ad | |||
| 8ca882d1c8 | |||
| 5d368990dc | |||
| 4adbd7af4c | |||
| 1935a4aca3 | |||
| c4d96f6ea3 | |||
| e163d5cfc7 | |||
| b1e7c772ae | |||
| c4b4923ff8 | |||
| b0479d543d | |||
| 81704c1d40 | |||
| d3798bf251 | |||
| 4bb7c1701f | |||
| 5fd0f56258 | |||
| 37d7e89e93 | |||
| 3ab197075a | |||
| 12e8ac8b41 | |||
| 6783dc1312 | |||
| c9f7847fb3 | |||
| 6fad085a7d | |||
| 4b23ef4021 | |||
| bb96a190fd | |||
| e1eb457f3d | |||
| 51025aad57 | |||
| 14c4765be2 | |||
| 3afe996755 | |||
| 0891bd747a | |||
| f2693ee957 | |||
| 0327e71f0f | |||
| 69c2cf2c4f | |||
| 9f02be0823 | |||
| b5553af8ed | |||
| 4d6f4303f5 | |||
| 34f1ea1c0e | |||
| e3561ad38e | |||
| a7ccf0efff | |||
| f422c2ecad | |||
| db9c435ae0 | |||
| a53f89cd2c | |||
| 280c03dcad | |||
| e70fb42f87 | |||
| f633dbba64 | |||
| a820f248c8 | |||
| 6a6272ace3 | |||
| bcb747f886 | |||
| 771c85e347 | |||
| e5c358a1fe | |||
| 52c906f6b8 | |||
| ca1555d962 | |||
| 7fdbbc846e | |||
| dbbbb103a0 | |||
| 2182ea1ce8 | |||
| 54b24434e1 | |||
| 653ad562a4 | |||
| 84a601f364 | |||
| 88d131666c | |||
| 00a9d5f33e | |||
| eb0fd2f519 | |||
| 81efaa7481 | |||
| 4119526889 | |||
| 6b3346d90c | |||
| 312ce51de6 | |||
| 09914f508f | |||
| 45eda0f611 | |||
| de088d452f | |||
| 2f18430ce4 | |||
| 8d67e02d63 | |||
| fb9a5f928f | |||
| 8e35a56386 | |||
| 30f738646e | |||
| 55281313d8 | |||
| 786799225c | |||
| c137fafa97 | |||
| cb22a3d556 | |||
| c7accb4599 | |||
| 72c85f93b6 | |||
| c1d5b8713b | |||
| 8310b91e00 | |||
| 71b5bf2eef | |||
| ab8b40745e | |||
| 16ec47573b | |||
| 556d0b44a3 | |||
| 0734d5b0b6 | |||
| f158a369a4 | |||
| bdbfb0f2a1 | |||
| e5653eaa93 | |||
| 3c4098038d | |||
| 8841e21dc9 | |||
| f5e03b264f | |||
| eb09a99eb0 | |||
| dc418a7033 | |||
| 15cc4345ab | |||
| 5272824d85 | |||
| 9bce0f2ff7 | |||
| d2e7e11dca | |||
| f2dddc1226 | |||
| 957becabb9 | |||
| 7df03722ee | |||
| 2decc3d6d3 | |||
| 2adc4ffa96 | |||
| 46779ca865 | |||
| 86661e79d1 | |||
| b2a5d86e30 | |||
| d7f0ee49b7 | |||
| 5b86ee7291 | |||
| ad2ba252ee | |||
| 17ef20ea92 | |||
| 8c327e6523 | |||
| 386b2839e8 | |||
| 934593e0b6 | |||
| a7aa178f24 | |||
| 69122034e7 | |||
| 1117f4289d | |||
| 97bc4a092f | |||
| d942b8f1fb | |||
| 49dc9767b6 | |||
| 482965197d | |||
| 410b547f3c | |||
| f769e8247f | |||
| 2b4a536f85 | |||
| 92ae069629 | |||
| 3084521521 | |||
| 4f12c86e74 | |||
| fd929f5dd9 | |||
| 7e9c8ba290 | |||
| 6f639f7bf0 | |||
| 3594ed67d1 | |||
| c816b960b5 | |||
| 5b02a43957 | |||
| 4d00bb98d1 | |||
| fdecdf4b15 | |||
| 5c1d4aff23 | |||
| bf0edcb92e | |||
| 39fa8ef3c0 | |||
| 9e8a369bc8 | |||
| 3f3fe8935f | |||
| 82387630c7 | |||
| 16c5033514 | |||
| 46ffaee1e4 | |||
| d936f92a7d | |||
| b424ddf42d | |||
| b3d5a893fc | |||
| 69ea6eebae | |||
| 0892c34125 | |||
| 52e14d501f | |||
| 4d093160fc | |||
| 0bcca99573 | |||
| 63516c4a4f | |||
| 19fb79ffff | |||
| 02835de13d | |||
| 55b004d53d | |||
| c1b72a8ce6 | |||
| fa0bea87e6 | |||
| c94a7613ca | |||
| dab5874fd7 | |||
| 4850227c04 | |||
| eb9a654cc3 | |||
| 4224bc0c43 | |||
| f893b23129 | |||
| 8c65880504 | |||
| 53cadf80fa | |||
| e177993bcc | |||
| 6a7f3ecc2e | |||
| 39cc99c89b | |||
| 25480293dc | |||
| 8b3bfc6bc9 | |||
| 19cff25300 | |||
| 848e1fe1f6 | |||
| fe147adec3 | |||
| 103809b65f | |||
| a398496dd4 | |||
| 839db8ef44 | |||
| bfd0530597 | |||
| 494ec5cd9c | |||
| fc61a4b88d | |||
| 50d8b02f8b | |||
| 860764a436 | |||
| 52d6a8990a | |||
| 87de67e4a9 | |||
| 8356b57fb4 | |||
| 3f04afee5c | |||
| 2117f61e8c | |||
| d40604b574 | |||
| 76129e9011 | |||
| 9c8411c7ff | |||
| c661d732b3 | |||
| fbe9daace6 | |||
| 651001bf6e | |||
| 9167f8a3ba | |||
| 6dc9ccffcd | |||
| 4b4e3badb7 | |||
| fe4dd23d39 | |||
| de6d71cc3b | |||
| 9e44052709 | |||
| 2d112f227a | |||
| bd8737d986 | |||
| 4b6d236eb7 | |||
| bf058bd5c6 | |||
| dfaf01e8aa | |||
| 2888dd6fd8 | |||
| 2e92ee8eee | |||
| 14b6a75507 | |||
| 2484a9db2c | |||
| bfc30794c5 | |||
| 27bc7a847b | |||
| 2a71877bec | |||
| 30bd920497 | |||
| 1d5b48b88d | |||
| 4295bad12f | |||
| 3a2eeaea7a | |||
| 12418ae91b | |||
| 783a24eb68 | |||
| 3f5c5cbe82 | |||
| 2439758ef3 | |||
| ac4aa97103 | |||
| 0bc93c6a1e | |||
| 685f1cbfb8 | |||
| e2d1e6b0c5 | |||
| c8c81a360c | |||
| bdd43b7134 | |||
| c50d5a678a | |||
| 96b3af7cbc | |||
| 9e7bbc968d | |||
| 5fc6771543 | |||
| 0d049a0be7 | |||
| 01c24ab4cd | |||
| 155f5f35cd | |||
| 7ad6caf30a | |||
| ac8aab0043 | |||
| 14ddf36d46 | |||
| 25f1167c9d | |||
| 420225c2d5 | |||
| 45f5eaac5b | |||
| cfe39afc11 | |||
| 2aa3ce15bd | |||
| c9873da732 | |||
| 8dd71f99cc | |||
| ab45975883 | |||
| c3b982e759 | |||
| e330e80c46 | |||
| f0836d4d3a | |||
| 1289920217 | |||
| 424cbd3248 | |||
| e59b9627c7 | |||
| f080c0cdcd | |||
| 29bda2ef7d | |||
| e8d9701a26 | |||
| 1d64692fd6 | |||
| 6f195cb8ec | |||
| 4c02a63acc | |||
| 0fc9fc7516 | |||
| 4450351b75 | |||
| 9bb15aaf1b | |||
| 65dd729e19 | |||
| c21142605d | |||
| 86e274faa3 | |||
| 5e8c129c7f | |||
| ab3b5ca4ef | |||
| 60a5afd752 | |||
| 9d0ea857fe | |||
| f763aea4fc | |||
| e16c04250e | |||
| ad99aa460b | |||
| e47f64f62d | |||
| c6d9da1571 | |||
| ab561d1ce8 | |||
| f39ba0136c | |||
| df60045bfe | |||
| 5e122f780f | |||
| eefe377159 | |||
| 20920c262d | |||
| 870612be1c | |||
| 266424ff0e | |||
| a8d48b758e | |||
| 67a8e0b5c6 | |||
| 7b7d424962 | |||
| a4e959818a | |||
| c3a71cc182 | |||
| da03cfdbe5 | |||
| 2b137d76fa | |||
| 81b8c111ca | |||
| c0dcae16f7 | |||
| c604f08749 | |||
| da21424416 | |||
| b5c83721ad | |||
| 9b21fca1a0 | |||
| af94b07771 | |||
| 0d23d28a65 | |||
| 710370ac24 | |||
| ed0a441e4d | |||
| 24e87cc0bb | |||
| 460693c153 | |||
| f54a94bd4c | |||
| a19b86add0 | |||
| 570944a48b | |||
| 5829b02323 | |||
| 5567a1e9ac | |||
| fa5016713f | |||
| f28a82de71 | |||
| 7c2fae7b9d | |||
| 70934e54cf | |||
| f6ad275030 | |||
| 6666a78936 | |||
| bbb2cdcceb | |||
| 6b6e18695f | |||
| b63962e90b | |||
| 4434de6241 | |||
| a2bca0e358 | |||
| 6d572424ac | |||
| d371d14c1f | |||
| 72eda1e909 | |||
| 7dbf8a8a8e | |||
| 133fdd7a37 | |||
| 0849262243 | |||
| 17095feb33 | |||
| f42ae46338 | |||
| 37ebf1827f | |||
| e65252725a | |||
| 6f53f2ac64 | |||
| e2679852ce | |||
| deaba46e1c | |||
| 989dab6259 | |||
| adacdc038d | |||
| f0aefaac42 | |||
| 1aeaa0094a | |||
| 6d3fa12f37 | |||
| 052f1eedd0 | |||
| 858da800be | |||
| d0f171ebc6 | |||
| 90cf612ac7 | |||
| 4baa949a99 | |||
| 3bed04a6d3 | |||
| d548b800d5 | |||
| f7f199d929 | |||
| 5519fddb78 | |||
| a9ed748fb2 | |||
| dcba2bfd25 | |||
| 774d0aa90c | |||
| 2eea67ce34 | |||
| db1c44f921 | |||
| e11287ec25 | |||
| f22803c59f | |||
| a72d4e5dc1 | |||
| 08f9aae8ae | |||
| 43c32fa1bf | |||
| 5afe024644 | |||
| c01bdb42fc | |||
| e3ef8d295f | |||
| ce1d677cdc | |||
| df5cacf8a2 | |||
| bb2c73348d | |||
| 4327653d70 | |||
| 5f7f4c9b91 | |||
| df95447ea4 | |||
| 01852c7188 | |||
| 79c5697042 | |||
| 65e21faa3e | |||
| 658ba092e2 | |||
| b221d99e7b | |||
| c2d863da99 | |||
| df4e6a0023 | |||
| 8081a82fe4 | |||
| f42e5d5292 | |||
| 5a3f5b03d0 | |||
| 721902c2d4 | |||
| a450530c74 | |||
| 2b4886f37f | |||
| b45de3e17f | |||
| f411c0fdd8 | |||
| 74d8431d01 | |||
| 5f6c746d25 | |||
| 1f2d30ebf4 | |||
| e6b366020a | |||
| e5800a2dac | |||
| 0d3d172108 | |||
| 86677b5f13 | |||
| cf8cb8521b | |||
| ccd00caa70 | |||
| 57010b4c23 | |||
| 545a185614 | |||
| 1572e339f9 | |||
| 7782e5cc93 | |||
| bcdda551b3 | |||
| bc66c0f223 | |||
| 1b8c7ff4ec | |||
| 756c44fcba | |||
| befa4428d0 | |||
| 60c678680a | |||
| 8bc9688d71 | |||
| aa8af2220c | |||
| b124b9af4d | |||
| ae403da8c1 | |||
| b5b8777c94 | |||
| 850f85ff59 | |||
| 84cc88831d | |||
| f4f9b165a7 | |||
| 3222d4c8df | |||
| 3173ead2e7 | |||
| 040f826c52 | |||
| df49e8350f | |||
| 3ced85080a | |||
| f450cafe3e | |||
| 1f29eb1875 | |||
| a4e5ae0938 | |||
| 77dacfcc30 | |||
| 0d9b6eaf71 | |||
| 02a37e2f89 | |||
| 9c1b1bc2b5 | |||
| 61bdb57b5d | |||
| 9df84e235c | |||
| 566ba4783e | |||
| ccf9457c45 | |||
| 8ff85c952e | |||
| afe1cb8234 | |||
| 6c735c97c6 | |||
| 31a57cdf14 | |||
| 7ebbef25e7 | |||
| f836342194 | |||
| 5cf1a6c300 | |||
| bd506820b7 | |||
| 5815607924 | |||
| 9b40c9788f | |||
| 67b5e9093e | |||
| 57d1c036ec | |||
| 71722b753d | |||
| a2625c889a | |||
| c98b9da612 | |||
| 675717ff82 | |||
| 83ef7c6fe8 | |||
| 5f4c964309 | |||
| 66ba96d531 | |||
| d67b2da064 | |||
| e45fd4088f | |||
| 7e4a0eedf0 | |||
| 3f812f696d | |||
| 633249b08a | |||
| 21c3b1fbd2 | |||
| e9e6f925bf | |||
| 828b84084d | |||
| c4b7e77498 | |||
| 91c7180bfd | |||
| b368acf941 | |||
| 7c39f516b9 | |||
| ce1ddcfcee | |||
| 945e8b402f | |||
| 5ed2bd0fb7 | |||
| bc908b854d | |||
| 2067b1138a | |||
| 1ffbdee156 | |||
| bd2812cac1 | |||
| f2a5eac256 | |||
| 530291f81c | |||
| de18e449a6 | |||
| dce19e0bea | |||
| 1f586d3102 | |||
| e5d01170d2 | |||
| 393dc51167 | |||
| 984cc7a4f2 | |||
| d8378f2a10 | |||
| a0a5480c97 | |||
| 52551dad0f | |||
| 321414f6e3 | |||
| 7787fe42c8 | |||
| 3b66912742 | |||
| 278a25c63b | |||
| 0d124bb5a1 | |||
| 417caae589 | |||
| 3d306aacc5 | |||
| 6c90f9e395 | |||
| ddf81ba135 | |||
| 8ebb41caa6 | |||
| faf48b1684 | |||
| b8232d205b | |||
| 62745923cf | |||
| 090466123f | |||
| 38a3e36cd6 | |||
| e8dc634a40 | |||
| 0d0984a400 | |||
| 8640cad033 | |||
| dc29717623 | |||
| c7083488b2 | |||
| 4b4d383509 | |||
| b9986033cc | |||
| 3680e04817 | |||
| 21e23baa37 | |||
| fcd130ae15 | |||
| 8b52d5da85 | |||
| d1dffb84dc | |||
| 541350916d | |||
| 01afa9a749 | |||
| 57f5d4a570 | |||
| 4588393b76 | |||
| 0fd99d410e | |||
| 002fd4ce30 | |||
| 7b4ecb275f | |||
| 2df5d3a8ff | |||
| da62d6af26 | |||
| d0d4d14787 | |||
| 09d69b214b | |||
| 0e2aaebda4 | |||
| ec2c58163f | |||
| a28bee86ba | |||
| fb64892971 | |||
| 95ff692b14 | |||
| 8003615b1f | |||
| 948dc3c974 | |||
| 1afb724606 | |||
| 451281d833 | |||
| 485f11c945 | |||
| dbc79655b0 | |||
| 02f6aa6161 | |||
| 07c5264b41 | |||
| 7c178a6a78 | |||
| 4cd4a936d8 | |||
| 7bae4c39c6 | |||
| d55d95bbea | |||
| 5e48d56561 | |||
| 4e835e1772 | |||
| cce8cb4f5e | |||
| 8aed26aab1 | |||
| 01d5d4c2c8 | |||
| 592385cb07 | |||
| 85123b3e66 | |||
| 0fcf223960 | |||
| bf0bd06f20 | |||
| f07b0b6bd7 | |||
| 717b26c834 | |||
| 693d1c9452 | |||
| b049bb1d5c | |||
| c5c6b3bbc6 | |||
| 1df56a7cab | |||
| 3f5cc2507d | |||
| 5a85424295 | |||
| 3fdee881f9 | |||
| a289dfaf88 | |||
| 8abd359b5b | |||
| 9359809b4f | |||
| cde5502f94 | |||
| 04e0a9d4a5 | |||
| 28ec0b8ebb | |||
| 37dfecf098 | |||
| 07880dfe50 | |||
| 7eed9f8542 | |||
| a2e70dd6b2 | |||
| b9aeded957 | |||
| 7939c691f7 | |||
| 3a0a13d06d | |||
| a251e61d73 | |||
| af06b1cd06 | |||
| 5b9120ba6c | |||
| ad0f873ece | |||
| 8ace30ee23 | |||
| eb0657c953 | |||
| 551354b1bb | |||
| 03b7891edc | |||
| 8978dff054 | |||
| d20844fefa | |||
| cd579a04dd | |||
| 15b8140bff | |||
| 9a93ad2e06 | |||
| bd4d3aa52b | |||
| bf32ab177f | |||
| 2ea883aa15 | |||
| 43cc296582 | |||
| 4c1aadd74e | |||
| 7d3719bf70 | |||
| c08164d864 | |||
| b156aa74a5 | |||
| 5ffade663a | |||
| b6fa6cba22 | |||
| 14358651e4 | |||
| 20dbacd22f | |||
| 8f2843bfcf | |||
| 9397372f87 | |||
| 8bbf6da052 | |||
| d67ff54f4b | |||
| 26728a85ad | |||
| c6d85a1b0b | |||
| 115e0fc119 | |||
| 1ceb703129 | |||
| fb28882f65 | |||
| d9c61fdb02 | |||
| 72c118a70f | |||
| 25241542d2 | |||
| 57a75e68b9 | |||
| dcf2168454 | |||
| c5e8d1c276 | |||
| 4093327b7f | |||
| d55358652b | |||
| f7d0cecdac | |||
| bcfa913450 | |||
| 43d8474caa | |||
| 8a2f3405d5 | |||
| 1248260df3 | |||
| 4cb804cf03 | |||
| 2b0dd8851c | |||
| 25db35e41e | |||
| 99000fada1 | |||
| 58b2f8ca17 | |||
| 8be3c1aaf2 | |||
| 2ee8639eaf | |||
| 32605578dd | |||
| fe0bba6df6 | |||
| 5fdcd6298f | |||
| 55943dbff6 | |||
| d12e571b20 | |||
| 0c15f05316 | |||
| 25c9f8e038 | |||
| a3811371cc | |||
| f734e196e5 | |||
| 6129f4722f | |||
| 585d7d0b87 | |||
| cdfc198f28 | |||
| 6fcb3d3d5e | |||
| 358d9ec06d | |||
| 322c8c1cf3 | |||
| 47eb0c34a2 | |||
| 4a290a85e6 | |||
| 5489c50cc0 | |||
| 066e40dada | |||
| 123d04ff7b | |||
| d5881736cf | |||
| 5e539da3da | |||
| ba58ca9d5c | |||
| 117007dfa1 | |||
| d29b3633d1 | |||
| 41e0275c95 | |||
| 7cf4baa92a | |||
| 6c3dfdfe6d | |||
| 149d159c05 | |||
| c0ad16f497 | |||
| 9edb631d52 | |||
| 58d2f0a41e | |||
| 255d9427b6 | |||
| d29f4489de | |||
| 4367fc54bf | |||
| b89116eb55 | |||
| 9430e9c8e2 | |||
| b01d53a247 | |||
| 65cbba3c2e | |||
| f93e7b91ff | |||
| 04069bab82 | |||
| 3106035ef9 | |||
| 18720b3302 | |||
| 12e546e63a | |||
| c28936fefb | |||
| 080d196138 | |||
| d3967b37cd | |||
| 0089ea2321 | |||
| 3f35124838 | |||
| 6ca5fb6b20 | |||
| a3b0273445 | |||
| 2a54797dce | |||
| 9770016eec | |||
| 6210716199 | |||
| e41a16dbf3 | |||
| 01f4aa018f | |||
| f9baeefbaf | |||
| af3417d6cc | |||
| 17bf8d0124 | |||
| c3e02286d3 | |||
| a9d82bf157 | |||
| e097df912e | |||
| 477bb6165f | |||
| 19850e8c5a | |||
| 12dcf76da9 | |||
| 36aac328e4 | |||
| 9cf81c7ad4 | |||
| 6c6417e595 | |||
| efa8ba5965 | |||
| fd02385c22 | |||
| 713b21de14 | |||
| af144a5184 | |||
| 9b31ae3146 | |||
| 4cb0012872 | |||
| eaa68327f7 | |||
| 68ef618028 | |||
| 3ecdafa685 | |||
| 20ae9dea19 | |||
| c9d46856a3 | |||
| 6d65f6646f | |||
| 34cd106357 | |||
| 714fce7e59 | |||
| 08e4ea384e | |||
| fe0f9743d3 | |||
| 57019e170f | |||
| 9f303cc116 | |||
| e504b20235 | |||
| f64b0d3818 | |||
| 1a26ea0911 | |||
| 7098ab6509 | |||
| 0d3589ca24 | |||
| f02ff62a08 | |||
| fbfc817c5f | |||
| 13601031cb | |||
| 442cad69a7 | |||
| be6545a366 | |||
| 32fdd36802 | |||
| fbc54526ed | |||
| a3306e4a81 | |||
| 3d858b21fa | |||
| 62b557f82c | |||
| 158c82ff5f | |||
| fd410c0281 | |||
| 65e3696029 | |||
| 690a5119f4 | |||
| cc0653c68a | |||
| 042a15d294 | |||
| 8e4c0f60a1 | |||
| 5615fc09e6 | |||
| d68f1e4cfa | |||
| 0c4901487d | |||
| 7f4b2d6e42 | |||
| 715a49d9fe | |||
| b98c1a0627 | |||
| da30292d84 | |||
| 7bda5c06de | |||
| 3d5847f4b3 | |||
| db400dffb5 | |||
| 199cdf6899 | |||
| 752171d5b8 | |||
| 7a5714cc85 | |||
| c77f5eee8c | |||
| aacfed40f8 | |||
| ec1059e74c | |||
| 3500217f94 | |||
| 427f8b1522 | |||
| 8b52bad16f | |||
| daed0b60bc | |||
| 4654f7aa37 | |||
| 70e87dad1c | |||
| ba8d8a6f05 | |||
| 605d267fe8 | |||
| 8f2a17585e | |||
| 51424d01a9 | |||
| f5ff9b2208 | |||
| 457f4c410a | |||
| bb013d5c3c | |||
| a9c7dbd17a | |||
| 09fdc946a0 | |||
| 31f1bce16b | |||
| 2f3ddaec20 | |||
| 7cd37e6e95 | |||
| a938009074 | |||
| 21d08ff742 | |||
| da8b41b12a | |||
| 6e031727fa | |||
| 381890b578 | |||
| 71fa6d765f | |||
| 9793130f6c | |||
| aadfa9aa9c | |||
| 645bba96cd |
@@ -3018,6 +3018,114 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "koiakoia",
|
||||
"name": "koiakoia",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60405354?v=4",
|
||||
"profile": "https://github.com/koiakoia",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mustafa-online",
|
||||
"name": "Mustafa Online",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5323832?v=4",
|
||||
"profile": "https://github.com/mustafa-online",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "franceslui",
|
||||
"name": "franceslui",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/104601439?v=4",
|
||||
"profile": "https://github.com/franceslui",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Q4kK",
|
||||
"name": "Q4kK",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/125313163?v=4",
|
||||
"profile": "https://github.com/Q4kK",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "squintfox",
|
||||
"name": "squintfox",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/55590532?v=4",
|
||||
"profile": "https://github.com/squintfox",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jeffclay",
|
||||
"name": "Jeff Clay",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1380084?v=4",
|
||||
"profile": "https://github.com/jeffclay",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PP-JN-RL",
|
||||
"name": "Phil J R",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/52716446?v=4",
|
||||
"profile": "https://github.com/PP-JN-RL",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chandanchowdhury",
|
||||
"name": "i_virus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1496725?v=4",
|
||||
"profile": "https://www.corelight.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gitgrimbo",
|
||||
"name": "Paul Grime",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1020541?v=4",
|
||||
"profile": "https://github.com/gitgrimbo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LeePorte",
|
||||
"name": "Lee Porte",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/922815?v=4",
|
||||
"profile": "https://leeporte.co.uk",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bryanlopezinc",
|
||||
"name": "BRYAN ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23613427?v=4",
|
||||
"profile": "https://github.com/bryanlopezinc",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "U-H-T",
|
||||
"name": "U-H-T",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/64061710?v=4",
|
||||
"profile": "https://github.com/U-H-T",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+3
-2
@@ -45,16 +45,17 @@ DB_SSL_KEY_PATH=null
|
||||
DB_SSL_CERT_PATH=null
|
||||
DB_SSL_CA_PATH=null
|
||||
DB_SSL_CIPHER=null
|
||||
DB_SSL_VERIFY_SERVER=null
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailhog
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_TLS_VERIFY_PEER=true
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME='Snipe-IT'
|
||||
MAIL_REPLYTO_ADDR=you@example.com
|
||||
|
||||
+2
-1
@@ -36,11 +36,12 @@ DB_SSL_KEY_PATH=null
|
||||
DB_SSL_CERT_PATH=null
|
||||
DB_SSL_CA_PATH=null
|
||||
DB_SSL_CIPHER=null
|
||||
DB_SSL_VERIFY_SERVER=null
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER="log"
|
||||
MAIL_MAILER="log"
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
|
||||
+10
-3
@@ -42,21 +42,26 @@ DB_SSL_KEY_PATH=null
|
||||
DB_SSL_CERT_PATH=null
|
||||
DB_SSL_CA_PATH=null
|
||||
DB_SSL_CIPHER=null
|
||||
DB_SSL_VERIFY_SERVER=null
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=YOURUSERNAME
|
||||
MAIL_PASSWORD=YOURPASSWORD
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME='Snipe-IT'
|
||||
MAIL_REPLYTO_ADDR=you@example.com
|
||||
MAIL_REPLYTO_NAME='Snipe-IT'
|
||||
MAIL_AUTO_EMBED_METHOD='attachment'
|
||||
MAIL_TLS_VERIFY_PEER=true
|
||||
|
||||
# MAIL_ENCRYPTION is no longer supported. SymfonyMailer will use tls if it's
|
||||
# advertised, and won't if it's not. If you want to use your mail server's IP but it's failing
|
||||
# because of certificate errors, set MAIL_TLS_VERIFY_PEER-true
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: IMAGE LIBRARY
|
||||
@@ -86,6 +91,7 @@ COOKIE_DOMAIN=null
|
||||
SECURE_COOKIES=false
|
||||
API_TOKEN_EXPIRATION_YEARS=15
|
||||
BS_TABLE_STORAGE=cookieStorage
|
||||
BS_TABLE_DEEPLINK=true
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SECURITY HEADER SETTINGS
|
||||
@@ -94,6 +100,7 @@ APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
|
||||
ALLOW_IFRAMING=false
|
||||
REFERRER_POLICY=same-origin
|
||||
ENABLE_CSP=false
|
||||
ADDITIONAL_CSP_URLS=null
|
||||
CORS_ALLOWED_ORIGINS=null
|
||||
ENABLE_HSTS=false
|
||||
|
||||
@@ -190,4 +197,4 @@ ARGON_TIME=2
|
||||
# OPTIONAL: SCIM
|
||||
# --------------------------------------------
|
||||
SCIM_TRACE=false
|
||||
SCIM_STANDARDS_COMPLIANCE=false
|
||||
SCIM_STANDARDS_COMPLIANCE=false
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ DB_PASSWORD=null
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER=log
|
||||
MAIL_MAILER=log
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
|
||||
+1
-1
@@ -18,6 +18,6 @@ APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
|
||||
LOGIN_MAX_ATTEMPTS=1000000
|
||||
LOGIN_LOCKOUT_DURATION=100000000
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_MAILER=log
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME=Snipe-IT
|
||||
|
||||
+1
-1
@@ -15,6 +15,6 @@ APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
|
||||
LOGIN_MAX_ATTEMPTS=1000000
|
||||
LOGIN_LOCKOUT_DURATION=100000000
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_MAILER=log
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME=Snipe-IT
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.0
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.1
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Crowdin push
|
||||
uses: crowdin/github-action@v1
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
||||
@@ -25,9 +25,8 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
- "8.1.1"
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
|
||||
name: PHP ${{ matrix.php-version }}
|
||||
|
||||
@@ -58,11 +57,17 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
- name: Generate key
|
||||
run: php artisan key:generate
|
||||
|
||||
- name: Directory Permissions
|
||||
run: chmod -R 777 storage bootstrap/cache
|
||||
- name: Setup Laravel
|
||||
env:
|
||||
DB_CONNECTION: mysql
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: |
|
||||
php artisan key:generate
|
||||
php artisan migrate --force
|
||||
php artisan passport:install
|
||||
chmod -R 777 storage bootstrap/cache
|
||||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
|
||||
@@ -49,6 +49,9 @@ jobs:
|
||||
- name: Generate key
|
||||
run: php artisan key:generate
|
||||
|
||||
- name: Setup Passport
|
||||
run: php artisan passport:keys
|
||||
|
||||
- name: Directory Permissions
|
||||
run: chmod -R 777 storage bootstrap/cache
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"DOC2": "In other words, what you see locally are the requirements for your _current_ install",
|
||||
"DOC3": "Please don't rely on these versions for planning upgrades unless you've fetched the most recent version",
|
||||
"DOC4": "You should really just ignore it and run upgrade.php. Really",
|
||||
"php_min_version": "7.4.0",
|
||||
"php_max_major_minor": "8.1",
|
||||
"php_max_wontwork": "8.2.0",
|
||||
"current_snipeit_version": "6.3"
|
||||
"php_min_version": "8.1.0",
|
||||
"php_max_major_minor": "8.2",
|
||||
"php_max_wontwork": "8.3.0",
|
||||
"current_snipeit_version": "7.0"
|
||||
}
|
||||
|
||||
@@ -432,6 +432,22 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bilias"><img src="https://avatars.githubusercontent.com/u/47315739?v=4?s=110" width="110px;" alt="bilias"/><br /><sub><b>bilias</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=bilias" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/coach1988"><img src="https://avatars.githubusercontent.com/u/2565989?v=4?s=110" width="110px;" alt="coach1988"/><br /><sub><b>coach1988</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=coach1988" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mauro-miatello"><img src="https://avatars.githubusercontent.com/u/11910225?v=4?s=110" width="110px;" alt="MrM"/><br /><sub><b>MrM</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=mauro-miatello" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/koiakoia"><img src="https://avatars.githubusercontent.com/u/60405354?v=4?s=110" width="110px;" alt="koiakoia"/><br /><sub><b>koiakoia</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=koiakoia" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mustafa-online"><img src="https://avatars.githubusercontent.com/u/5323832?v=4?s=110" width="110px;" alt="Mustafa Online"/><br /><sub><b>Mustafa Online</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=mustafa-online" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/franceslui"><img src="https://avatars.githubusercontent.com/u/104601439?v=4?s=110" width="110px;" alt="franceslui"/><br /><sub><b>franceslui</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=franceslui" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Q4kK"><img src="https://avatars.githubusercontent.com/u/125313163?v=4?s=110" width="110px;" alt="Q4kK"/><br /><sub><b>Q4kK</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=Q4kK" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/squintfox"><img src="https://avatars.githubusercontent.com/u/55590532?v=4?s=110" width="110px;" alt="squintfox"/><br /><sub><b>squintfox</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=squintfox" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jeffclay"><img src="https://avatars.githubusercontent.com/u/1380084?v=4?s=110" width="110px;" alt="Jeff Clay"/><br /><sub><b>Jeff Clay</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=jeffclay" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PP-JN-RL"><img src="https://avatars.githubusercontent.com/u/52716446?v=4?s=110" width="110px;" alt="Phil J R"/><br /><sub><b>Phil J R</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=PP-JN-RL" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.corelight.com/"><img src="https://avatars.githubusercontent.com/u/1496725?v=4?s=110" width="110px;" alt="i_virus"/><br /><sub><b>i_virus</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=chandanchowdhury" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gitgrimbo"><img src="https://avatars.githubusercontent.com/u/1020541?v=4?s=110" width="110px;" alt="Paul Grime"/><br /><sub><b>Paul Grime</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=gitgrimbo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://leeporte.co.uk"><img src="https://avatars.githubusercontent.com/u/922815?v=4?s=110" width="110px;" alt="Lee Porte"/><br /><sub><b>Lee Porte</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=LeePorte" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bryanlopezinc"><img src="https://avatars.githubusercontent.com/u/23613427?v=4?s=110" width="110px;" alt="BRYAN "/><br /><sub><b>BRYAN </b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=bryanlopezinc" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/U-H-T"><img src="https://avatars.githubusercontent.com/u/64061710?v=4?s=110" width="110px;" alt="U-H-T"/><br /><sub><b>U-H-T</b></sub></a><br /><a href="https://github.com/snipe/snipe-it/commits?author=U-H-T" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ RUN \
|
||||
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.cert" "/var/www/html/storage/ldap_client_tls.cert" \
|
||||
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.key" "/var/www/html/storage/ldap_client_tls.key" \
|
||||
&& chown docker "/var/lib/snipeit/keys/" \
|
||||
&& chown -h docker "/var/www/html/storage/" \
|
||||
&& chown -Rh docker "/var/www/html/storage/" \
|
||||
&& chmod +x /var/www/html/artisan \
|
||||
&& echo "Finished setting up application in /var/www/html"
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||

|
||||
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade) [](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[](#contributing) [](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
|
||||
|
||||
It is built on [Laravel 8](http://laravel.com).
|
||||
It is built on [Laravel 10](http://laravel.com).
|
||||
|
||||
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
|
||||
|
||||
__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, 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.
|
||||
> [!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.
|
||||
|
||||
-----
|
||||
|
||||
@@ -21,7 +22,7 @@ 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.
|
||||
|
||||
[](https://heroku.com/deploy)
|
||||
<!-- [](https://heroku.com/deploy) -->
|
||||
|
||||
-----
|
||||
### User's Manual
|
||||
@@ -32,8 +33,9 @@ For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.i
|
||||
|
||||
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
|
||||
|
||||
**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.**
|
||||
|
||||
> [!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.**
|
||||
>
|
||||
-----
|
||||
|
||||
### Upgrading
|
||||
@@ -57,6 +59,9 @@ Please see the [translations documentation](https://snipe-it.readme.io/docs/tran
|
||||
|
||||
Since the release of the JSON REST API, several third-party developers have been developing modules and libraries to work with Snipe-IT.
|
||||
|
||||
> [!NOTE]
|
||||
> As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
|
||||
|
||||
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
|
||||
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
|
||||
- [InQRy -unmaintained-](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
|
||||
@@ -73,8 +78,6 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
- [UniFi to Snipe-IT](https://github.com/RodneyLeeBrands/UnifiSnipeSync) by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT.
|
||||
- [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT.
|
||||
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by @ReticentRobot - Windows agent for Snipe-IT
|
||||
|
||||
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
|
||||
|
||||
-----
|
||||
|
||||
@@ -92,4 +95,5 @@ The ERD is available [online here](https://drawsql.app/templates/snipe-it).
|
||||
|
||||
### Security
|
||||
|
||||
To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.
|
||||
> [!IMPORTANT]
|
||||
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"description": "The maximum number of search results that can be returned at one time.",
|
||||
"value": "500"
|
||||
},
|
||||
"MAIL_DRIVER": {
|
||||
"MAIL_MAILER": {
|
||||
"description": "Mail driver - Generally SMTP on Heroku - https://snipe-it.readme.io/docs/configuration#required-outgoing-mail-settings",
|
||||
"value": "smtp"
|
||||
},
|
||||
@@ -58,9 +58,9 @@
|
||||
"description": "SMTP Server Password",
|
||||
"value": "YOURPASSWORD"
|
||||
},
|
||||
"MAIL_ENCRYPTION": {
|
||||
"description": "Encryption protocol for email sending.",
|
||||
"value": "null"
|
||||
"MAIL_TLS_VERIFY_PEER": {
|
||||
"description": "Ensure validity of TLS certificate on remote mail server",
|
||||
"value": true
|
||||
},
|
||||
"MAIL_FROM_ADDR": {
|
||||
"description": "Email from address",
|
||||
|
||||
@@ -7,7 +7,7 @@ use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class GeneratePersonalAccessToken extends Command
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\Models\Setting;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\User;
|
||||
use App\Models\Location;
|
||||
use Log;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LdapSync extends Command
|
||||
{
|
||||
@@ -298,7 +298,7 @@ class LdapSync extends Command
|
||||
try {
|
||||
$ldap_manager = Ldap::findLdapUsers($item['manager'], -1, $this->option('filter'));
|
||||
} catch (\Exception $e) {
|
||||
\Log::warning("Manager lookup caused an exception: " . $e->getMessage() . ". Falling back to direct username lookup");
|
||||
Log::warning("Manager lookup caused an exception: " . $e->getMessage() . ". Falling back to direct username lookup");
|
||||
// Hail-mary for Okta manager 'shortnames' - will only work if
|
||||
// Okta configuration is using full email-address-style usernames
|
||||
$ldap_manager = [
|
||||
@@ -390,7 +390,7 @@ class LdapSync extends Command
|
||||
$user->location_id = $location->id;
|
||||
}
|
||||
}
|
||||
|
||||
$location = null;
|
||||
$user->ldap_import = 1;
|
||||
|
||||
$errors = '';
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Console\Commands;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Setting;
|
||||
use Exception;
|
||||
use Crypt;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
/**
|
||||
* Check if a given ip is in a network
|
||||
@@ -160,7 +160,7 @@ class LdapTroubleshooter extends Command
|
||||
$output[] = "-x";
|
||||
$output[] = "-b ".escapeshellarg($settings->ldap_basedn);
|
||||
$output[] = "-D ".escapeshellarg($settings->ldap_uname);
|
||||
$output[] = "-w ".escapeshellarg(\Crypt::Decrypt($settings->ldap_pword));
|
||||
$output[] = "-w ".escapeshellarg(Crypt::Decrypt($settings->ldap_pword));
|
||||
$output[] = escapeshellarg(parenthesized_filter($settings->ldap_filter));
|
||||
if($settings->ldap_tls) {
|
||||
$this->line("# adding STARTTLS option");
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class MoveUploadsToNewDisk extends Command
|
||||
{
|
||||
@@ -74,7 +75,7 @@ class MoveUploadsToNewDisk extends Command
|
||||
$new_url = Storage::disk('public')->url('uploads/'.$public_type.'/'.$filename, $filename);
|
||||
$this->info($type_count.'. PUBLIC: '.$filename.' was copied to '.$new_url);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
$this->error($e);
|
||||
}
|
||||
}
|
||||
@@ -116,7 +117,7 @@ class MoveUploadsToNewDisk extends Command
|
||||
$new_url = Storage::url($private_type . '/' . $filename, $filename);
|
||||
$this->info($type_count . '. PRIVATE: ' . $filename . ' was copied to ' . $new_url);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
$this->error($e);
|
||||
}
|
||||
}
|
||||
@@ -140,7 +141,7 @@ class MoveUploadsToNewDisk extends Command
|
||||
unlink($filename);
|
||||
$public_delete_count++;
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
$this->error($e);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +154,7 @@ class MoveUploadsToNewDisk extends Command
|
||||
unlink($filename);
|
||||
$private_delete_count++;
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
$this->error($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Console\Commands;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
|
||||
@@ -59,7 +60,7 @@ class ObjectImportCommand extends Command
|
||||
|
||||
// This $logFile/useFiles() bit is currently broken, so commenting it out for now
|
||||
// $logFile = $this->option('logfile');
|
||||
// \Log::useFiles($logFile);
|
||||
// Log::useFiles($logFile);
|
||||
$this->comment('======= Importing Items from '.$filename.' =========');
|
||||
$importer->import();
|
||||
|
||||
@@ -112,10 +113,10 @@ class ObjectImportCommand extends Command
|
||||
public function log($string, $level = 'info')
|
||||
{
|
||||
if ($level === 'warning') {
|
||||
\Log::warning($string);
|
||||
Log::warning($string);
|
||||
$this->comment($string);
|
||||
} else {
|
||||
\Log::Info($string);
|
||||
Log::Info($string);
|
||||
if ($this->option('verbose')) {
|
||||
$this->comment($string);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Console\Commands;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use Schema;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class PaveIt extends Command
|
||||
|
||||
@@ -103,7 +103,7 @@ class RecryptFromMcrypt extends Command
|
||||
$this->comment('INFO: No LDAP password found. Skipping... ');
|
||||
} else {
|
||||
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
|
||||
$settings->ldap_pword = \Crypt::encrypt($decrypted_ldap_pword);
|
||||
$settings->ldap_pword = Crypt::encrypt($decrypted_ldap_pword);
|
||||
$settings->save();
|
||||
}
|
||||
/** @var CustomField[] $custom_fields */
|
||||
@@ -132,7 +132,7 @@ class RecryptFromMcrypt extends Command
|
||||
// Try to decrypt the payload using the legacy app key
|
||||
try {
|
||||
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
|
||||
$asset->{$columnName} = \Crypt::encrypt($decrypted_field);
|
||||
$asset->{$columnName} = Crypt::encrypt($decrypted_field);
|
||||
$this->comment($decrypted_field);
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Artisan;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RegenerateAssetTags extends Command
|
||||
|
||||
@@ -6,8 +6,8 @@ use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use Artisan;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RestoreDeletedUsers extends Command
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use ZipArchive;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SQLStreamer {
|
||||
private $input;
|
||||
@@ -125,7 +126,7 @@ class SQLStreamer {
|
||||
while (($buffer = fgets($this->input, SQLStreamer::$buffer_size)) !== false) {
|
||||
$bytes_read += strlen($buffer);
|
||||
if ($this->reading_beginning_of_line) {
|
||||
// \Log::debug("Buffer is: '$buffer'");
|
||||
// Log::debug("Buffer is: '$buffer'");
|
||||
$cleaned_buffer = $this->parse_sql($buffer);
|
||||
if ($this->output) {
|
||||
$bytes_written = fwrite($this->output, $cleaned_buffer);
|
||||
@@ -191,7 +192,7 @@ class RestoreFromBackup extends Command
|
||||
{
|
||||
$dir = getcwd();
|
||||
if( $dir != base_path() ) { // usually only the case when running via webserver, not via command-line
|
||||
\Log::debug("Current working directory is: $dir, changing directory to: ".base_path());
|
||||
Log::debug("Current working directory is: $dir, changing directory to: ".base_path());
|
||||
chdir(base_path()); // TODO - is this *safe* to change on a running script?!
|
||||
}
|
||||
//
|
||||
@@ -297,7 +298,7 @@ class RestoreFromBackup extends Command
|
||||
continue;
|
||||
}
|
||||
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
|
||||
\Log::debug("Found a sql file!");
|
||||
Log::debug("Found a sql file!");
|
||||
$sqlfiles[] = $raw_path;
|
||||
$sqlfile_indices[] = $i;
|
||||
continue;
|
||||
@@ -413,7 +414,7 @@ class RestoreFromBackup extends Command
|
||||
$bytes_read = 0;
|
||||
while (($buffer = fgets($sql_contents, SQLStreamer::$buffer_size)) !== false) {
|
||||
$bytes_read += strlen($buffer);
|
||||
// \Log::debug("Buffer is: '$buffer'");
|
||||
// Log::debug("Buffer is: '$buffer'");
|
||||
$bytes_written = fwrite($pipes[0], $buffer);
|
||||
|
||||
if ($bytes_written === false) {
|
||||
@@ -425,13 +426,13 @@ class RestoreFromBackup extends Command
|
||||
$bytes_read = $sql_importer->line_aware_piping();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("Error during restore!!!! ".$e->getMessage());
|
||||
Log::error("Error during restore!!!! ".$e->getMessage());
|
||||
// FIXME - put these back and/or put them in the right places?!
|
||||
$err_out = fgets($pipes[1]);
|
||||
$err_err = fgets($pipes[2]);
|
||||
\Log::error("Error OUTPUT: ".$err_out);
|
||||
Log::error("Error OUTPUT: ".$err_out);
|
||||
$this->info($err_out);
|
||||
\Log::error("Error ERROR : ".$err_err);
|
||||
Log::error("Error ERROR : ".$err_err);
|
||||
$this->error($err_err);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Console\Commands;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use Artisan;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
|
||||
@@ -43,7 +43,7 @@ class SendCurrentInventoryToUsers extends Command
|
||||
|
||||
$count = 0;
|
||||
foreach ($users as $user) {
|
||||
if (($user->assets->count() > 0) || ($user->accessories->count() > 0) || ($user->licenses->count() > 0)) {
|
||||
if (($user->assets->count() > 0) || ($user->accessories->count() > 0) || ($user->licenses->count() > 0) || ($user->consumables->count() > 0)) {
|
||||
$count++;
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ use App\Notifications\ExpectedCheckinAdminNotification;
|
||||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
{
|
||||
@@ -43,25 +42,31 @@ class SendExpectedCheckinAlerts extends Command
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$whenNotify = Carbon::now();
|
||||
$assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
|
||||
$interval = $settings->audit_warning_days ?? 0;
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
|
||||
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForCheckin($settings)->orderBy('assets.expected_checkin', 'desc')->get();
|
||||
|
||||
$this->info($assets->count().' assets must be checked in on or before '.$interval_date.' is deadline');
|
||||
|
||||
$this->info($whenNotify.' is deadline');
|
||||
$this->info($assets->count().' assets');
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->assigned && $asset->checkedOutToUser()) {
|
||||
Log::info('Sending ExpectedCheckinNotification to ' . $asset->assigned->email);
|
||||
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
|
||||
if ($asset->assignedTo && (isset($asset->assignedTo->email)) && ($asset->assignedTo->email!='') && $asset->checkedOutToUser()) {
|
||||
$this->info('Sending User ExpectedCheckinNotification to: '.$asset->assignedTo->email);
|
||||
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
|
||||
}
|
||||
}
|
||||
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
|
||||
$this->info('Sending Admin ExpectedCheckinNotification to: '.$settings->alert_email);
|
||||
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\Recipients;
|
||||
use App\Models\Recipients\AlertRecipient;
|
||||
use App\Models\Setting;
|
||||
use App\Notifications\ExpiringAssetsNotification;
|
||||
use App\Notifications\SendUpcomingAuditNotification;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendUpcomingAuditReport extends Command
|
||||
@@ -46,39 +44,24 @@ class SendUpcomingAuditReport extends Command
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$interval = $settings->audit_warning_days ?? 0;
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
|
||||
if (($settings->alert_email != '') && ($settings->audit_warning_days) && ($settings->alerts_enabled == 1)) {
|
||||
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
|
||||
$this->info($assets->count().' assets must be audited in on or before '.$interval_date.' is deadline');
|
||||
|
||||
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
|
||||
// Assets due for auditing
|
||||
$this->info('Sending Admin SendUpcomingAuditNotification to: '.$settings->alert_email);
|
||||
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
|
||||
|
||||
$assets = Asset::whereNotNull('next_audit_date')
|
||||
->DueOrOverdueForAudit($settings)
|
||||
->orderBy('last_audit_date', 'asc')->get();
|
||||
|
||||
if ($assets->count() > 0) {
|
||||
$this->info(trans_choice('mail.upcoming-audits', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $settings->audit_warning_days]));
|
||||
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
|
||||
$this->info('Audit report sent to '.$settings->alert_email);
|
||||
} else {
|
||||
$this->info('No assets to be audited. No report sent.');
|
||||
}
|
||||
} elseif ($settings->alert_email == '') {
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif (! $settings->audit_warning_days) {
|
||||
$this->error('No audit warning days set in Admin Notifications. No mail will be sent.');
|
||||
} elseif ($settings->alerts_enabled != 1) {
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
} else {
|
||||
$this->error('Something went wrong. :( ');
|
||||
$this->error('Admin Notifications Email Setting: '.$settings->alert_email);
|
||||
$this->error('Admin Audit Warning Setting: '.$settings->audit_warning_days);
|
||||
$this->error('Admin Alerts Emnabled: '.$settings->alerts_enabled);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SyncAssetCounters extends Command
|
||||
{
|
||||
@@ -58,7 +59,7 @@ class SyncAssetCounters extends Command
|
||||
$asset->save();
|
||||
$bar->advance();
|
||||
|
||||
\Log::debug('Asset: '.$asset->id.' has '.$asset->checkin_counter.' checkins, '.$asset->checkout_counter.' checkouts, and '.$asset->requests_counter.' requests');
|
||||
Log::debug('Asset: '.$asset->id.' has '.$asset->checkin_counter.' checkins, '.$asset->checkout_counter.' checkouts, and '.$asset->requests_counter.' requests');
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\Helpers\Helper;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException;
|
||||
use Log;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
use JsonException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
@@ -44,8 +44,8 @@ class Handler extends ExceptionHandler
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
if ($this->shouldReport($exception)) {
|
||||
if (class_exists(\Log::class)) {
|
||||
\Log::error($exception);
|
||||
if (class_exists(Log::class)) {
|
||||
Log::error($exception);
|
||||
}
|
||||
return parent::report($exception);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/*********************
|
||||
* These two helper methods are more designed for being re-used with the new HasCustomFields Trait
|
||||
*
|
||||
* The 'transform' method is designed for BlahTransformer things that need to return custom field values.
|
||||
*
|
||||
* The 'present' method is designed for when you're trying to generate fieldlists for use in Bootstrap tables
|
||||
* - typically the 'dataTableLayout' method
|
||||
*
|
||||
*********************/
|
||||
class CustomFieldHelper {
|
||||
|
||||
static function transform($fieldset, $item) {
|
||||
if ($fieldset && ($fieldset->fields->count() > 0)) {
|
||||
$fields_array = [];
|
||||
|
||||
foreach ($fieldset->fields as $field) {
|
||||
if ($field->isFieldDecryptable($item->{$field->db_column})) {
|
||||
$decrypted = Helper::gracefulDecrypt($field, $item->{$field->db_column});
|
||||
$value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted'));
|
||||
|
||||
if ($field->format == 'DATE'){
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')){
|
||||
$value = Helper::getFormattedDateObject($value, 'date', false);
|
||||
} else {
|
||||
$value = strtoupper(trans('admin/custom_fields/general.encrypted'));
|
||||
}
|
||||
}
|
||||
|
||||
$fields_array[$field->name] = [
|
||||
'field' => e($field->db_column),
|
||||
'value' => e($value),
|
||||
'field_format' => $field->format,
|
||||
'element' => $field->element,
|
||||
];
|
||||
|
||||
} else {
|
||||
$value = $item->{$field->db_column};
|
||||
|
||||
if (($field->format == 'DATE') && (!is_null($value)) && ($value!='')){
|
||||
$value = Helper::getFormattedDateObject($value, 'date', false);
|
||||
}
|
||||
|
||||
$fields_array[$field->name] = [
|
||||
'field' => e($field->db_column),
|
||||
'value' => e($value),
|
||||
'field_format' => $field->format,
|
||||
'element' => $field->element,
|
||||
];
|
||||
}
|
||||
|
||||
return $fields_array;
|
||||
}
|
||||
} else {
|
||||
return new \stdClass; // HACK to force generation of empty object instead of empty list
|
||||
}
|
||||
}
|
||||
|
||||
static function present($field) {
|
||||
return [
|
||||
'field' => 'custom_fields.'.$field->db_column,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => $field->name,
|
||||
'formatter'=> 'customFieldsFormatter',
|
||||
'escape' => true,
|
||||
'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '',
|
||||
'visible' => ($field->show_in_listview == '1') ? true : false,
|
||||
];
|
||||
}
|
||||
}
|
||||
+63
-9
@@ -12,10 +12,12 @@ use App\Models\Depreciation;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\License;
|
||||
use Crypt;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Image;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Intervention\Image\ImageManagerStatic as Image;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class Helper
|
||||
{
|
||||
@@ -412,7 +414,7 @@ class Helper
|
||||
|
||||
if ($index >= $total_colors) {
|
||||
|
||||
\Log::info('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
Log::info('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
//patch fix for array key overflow (color count starts at 1, array starts at 0)
|
||||
$index = $index - $total_colors - 1;
|
||||
|
||||
@@ -649,6 +651,17 @@ class Helper
|
||||
return $customfields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the different types of custom fields there are
|
||||
* TODO - how to make this more general? Or more useful? or more dynamic?
|
||||
* idea - key of classname, *value* of trans? (thus having to make this a method, which is fine)
|
||||
*/
|
||||
static $itemtypes_having_custom_fields = [
|
||||
0 => \App\Models\Asset::class,
|
||||
1 => \App\Models\User::class,
|
||||
// 2 => \App\Models\Accessory::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the list of custom field formats in an array to make a dropdown menu
|
||||
*
|
||||
@@ -842,7 +855,7 @@ class Helper
|
||||
$filetype = @finfo_file($finfo, $file);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif')) {
|
||||
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif') || ($filetype == 'image/avif')) {
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
@@ -875,12 +888,15 @@ class Helper
|
||||
$permission_name = $permission[$x]['permission'];
|
||||
|
||||
if ($permission[$x]['display'] === true) {
|
||||
if ($selected_arr) {
|
||||
|
||||
if (is_array($selected_arr)) {
|
||||
|
||||
if (array_key_exists($permission_name, $selected_arr)) {
|
||||
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
|
||||
} else {
|
||||
$permissions_arr[$permission_name] = '0';
|
||||
}
|
||||
|
||||
} else {
|
||||
$permissions_arr[$permission_name] = '0';
|
||||
}
|
||||
@@ -1012,7 +1028,7 @@ class Helper
|
||||
|
||||
|
||||
try {
|
||||
$tmp_date = new \Carbon($date);
|
||||
$tmp_date = new Carbon($date);
|
||||
|
||||
if ($type == 'datetime') {
|
||||
$dt['datetime'] = $tmp_date->format('Y-m-d H:i:s');
|
||||
@@ -1029,7 +1045,7 @@ class Helper
|
||||
return $dt['formatted'];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::warning($e);
|
||||
Log::warning($e);
|
||||
return $date.' (Invalid '.$type.' value.)';
|
||||
}
|
||||
|
||||
@@ -1106,6 +1122,8 @@ class Helper
|
||||
'jpeg' => 'far fa-image',
|
||||
'gif' => 'far fa-image',
|
||||
'png' => 'far fa-image',
|
||||
'webp' => 'far fa-image',
|
||||
'avif' => 'far fa-image',
|
||||
// word
|
||||
'doc' => 'far fa-file-word',
|
||||
'docx' => 'far fa-file-word',
|
||||
@@ -1141,6 +1159,8 @@ class Helper
|
||||
case 'jpeg':
|
||||
case 'gif':
|
||||
case 'png':
|
||||
case 'webp':
|
||||
case 'avif':
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
@@ -1338,7 +1358,7 @@ class Helper
|
||||
public static function isDemoMode() {
|
||||
if (config('app.lock_passwords') === true) {
|
||||
return true;
|
||||
\Log::debug('app locked!');
|
||||
Log::debug('app locked!');
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1431,7 +1451,7 @@ class Helper
|
||||
|
||||
foreach (self::$language_map as $legacy => $new) {
|
||||
if ($language_code == $legacy) {
|
||||
\Log::debug('Current language is '.$legacy.', using '.$new.' instead');
|
||||
Log::debug('Current language is '.$legacy.', using '.$new.' instead');
|
||||
return $new;
|
||||
}
|
||||
}
|
||||
@@ -1455,4 +1475,38 @@ class Helper
|
||||
return $new_locale; // better that you have some weird locale that doesn't fit into our mappings anywhere than 'void'
|
||||
}
|
||||
|
||||
|
||||
static public function getRedirectOption($request, $id, $table, $asset_id = null)
|
||||
{
|
||||
|
||||
$redirect_option = Session::get('redirect_option');
|
||||
$checkout_to_type = Session::get('checkout_to_type');
|
||||
|
||||
//return to index
|
||||
if ($redirect_option == '0') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
//return to thing being assigned
|
||||
if ($redirect_option == '1') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return redirect()->route('hardware.show', $id ? $id : $asset_id)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
//return to thing being assigned to
|
||||
if ($redirect_option == '2') {
|
||||
switch ($checkout_to_type) {
|
||||
case 'user':
|
||||
return redirect()->route('users.show', $request->assigned_user)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
case 'location':
|
||||
return redirect()->route('locations.show', $request->assigned_location)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
case 'asset':
|
||||
return redirect()->route('hardware.show', $request->assigned_asset)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Redirect;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/** This controller handles all actions related to Accessories for
|
||||
* the Snipe-IT Asset Management application.
|
||||
@@ -57,7 +58,7 @@ class AccessoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param ImageUploadRequest $request
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
@@ -150,7 +151,7 @@ class AccessoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param ImageUploadRequest $request
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null)
|
||||
@@ -204,7 +205,7 @@ class AccessoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function destroy($accessoryId)
|
||||
@@ -224,7 +225,7 @@ class AccessoriesController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('accessories'.'/'.$accessory->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Accessory;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Accessory\HttpFoundation\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AccessoriesFilesController extends Controller
|
||||
{
|
||||
@@ -85,7 +86,7 @@ class AccessoriesFilesController extends Controller
|
||||
try {
|
||||
Storage::delete('accessories/'.$log->filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +113,7 @@ class AccessoriesFilesController extends Controller
|
||||
public function show($accessoryId = null, $fileId = null, $download = true)
|
||||
{
|
||||
|
||||
\Log::debug('Private filesystem is: '.config('filesystems.default'));
|
||||
Log::debug('Private filesystem is: '.config('filesystems.default'));
|
||||
$accessory = Accessory::find($accessoryId);
|
||||
|
||||
|
||||
@@ -129,8 +130,8 @@ class AccessoriesFilesController extends Controller
|
||||
$file = 'private_uploads/accessories/'.$log->filename;
|
||||
|
||||
if (Storage::missing($file)) {
|
||||
\Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
\Log::debug('URL should be '.Storage::url($file));
|
||||
Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
Log::debug('URL should be '.Storage::url($file));
|
||||
|
||||
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
|
||||
@@ -44,7 +44,7 @@ class AccessoryCheckinController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param null $accessoryUserId
|
||||
* @param string $backto
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @internal param int $accessoryId
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ class AccessoryCheckoutController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(Request $request, $accessoryId)
|
||||
|
||||
@@ -23,13 +23,13 @@ use App\Notifications\AcceptanceAssetAcceptedNotification;
|
||||
use App\Notifications\AcceptanceAssetDeclinedNotification;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\SettingsController;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Carbon\Carbon;
|
||||
use phpDocumentor\Reflection\Types\Compound;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AcceptanceController extends Controller
|
||||
{
|
||||
@@ -80,7 +80,7 @@ class AcceptanceController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request, $id)
|
||||
{
|
||||
@@ -223,6 +223,7 @@ class AcceptanceController extends Controller
|
||||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'eula' => $item->getEula(),
|
||||
'note' => $request->input('note'),
|
||||
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
|
||||
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'),
|
||||
'assigned_to' => $assigned_to,
|
||||
@@ -233,12 +234,12 @@ class AcceptanceController extends Controller
|
||||
];
|
||||
|
||||
if ($pdf_view_route!='') {
|
||||
\Log::debug($pdf_filename.' is the filename, and the route was specified.');
|
||||
Log::debug($pdf_filename.' is the filename, and the route was specified.');
|
||||
$pdf = Pdf::loadView($pdf_view_route, $data);
|
||||
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output());
|
||||
}
|
||||
|
||||
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename);
|
||||
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
|
||||
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
|
||||
event(new CheckoutAccepted($acceptance));
|
||||
|
||||
@@ -306,10 +307,12 @@ class AcceptanceController extends Controller
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'item_tag' => $item->asset_tag,
|
||||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'note' => $request->input('note'),
|
||||
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
|
||||
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
|
||||
'assigned_to' => $assigned_to,
|
||||
@@ -318,12 +321,12 @@ class AcceptanceController extends Controller
|
||||
];
|
||||
|
||||
if ($pdf_view_route!='') {
|
||||
\Log::debug($pdf_filename.' is the filename, and the route was specified.');
|
||||
Log::debug($pdf_filename.' is the filename, and the route was specified.');
|
||||
$pdf = Pdf::loadView($pdf_view_route, $data);
|
||||
Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output());
|
||||
}
|
||||
|
||||
$acceptance->decline($sig_filename);
|
||||
$acceptance->decline($sig_filename, $request->input('note'));
|
||||
$acceptance->notify(new AcceptanceAssetDeclinedNotification($data));
|
||||
event(new CheckoutDeclined($acceptance));
|
||||
$return_msg = trans('admin/users/message.declined');
|
||||
|
||||
@@ -4,8 +4,9 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Actionlog;
|
||||
use Response;
|
||||
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
class ActionlogController extends Controller
|
||||
{
|
||||
public function displaySig($filename)
|
||||
@@ -14,18 +15,23 @@ class ActionlogController extends Controller
|
||||
// file_get_contents, so we set the error reporting for just this class
|
||||
error_reporting(0);
|
||||
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
$file = config('app.private_uploads').'/signatures/'.$filename;
|
||||
$filetype = Helper::checkUploadIsImage($file);
|
||||
$disk = config('filesystems.default');
|
||||
switch (config("filesystems.disks.$disk.driver")) {
|
||||
case 's3':
|
||||
$file = 'private_uploads/signatures/'.$filename;
|
||||
return redirect()->away(Storage::disk($disk)->temporaryUrl($file, now()->addMinutes(5)));
|
||||
default:
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
$file = config('app.private_uploads').'/signatures/'.$filename;
|
||||
$filetype = Helper::checkUploadIsImage($file);
|
||||
|
||||
$contents = file_get_contents($file, false, stream_context_create(['http' => ['ignore_errors' => true]]));
|
||||
if ($contents === false) {
|
||||
\Log::warn('File '.$file.' not found');
|
||||
return false;
|
||||
} else {
|
||||
return Response::make($contents)->header('Content-Type', $filetype);
|
||||
}
|
||||
|
||||
$contents = file_get_contents($file, false, stream_context_create(['http' => ['ignore_errors' => true]]));
|
||||
if ($contents === false) {
|
||||
Log::warning('File '.$file.' not found');
|
||||
return false;
|
||||
} else {
|
||||
return Response::make($contents)->header('Content-Type', $filetype);
|
||||
}
|
||||
}
|
||||
public function getStoredEula($filename){
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
|
||||
@@ -10,9 +10,9 @@ use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
@@ -274,7 +274,7 @@ class AccessoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function checkout(Request $request, $accessoryId)
|
||||
{
|
||||
@@ -320,7 +320,7 @@ class AccessoriesController extends Controller
|
||||
* @param Request $request
|
||||
* @param int $accessoryUserId
|
||||
* @param string $backto
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @internal param int $accessoryId
|
||||
*/
|
||||
public function checkin(Request $request, $accessoryUserId = null)
|
||||
|
||||
@@ -8,7 +8,7 @@ use App\Http\Transformers\AssetMaintenancesTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetMaintenance;
|
||||
use App\Models\Company;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Models\AssetModel;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to asset models for
|
||||
@@ -66,7 +67,7 @@ class AssetModelsController extends Controller
|
||||
'models.deleted_at',
|
||||
'models.updated_at',
|
||||
])
|
||||
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues')
|
||||
->with('category', 'depreciation', 'manufacturer')
|
||||
->withCount('assets as assets_count');
|
||||
|
||||
if ($request->input('status')=='deleted') {
|
||||
@@ -224,7 +225,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('assetmodels/'.$assetmodel->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,17 +27,16 @@ use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use \Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Input;
|
||||
use Paginator;
|
||||
use Slack;
|
||||
use Str;
|
||||
use TCPDF;
|
||||
use Validator;
|
||||
use Route;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
||||
/**
|
||||
@@ -59,7 +58,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request, $audit = null)
|
||||
public function index(Request $request, $action = null, $upcoming_status = null)
|
||||
{
|
||||
|
||||
$filter_non_deprecable_assets = false;
|
||||
@@ -94,6 +93,7 @@ class AssetsController extends Controller
|
||||
'serial',
|
||||
'model_number',
|
||||
'last_checkout',
|
||||
'last_checkin',
|
||||
'notes',
|
||||
'expected_checkin',
|
||||
'order_number',
|
||||
@@ -120,7 +120,7 @@ class AssetsController extends Controller
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
}
|
||||
|
||||
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
|
||||
$all_custom_fields = CustomField::where('type', Asset::class); //used as a 'cache' of custom fields throughout this page load
|
||||
foreach ($all_custom_fields as $field) {
|
||||
$allowed_columns[] = $field->db_column_name();
|
||||
}
|
||||
@@ -154,17 +154,44 @@ class AssetsController extends Controller
|
||||
$assets->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
// This is used by the audit reporting routes
|
||||
if (Gate::allows('audit', Asset::class)) {
|
||||
switch ($audit) {
|
||||
case 'due':
|
||||
$assets->DueOrOverdueForAudit($settings);
|
||||
break;
|
||||
case 'overdue':
|
||||
$assets->overdueForAudit($settings);
|
||||
break;
|
||||
|
||||
/**
|
||||
* Handle due and overdue audits and checkin dates
|
||||
*/
|
||||
switch ($action) {
|
||||
case 'audits':
|
||||
|
||||
switch ($upcoming_status) {
|
||||
case 'due':
|
||||
$assets->DueForAudit($settings);
|
||||
break;
|
||||
case 'overdue':
|
||||
$assets->OverdueForAudit();
|
||||
break;
|
||||
case 'due-or-overdue':
|
||||
$assets->DueOrOverdueForAudit($settings);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'checkins':
|
||||
switch ($upcoming_status) {
|
||||
case 'due':
|
||||
$assets->DueForCheckin($settings);
|
||||
break;
|
||||
case 'overdue':
|
||||
$assets->OverdueForCheckin();
|
||||
break;
|
||||
case 'due-or-overdue':
|
||||
$assets->DueOrOverdueForCheckin($settings);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End handling due and overdue audits and checkin dates
|
||||
*/
|
||||
|
||||
|
||||
// This is used by the sidenav, mostly
|
||||
@@ -559,43 +586,8 @@ class AssetsController extends Controller
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
|
||||
// Update custom fields in the database.
|
||||
$model = AssetModel::find($request->input('model_id'));
|
||||
$asset->customFill($request, Auth::user(), true);
|
||||
|
||||
// Check that it's an object and not a collection
|
||||
// (Sometimes people send arrays here and they shouldn't
|
||||
if (($model) && ($model instanceof AssetModel) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
|
||||
// Set the field value based on what was sent in the request
|
||||
$field_val = $request->input($field->db_column, null);
|
||||
|
||||
// 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');
|
||||
$field_val = $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
|
||||
if ($field->field_encrypted == '1') {
|
||||
Log::debug('This model field is encrypted in this fieldset.');
|
||||
|
||||
if (Gate::allows('admin')) {
|
||||
|
||||
// If input value is null, use custom field's default value
|
||||
if (($field_val == null) && ($request->has('model_id') != '')) {
|
||||
$field_val = Crypt::encrypt($field->defaultValue($request->get('model_id')));
|
||||
} else {
|
||||
$field_val = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$asset->{$field->db_column} = $field_val;
|
||||
}
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
if ($request->get('assigned_user')) {
|
||||
@@ -614,6 +606,8 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
@@ -654,23 +648,8 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = AssetModel::find($asset->model_id);
|
||||
|
||||
// Update custom fields
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$problems_updating_encrypted_custom_fields = !$asset->customFill($request, Auth::user());
|
||||
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
@@ -692,7 +671,11 @@ class AssetsController extends Controller
|
||||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $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('error', null, $asset->getErrors()), 200);
|
||||
@@ -802,7 +785,6 @@ class AssetsController extends Controller
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
];
|
||||
|
||||
|
||||
// This item is checked out to a location
|
||||
if (request('checkout_to_type') == 'location') {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
@@ -829,13 +811,10 @@ class AssetsController extends Controller
|
||||
$asset->status_id = $request->get('status_id');
|
||||
}
|
||||
|
||||
|
||||
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.'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
$checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
|
||||
$expected_checkin = request('expected_checkin', null);
|
||||
$note = request('note', null);
|
||||
@@ -851,8 +830,6 @@ class AssetsController extends Controller
|
||||
// $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')));
|
||||
}
|
||||
@@ -983,25 +960,39 @@ class AssetsController extends Controller
|
||||
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
$rules = [
|
||||
'asset_tag' => 'required',
|
||||
'location_id' => 'exists:locations,id|nullable|numeric',
|
||||
'next_audit_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
|
||||
// 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'),
|
||||
], trans('admin/hardware/message.no_tag')), 200);
|
||||
}
|
||||
|
||||
|
||||
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
|
||||
|
||||
|
||||
if ($asset) {
|
||||
// We don't want to log this as a normal update, so let's bypass that
|
||||
|
||||
/**
|
||||
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
||||
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
||||
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
||||
* the audit log entry we're creating through this controller.
|
||||
*
|
||||
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
||||
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
||||
* will bypass normal model-level validation that's usually handled at the observer )
|
||||
*
|
||||
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||
*
|
||||
* @see \App\Observers\AssetObserver::updating()
|
||||
*/
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->next_audit_date = $dt;
|
||||
|
||||
@@ -1017,8 +1008,12 @@ class AssetsController extends Controller
|
||||
|
||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||
|
||||
if ($asset->save()) {
|
||||
$log = $asset->logAudit(request('note'), request('location_id'));
|
||||
/**
|
||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||
* We have to invoke this manually because of the unsetEventDispatcher() above.)
|
||||
*/
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
$asset->logAudit(request('note'), request('location_id'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
@@ -1026,9 +1021,23 @@ class AssetsController extends Controller
|
||||
'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(),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.e($request->input('asset_tag')).' not found'));
|
||||
|
||||
// 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'),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1062,8 +1071,7 @@ class AssetsController extends Controller
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'assetlog', 'company','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests')
|
||||
->requestableAssets();
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests');
|
||||
|
||||
|
||||
|
||||
@@ -1071,7 +1079,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())) {
|
||||
@@ -1101,6 +1109,7 @@ class AssetsController extends Controller
|
||||
break;
|
||||
}
|
||||
|
||||
$assets->requestableAssets();
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Events\ComponentCheckedIn;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ComponentsController extends Controller
|
||||
{
|
||||
@@ -331,7 +332,7 @@ class ComponentsController extends Controller
|
||||
// actually checked out.
|
||||
$component_assets->assigned_qty = $qty_remaining_in_checkout;
|
||||
|
||||
\Log::debug($component_asset_id.' - '.$qty_remaining_in_checkout.' remaining in record '.$component_assets->id);
|
||||
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]);
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ConsumablesController extends Controller
|
||||
{
|
||||
@@ -277,7 +278,7 @@ class ConsumablesController extends Controller
|
||||
if (!$user = User::find($request->input('assigned_to'))) {
|
||||
// Return error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
|
||||
\Log::debug('No valid user');
|
||||
Log::debug('No valid user');
|
||||
}
|
||||
|
||||
// Update the consumable data
|
||||
|
||||
@@ -179,7 +179,7 @@ class CustomFieldsController extends Controller
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($field_id)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ class CustomFieldsetsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomField::class);
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count')->get();
|
||||
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
|
||||
}
|
||||
@@ -118,14 +118,14 @@ class CustomFieldsetsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', CustomField::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
|
||||
$modelsCount = $fieldset->models->count();
|
||||
$modelsCount = $fieldset->customizables()->count();
|
||||
$fieldsCount = $fieldset->fields->count();
|
||||
|
||||
if (($modelsCount > 0) || ($fieldsCount > 0)) {
|
||||
|
||||
@@ -8,7 +8,7 @@ use App\Http\Transformers\DepartmentsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Company;
|
||||
use App\Models\Department;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\GroupsTransformer;
|
||||
use App\Models\Group;
|
||||
use Illuminate\Http\Request;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
class GroupsController extends Controller
|
||||
@@ -62,13 +62,16 @@ class GroupsController extends Controller
|
||||
{
|
||||
$this->authorize('superadmin');
|
||||
$group = new Group;
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
|
||||
|
||||
$group->name = $request->input('name');
|
||||
$group->created_by = Auth::user()->id;
|
||||
$group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here
|
||||
$group->permissions = json_encode($request->input('permissions', $groupPermissions));
|
||||
|
||||
if ($group->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new GroupsTransformer)->transformGroup($group), trans('admin/groups/message.success.create')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
|
||||
@@ -86,7 +89,6 @@ class GroupsController extends Controller
|
||||
{
|
||||
$this->authorize('superadmin');
|
||||
$group = Group::findOrFail($id);
|
||||
|
||||
return (new GroupsTransformer)->transformGroup($group);
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ class GroupsController extends Controller
|
||||
$group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here
|
||||
|
||||
if ($group->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new GroupsTransformer)->transformGroup($group), trans('admin/groups/message.success.update')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
|
||||
|
||||
@@ -9,13 +9,14 @@ use App\Http\Transformers\ImportsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Import;
|
||||
use Artisan;
|
||||
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 Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ImportController extends Controller
|
||||
{
|
||||
@@ -159,10 +160,10 @@ class ImportController extends Controller
|
||||
|
||||
// Run a backup immediately before processing
|
||||
if ($request->get('run-backup')) {
|
||||
\Log::debug('Backup manually requested via importer');
|
||||
Log::debug('Backup manually requested via importer');
|
||||
Artisan::call('snipeit:backup', ['--filename' => 'pre-import-backup-'.date('Y-m-d-H:i:s')]);
|
||||
} else {
|
||||
\Log::debug('NO BACKUP requested via importer');
|
||||
Log::debug('NO BACKUP requested via importer');
|
||||
}
|
||||
|
||||
$import = Import::find($import_id);
|
||||
|
||||
@@ -8,7 +8,7 @@ use App\Http\Transformers\LabelsTransformer;
|
||||
use App\Models\Labels\Label;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\ItemNotFoundException;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class LabelsController extends Controller
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LicenseSeatsController extends Controller
|
||||
|
||||
@@ -235,7 +235,13 @@ class LocationsController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
$location = Location::withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->findOrFail($id);
|
||||
|
||||
if (! $location->isDeletable()) {
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('error', null, trans('admin/companies/message.assoc_users')));
|
||||
|
||||
@@ -12,7 +12,7 @@ use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Models\CustomField;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
|
||||
@@ -32,19 +32,26 @@ class ReportsController extends Controller
|
||||
}
|
||||
|
||||
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
|
||||
$actionlogs = $actionlogs->where('item_id', '=', $request->input('item_id'))
|
||||
$actionlogs = $actionlogs->where(function($query) use ($request)
|
||||
{
|
||||
$query->where('item_id', '=', $request->input('item_id'))
|
||||
->where('item_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')))
|
||||
->orWhere(function($query) use ($request)
|
||||
{
|
||||
$query->where('target_id', '=', $request->input('item_id'))
|
||||
->where('target_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('action_type')) {
|
||||
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
if ($request->filled('user_id')) {
|
||||
$actionlogs = $actionlogs->where('user_id', '=', $request->input('user_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('action_source')) {
|
||||
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
@@ -33,18 +33,18 @@ class SettingsController extends Controller
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->ldap_enabled!='1') {
|
||||
\Log::debug('LDAP is not enabled cannot test.');
|
||||
Log::debug('LDAP is not enabled cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
|
||||
}
|
||||
|
||||
\Log::debug('Preparing to test LDAP connection');
|
||||
Log::debug('Preparing to test LDAP connection');
|
||||
|
||||
$message = []; //where we collect together test messages
|
||||
try {
|
||||
$connection = Ldap::connectToLdap();
|
||||
try {
|
||||
$message['bind'] = ['message' => 'Successfully bound to LDAP server.'];
|
||||
\Log::debug('attempting to bind to LDAP for LDAP test');
|
||||
Log::debug('attempting to bind to LDAP for LDAP test');
|
||||
Ldap::bindAdminToLdap($connection);
|
||||
$message['login'] = [
|
||||
'message' => 'Successfully connected to LDAP server.',
|
||||
@@ -75,13 +75,13 @@ class SettingsController extends Controller
|
||||
|
||||
return response()->json($message, 200);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Bind failed');
|
||||
\Log::debug("Exception was: ".$e->getMessage());
|
||||
Log::debug('Bind failed');
|
||||
Log::debug("Exception was: ".$e->getMessage());
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
//return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed but we cannot debug it any further on our end.');
|
||||
Log::debug('Connection failed but we cannot debug it any further on our end.');
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class SettingsController extends Controller
|
||||
{
|
||||
|
||||
if (Setting::getSettings()->ldap_enabled != '1') {
|
||||
\Log::debug('LDAP is not enabled. Cannot test.');
|
||||
Log::debug('LDAP is not enabled. Cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
|
||||
}
|
||||
|
||||
@@ -104,39 +104,39 @@ class SettingsController extends Controller
|
||||
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
\Log::debug('LDAP Validation test failed.');
|
||||
Log::debug('LDAP Validation test failed.');
|
||||
$validation_errors = implode(' ',$validator->errors()->all());
|
||||
return response()->json(['message' => $validator->errors()->all()], 400);
|
||||
}
|
||||
|
||||
|
||||
|
||||
\Log::debug('Preparing to test LDAP login');
|
||||
Log::debug('Preparing to test LDAP login');
|
||||
try {
|
||||
$connection = Ldap::connectToLdap();
|
||||
try {
|
||||
Ldap::bindAdminToLdap($connection);
|
||||
\Log::debug('Attempting to bind to LDAP for LDAP test');
|
||||
Log::debug('Attempting to bind to LDAP for LDAP test');
|
||||
try {
|
||||
$ldap_user = Ldap::findAndBindUserLdap($request->input('ldaptest_user'), $request->input('ldaptest_password'));
|
||||
if ($ldap_user) {
|
||||
\Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
|
||||
Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
|
||||
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
|
||||
}
|
||||
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('LDAP login failed');
|
||||
Log::debug('LDAP login failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Bind failed');
|
||||
Log::debug('Bind failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
//return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed');
|
||||
Log::debug('Connection failed');
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
@@ -181,19 +181,19 @@ class SettingsController extends Controller
|
||||
|
||||
$file_parts = explode('.', $file);
|
||||
$extension = end($file_parts);
|
||||
\Log::debug($extension);
|
||||
Log::debug($extension);
|
||||
|
||||
// Only generated barcodes would have a .png file extension
|
||||
if ($extension == 'png') {
|
||||
\Log::debug('Deleting: '.$file);
|
||||
Log::debug('Deleting: '.$file);
|
||||
|
||||
|
||||
try {
|
||||
Storage::disk('public')->delete($file);
|
||||
\Log::debug('Deleting: '.$file);
|
||||
Log::debug('Deleting: '.$file);
|
||||
$file_count++;
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,15 @@ use App\Http\Transformers\UsersTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UsersController extends Controller
|
||||
{
|
||||
@@ -37,7 +38,7 @@ class UsersController extends Controller
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$users = User::select([
|
||||
$allowed_columns = [
|
||||
'users.activated',
|
||||
'users.created_by',
|
||||
'users.address',
|
||||
@@ -75,9 +76,18 @@ class UsersController extends Controller
|
||||
'users.autoassign_licenses',
|
||||
'users.website',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
|
||||
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count');
|
||||
];
|
||||
|
||||
foreach (CustomField::where('type', User::class)->get() as $field) {
|
||||
$allowed_columns[] = $field->db_column_name();
|
||||
}
|
||||
$users = User::select($allowed_columns)->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
|
||||
->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');
|
||||
|
||||
|
||||
if ($request->filled('search') != '') {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('activated')) {
|
||||
$users = $users->where('users.activated', '=', $request->input('activated'));
|
||||
@@ -187,12 +197,26 @@ class UsersController extends Controller
|
||||
$users->has('accessories', '=', $request->input('accessories_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('manages_users_count')) {
|
||||
$users->has('manages_users_count', '=', $request->input('manages_users_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('manages_locations_count')) {
|
||||
$users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('autoassign_licenses')) {
|
||||
$users->where('autoassign_licenses', '=', $request->input('autoassign_licenses'));
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
if ($request->filled('location_id') != '') {
|
||||
$users = $users->UserLocation($request->input('location_id'), $request->input('search'));
|
||||
}
|
||||
|
||||
if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) {
|
||||
$users = $users->onlyTrashed();
|
||||
} elseif (($request->filled('all')) && ($request->input('all') == 'true')) {
|
||||
$users = $users->withTrashed();
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -244,6 +268,8 @@ class UsersController extends Controller
|
||||
'licenses_count',
|
||||
'consumables_count',
|
||||
'accessories_count',
|
||||
'manages_users_count',
|
||||
'manages_locations_count',
|
||||
'phone',
|
||||
'address',
|
||||
'city',
|
||||
@@ -262,17 +288,14 @@ class UsersController extends Controller
|
||||
'website',
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
|
||||
$users = $users->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) {
|
||||
$users = $users->onlyTrashed();
|
||||
} elseif (($request->filled('all')) && ($request->input('all') == 'true')) {
|
||||
$users = $users->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
// Apply companyable scope
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
|
||||
@@ -379,7 +402,9 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
|
||||
$user->customFill($request,Auth::user());
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
@@ -403,9 +428,16 @@ class UsersController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count')->findOrFail($id);
|
||||
|
||||
return (new UsersTransformer)->transformUser($user);
|
||||
$user = User::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');
|
||||
|
||||
if ($user = Company::scopeCompanyables($user)->find($id)) {
|
||||
$this->authorize('view', $user);
|
||||
return (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found', compact('id'))));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -423,6 +455,8 @@ class UsersController extends Controller
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
$user = User::findOrFail($id);
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
$this->authorize('update', $user);
|
||||
|
||||
/**
|
||||
* This is a janky hack to prevent people from changing admin demo user data on the public demo.
|
||||
@@ -455,45 +489,42 @@ class UsersController extends Controller
|
||||
if ($request->has('permissions')) {
|
||||
$permissions_array = $request->input('permissions');
|
||||
|
||||
// Strip out the superuser permission if the API user isn't a superadmin
|
||||
// Strip out the individual superuser permission if the API user isn't a superadmin
|
||||
if (! Auth::user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
}
|
||||
|
||||
$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)]);
|
||||
|
||||
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
|
||||
$user->customFill($request,Auth::user());
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
// Sync group memberships:
|
||||
// This was changed in Snipe-IT v4.6.x to 4.7, since we upgraded to Laravel 5.5
|
||||
// which changes the behavior of has vs filled.
|
||||
// The $request->has method will now return true even if the input value is an empty string or null.
|
||||
// A new $request->filled method has was added that provides the previous behavior of the has method.
|
||||
// Check if the request has groups passed and has a value, AND that the user us a superuser
|
||||
if (($request->has('groups')) && (Auth::user()->isSuperUser())) {
|
||||
|
||||
// Check if the request has groups passed and has a value
|
||||
if ($request->filled('groups')) {
|
||||
$validator = Validator::make($request->all(), [
|
||||
$validator = Validator::make($request->only('groups'), [
|
||||
'groups.*' => 'integer|exists:permission_groups,id',
|
||||
]);
|
||||
|
||||
if ($validator->fails()){
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
}
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
// The groups field has been passed but it is null, so we should blank it out
|
||||
} elseif ($request->has('groups')) {
|
||||
$user->groups()->sync([]);
|
||||
}
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
|
||||
}
|
||||
|
||||
// Sync the groups since the user is a superuser and the groups pass validation
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
|
||||
}
|
||||
@@ -512,37 +543,50 @@ class UsersController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
if (($user->assets) && ($user->assets->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete_has_assets')));
|
||||
}
|
||||
if ($user) {
|
||||
|
||||
if (($user->licenses) && ($user->licenses->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has '.$user->licenses->count().' license(s) associated with them and cannot be deleted.'));
|
||||
}
|
||||
|
||||
if (($user->accessories) && ($user->accessories->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has '.$user->accessories->count().' accessories associated with them.'));
|
||||
}
|
||||
|
||||
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has '.$user->managedLocations()->count().' locations that they manage.'));
|
||||
}
|
||||
|
||||
if ($user->delete()) {
|
||||
|
||||
// Remove the user's avatar if they have one
|
||||
if (Storage::disk('public')->exists('avatars/'.$user->avatar)) {
|
||||
try {
|
||||
Storage::disk('public')->delete('avatars/'.$user->avatar);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
if ($user->id === Auth::id()) {
|
||||
// Redirect to the user management page
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.cannot_delete_yourself')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
|
||||
if (($user->assets) && ($user->assets->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('admin/users/message.error.delete_has_assets_var', $user->assets()->count(), ['count'=> $user->assets()->count()])));
|
||||
}
|
||||
|
||||
if (($user->licenses) && ($user->licenses->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('admin/users/message.error.delete_has_licenses_var', $user->licenses()->count(), ['count'=> $user->licenses()->count()])));
|
||||
}
|
||||
|
||||
if (($user->accessories) && ($user->accessories->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('admin/users/message.error.delete_has_accessories_var', $user->accessories()->count(), ['count'=> $user->accessories()->count()])));
|
||||
}
|
||||
|
||||
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('admin/users/message.error.delete_has_locations_var', $user->managedLocations()->count(), ['count'=> $user->managedLocations()->count()])));
|
||||
}
|
||||
|
||||
if (($user->managesUsers()) && ($user->managesUsers()->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('admin/users/message.error.delete_has_users_var', $user->managesUsers()->count(), ['count'=> $user->managesUsers()->count()])));
|
||||
}
|
||||
|
||||
if ($user->delete()) {
|
||||
|
||||
// Remove the user's avatar if they have one
|
||||
if (Storage::disk('public')->exists('avatars/' . $user->avatar)) {
|
||||
try {
|
||||
Storage::disk('public')->delete('avatars/' . $user->avatar);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete')));
|
||||
@@ -560,7 +604,31 @@ class UsersController extends Controller
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
|
||||
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
$this->authorize('view', $user);
|
||||
|
||||
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model');
|
||||
|
||||
|
||||
// Filter on category ID
|
||||
if ($request->filled('category_id')) {
|
||||
$assets = $assets->InCategory($request->input('category_id'));
|
||||
}
|
||||
|
||||
|
||||
// Filter on model ID
|
||||
if ($request->filled('model_id')) {
|
||||
|
||||
$model_ids = $request->input('model_id');
|
||||
if (!is_array($model_ids)) {
|
||||
$model_ids = array($model_ids);
|
||||
}
|
||||
$assets = $assets->InModelList($model_ids);
|
||||
}
|
||||
|
||||
$assets = $assets->get();
|
||||
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
@@ -576,7 +644,10 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function emailAssetList(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
$this->authorize('update', $user);
|
||||
|
||||
if (empty($user->email)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.inventorynotification.error')));
|
||||
@@ -600,6 +671,7 @@ class UsersController extends Controller
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('view', Consumable::class);
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('update', $user);
|
||||
$consumables = $user->consumables;
|
||||
return (new ConsumablesTransformer)->transformConsumables($consumables, $consumables->count(), $request);
|
||||
}
|
||||
@@ -616,6 +688,7 @@ class UsersController extends Controller
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('view', $user);
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessories = $user->accessories;
|
||||
|
||||
@@ -636,6 +709,7 @@ class UsersController extends Controller
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
if ($user = User::where('id', $id)->withTrashed()->first()) {
|
||||
$this->authorize('update', $user);
|
||||
$licenses = $user->licenses()->get();
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
@@ -659,9 +733,20 @@ class UsersController extends Controller
|
||||
if ($request->filled('id')) {
|
||||
try {
|
||||
$user = User::find($request->get('id'));
|
||||
$this->authorize('update', $user);
|
||||
$user->two_factor_secret = null;
|
||||
$user->two_factor_enrolled = 0;
|
||||
$user->save();
|
||||
$user->saveQuietly();
|
||||
|
||||
// Log the reset
|
||||
$logaction = new Actionlog();
|
||||
$logaction->target_type = User::class;
|
||||
$logaction->target_id = $user->id;
|
||||
$logaction->item_type = User::class;
|
||||
$logaction->item_id = $user->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('2FA reset');
|
||||
|
||||
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetMaintenance;
|
||||
use App\Models\Company;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Slack;
|
||||
|
||||
@@ -7,16 +7,17 @@ use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\User;
|
||||
use App\Models\DefaultValuesForCustomFields;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Redirect;
|
||||
use Request;
|
||||
use Storage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to asset models for
|
||||
@@ -66,7 +67,7 @@ class AssetModelsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param ImageUploadRequest $request
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
@@ -139,7 +140,7 @@ class AssetModelsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @param ImageUploadRequest $request
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $modelId = null)
|
||||
@@ -163,7 +164,7 @@ class AssetModelsController extends Controller
|
||||
$model->notes = $request->input('notes');
|
||||
$model->requestable = $request->input('requestable', '0');
|
||||
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
DefaultValuesForCustomFields::forPivot($model, Asset::class)->delete();
|
||||
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
|
||||
@@ -200,7 +201,7 @@ class AssetModelsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function destroy($modelId)
|
||||
@@ -220,7 +221,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('models/'.$model->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +238,7 @@ class AssetModelsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $id
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function getRestore($id)
|
||||
@@ -429,7 +430,7 @@ class AssetModelsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postBulkDelete(Request $request)
|
||||
{
|
||||
@@ -480,17 +481,17 @@ class AssetModelsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default values to a model (as long as they are truthy)
|
||||
* Adds default values to a model (as long as they are truthy) (does this mean I cannot set a default value of 0?)
|
||||
*
|
||||
* @param AssetModel $model
|
||||
* @param array $defaultValues
|
||||
* @return void
|
||||
*/
|
||||
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues)
|
||||
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues): bool
|
||||
{
|
||||
$data = array();
|
||||
foreach ($defaultValues as $customFieldId => $defaultValue) {
|
||||
$customField = \App\Models\CustomField::find($customFieldId);
|
||||
$customField = CustomField::find($customFieldId);
|
||||
|
||||
$data[$customField->db_column] = $defaultValue;
|
||||
}
|
||||
@@ -515,22 +516,12 @@ class AssetModelsController extends Controller
|
||||
}
|
||||
|
||||
foreach ($defaultValues as $customFieldId => $defaultValue) {
|
||||
if(is_array($defaultValue)){
|
||||
$model->defaultValues()->attach($customFieldId, ['default_value' => implode(', ', $defaultValue)]);
|
||||
}elseif ($defaultValue) {
|
||||
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
|
||||
if (is_array($defaultValue)) {
|
||||
$defaultValue = implode(', ', $defaultValue);
|
||||
}
|
||||
DefaultValuesForCustomFields::updateOrCreate(['custom_field_id' => $customFieldId, 'item_pivot_id' => $model->id], ['default_value' => $defaultValue]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all default values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function removeCustomFieldsDefaultValues(AssetModel $model)
|
||||
{
|
||||
$model->defaultValues()->detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class AssetModelsFilesController extends Controller
|
||||
*
|
||||
* @param UploadFileRequest $request
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*@since [v1.0]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -38,7 +38,7 @@ class AssetModelsFilesController extends Controller
|
||||
|
||||
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$model->id,$file);
|
||||
|
||||
$model->logUpload($file_name, e($request->get('notes')));
|
||||
$model->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('general.file_upload_success'));
|
||||
@@ -70,8 +70,6 @@ class AssetModelsFilesController extends Controller
|
||||
}
|
||||
|
||||
$file = 'private_uploads/assetmodels/'.$log->filename;
|
||||
\Log::debug('Checking for '.$file);
|
||||
|
||||
|
||||
if (! Storage::exists($file)) {
|
||||
return response('File '.$file.' not found on server', 404)
|
||||
|
||||
@@ -12,8 +12,9 @@ use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AssetCheckinController extends Controller
|
||||
{
|
||||
@@ -39,7 +40,17 @@ class AssetCheckinController extends Controller
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto);
|
||||
// This asset is already checked in, redirect
|
||||
|
||||
if (is_null($asset->assignedTo)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
|
||||
}
|
||||
|
||||
if (!$asset->model) {
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto)->with('table_name', 'Assets');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +60,7 @@ class AssetCheckinController extends Controller
|
||||
* @param AssetCheckinRequest $request
|
||||
* @param int $assetId
|
||||
* @param null $backto
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v1.0]
|
||||
*/
|
||||
@@ -64,6 +75,11 @@ class AssetCheckinController extends Controller
|
||||
if (is_null($target = $asset->assignedTo)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
|
||||
}
|
||||
|
||||
if (!$asset->model) {
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
if ($asset->assignedType() == Asset::USER) {
|
||||
@@ -86,7 +102,7 @@ class AssetCheckinController extends Controller
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
\Log::debug('NEW Location ID: '.$request->get('location_id'));
|
||||
Log::debug('NEW Location ID: '.$request->get('location_id'));
|
||||
$asset->location_id = $request->get('location_id');
|
||||
|
||||
if ($request->get('update_default_location') == 0){
|
||||
@@ -116,15 +132,12 @@ class AssetCheckinController extends Controller
|
||||
$acceptance->delete();
|
||||
});
|
||||
|
||||
Session::put('redirect_option', $request->get('redirect_option'));
|
||||
// Was the asset updated?
|
||||
if ($asset->save()) {
|
||||
|
||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
if ((isset($user)) && ($backto == 'user')) {
|
||||
return redirect()->route('users.show', $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
return Helper::getRedirectOption($asset, $assetId, 'Assets');
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class AssetCheckoutController extends Controller
|
||||
{
|
||||
@@ -33,11 +34,17 @@ class AssetCheckoutController extends Controller
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if (!$asset->model) {
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'))
|
||||
->with('statusLabel_list', Helper::deployableStatusLabelList());
|
||||
->with('statusLabel_list', Helper::deployableStatusLabelList())
|
||||
->with('table_name', 'Assets');
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ class AssetCheckoutController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckoutRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetCheckoutRequest $request, $assetId)
|
||||
@@ -60,9 +67,14 @@ class AssetCheckoutController extends Controller
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
}
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if (!$asset->model) {
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
$admin = Auth::user();
|
||||
|
||||
$target = $this->determineCheckoutTarget($asset);
|
||||
$target = $this->determineCheckoutTarget();
|
||||
|
||||
$asset = $this->updateAssetLocation($asset, $target);
|
||||
|
||||
@@ -97,11 +109,12 @@ class AssetCheckoutController extends Controller
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('general.error_user_company'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
Session::put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return Helper::getRedirectOption($request, $assetId, 'Assets');
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
|
||||
} catch (ModelNotFoundException $e) {
|
||||
|
||||
@@ -17,7 +17,7 @@ class AssetFilesController extends Controller
|
||||
*
|
||||
* @param UploadFileRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*@since [v1.0]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -38,7 +38,7 @@ class AssetFilesController extends Controller
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
|
||||
|
||||
$asset->logUpload($file_name, e($request->get('notes')));
|
||||
$asset->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
@@ -70,7 +70,6 @@ class AssetFilesController extends Controller
|
||||
}
|
||||
|
||||
$file = 'private_uploads/assets/'.$log->filename;
|
||||
\Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
@@ -96,12 +96,16 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize(Asset::class);
|
||||
|
||||
// There are a lot more rules to add here but prevents
|
||||
// errors around `asset_tags` not being present below.
|
||||
$this->validate($request, ['asset_tags' => ['required', 'array']]);
|
||||
|
||||
// Handle asset tags - there could be one, or potentially many.
|
||||
// This is only necessary on create, not update, since bulk editing is handled
|
||||
// differently
|
||||
@@ -156,29 +160,7 @@ class AssetsController extends Controller
|
||||
$asset = $request->handleImages($asset);
|
||||
}
|
||||
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
$model = AssetModel::find($request->get('model_id'));
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$asset->customFill($request, Auth::user()); // Update custom fields in the database.
|
||||
|
||||
// Validate the asset before saving
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
@@ -203,7 +185,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
\Log::debug(e($asset->asset_tag));
|
||||
Log::debug(e($asset->asset_tag));
|
||||
return redirect()->route('hardware.index')
|
||||
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => e($asset->asset_tag)]));
|
||||
|
||||
@@ -289,7 +271,7 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @param int $assetId
|
||||
* @return \Illuminate\Http\RedirectResponse|Redirect
|
||||
*@since [v1.0]
|
||||
* @since [v1.0]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $assetId = null)
|
||||
@@ -304,7 +286,8 @@ class AssetsController extends Controller
|
||||
$asset->status_id = $request->input('status_id', null);
|
||||
$asset->warranty_months = $request->input('warranty_months', null);
|
||||
$asset->purchase_cost = $request->input('purchase_cost', null);
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->next_audit_date = $request->input('next_audit_date', null);
|
||||
if ($request->filled('purchase_date') && !$request->filled('asset_eol_date') && ($asset->model->eol > 0)) {
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->asset_eol_date = Carbon::parse($request->input('purchase_date'))->addMonths($asset->model->eol)->format('Y-m-d');
|
||||
@@ -406,7 +389,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($assetId)
|
||||
{
|
||||
@@ -440,7 +423,7 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function getAssetBySerial(Request $request)
|
||||
{
|
||||
@@ -458,7 +441,7 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function getAssetByTag(Request $request, $tag=null)
|
||||
{
|
||||
@@ -850,15 +833,15 @@ class AssetsController extends Controller
|
||||
return view('hardware/audit-due');
|
||||
}
|
||||
|
||||
public function overdueForAudit()
|
||||
public function dueForCheckin()
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
$this->authorize('checkin', Asset::class);
|
||||
|
||||
return view('hardware/audit-overdue');
|
||||
return view('hardware/checkin-due');
|
||||
}
|
||||
|
||||
|
||||
public function auditStore(Request $request, $id)
|
||||
public function auditStore(UploadFileRequest $request, $id)
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
||||
@@ -875,7 +858,21 @@ class AssetsController extends Controller
|
||||
|
||||
$asset = Asset::findOrFail($id);
|
||||
|
||||
// We don't want to log this as a normal update, so let's bypass that
|
||||
/**
|
||||
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
||||
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
||||
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
||||
* the audit log entry we're creating through this controller.
|
||||
*
|
||||
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
||||
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
||||
* will bypass normal model-level validation that's usually handled at the observer )
|
||||
*
|
||||
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||
*
|
||||
* @see \App\Observers\AssetObserver::updating()
|
||||
*/
|
||||
$asset->unsetEventDispatcher();
|
||||
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
@@ -884,29 +881,27 @@ class AssetsController extends Controller
|
||||
// Check to see if they checked the box to update the physical location,
|
||||
// not just note it in the audit notes
|
||||
if ($request->input('update_location') == '1') {
|
||||
Log::debug('update location in audit');
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
$file_name = '';
|
||||
// Upload an image, if attached
|
||||
if ($request->hasFile('image')) {
|
||||
$path = 'private_uploads/audits';
|
||||
if (! Storage::exists($path)) {
|
||||
Storage::makeDirectory($path, 775);
|
||||
}
|
||||
$upload = $image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = 'audit-'.str_random(18).'.'.$ext;
|
||||
Storage::putFileAs($path, $upload, $file_name);
|
||||
}
|
||||
/**
|
||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||
* We have to invoke this manually because of the unsetEventDispatcher() above.)
|
||||
*/
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
|
||||
$file_name = null;
|
||||
// Create the image (if one was chosen.)
|
||||
if ($request->hasFile('image')) {
|
||||
$file_name = $request->handleFile('private_uploads/audits/', 'audit-'.$asset->id, $request->file('image'));
|
||||
}
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
|
||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
public function getRequestedIndex($user_id = null)
|
||||
|
||||
@@ -13,7 +13,9 @@ use App\Models\Setting;
|
||||
use App\View\Label;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
@@ -189,7 +191,6 @@ class BulkAssetsController extends Controller
|
||||
* Save bulk edits
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
@@ -214,7 +215,7 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
$assets = Asset::whereIn('id', array_keys($request->input('ids')))->get();
|
||||
$assets = Asset::whereIn('id', $request->input('ids'))->get();
|
||||
|
||||
|
||||
|
||||
@@ -379,28 +380,30 @@ class BulkAssetsController extends Controller
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
if (Gate::allows('admin')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
|
||||
/*
|
||||
* Check if the decrypted existing value is different from one we just submitted
|
||||
* and if not, pull it out of the object since it shouldn't really be updating at all.
|
||||
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
|
||||
* different times will have different values, so it will *look* like it was updated
|
||||
* but it wasn't.
|
||||
*/
|
||||
if ($decrypted_old != $this->update_array[$field->db_column]) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
|
||||
} else {
|
||||
/*
|
||||
* Remove the encrypted custom field from the update_array, since nothing changed
|
||||
* Check if the decrypted existing value is different from one we just submitted
|
||||
* and if not, pull it out of the object since it shouldn't really be updating at all.
|
||||
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
|
||||
* different times will have different values, so it will *look* like it was updated
|
||||
* but it wasn't.
|
||||
*/
|
||||
unset($this->update_array[$field->db_column]);
|
||||
unset($asset->{$field->db_column});
|
||||
}
|
||||
if ($decrypted_old != $this->update_array[$field->db_column]) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($this->update_array[$field->db_column]);
|
||||
} else {
|
||||
/*
|
||||
* Remove the encrypted custom field from the update_array, since nothing changed
|
||||
*/
|
||||
unset($this->update_array[$field->db_column]);
|
||||
unset($asset->{$field->db_column});
|
||||
}
|
||||
|
||||
/*
|
||||
* These custom fields aren't encrypted, just carry on as usual
|
||||
*/
|
||||
/*
|
||||
* These custom fields aren't encrypted, just carry on as usual
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Auth;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
@@ -79,16 +79,16 @@ class ForgotPasswordController extends Controller
|
||||
)
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
\Log::info('Password reset attempt: User '.$request->input('username').'failed with exception: '.$e );
|
||||
Log::info('Password reset attempt: User '.$request->input('username').'failed with exception: '.$e );
|
||||
}
|
||||
|
||||
// Prevent timing attack to enumerate users.
|
||||
usleep(500000 + random_int(0, 1500000));
|
||||
|
||||
if ($response === \Password::RESET_LINK_SENT) {
|
||||
\Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent');
|
||||
Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent');
|
||||
} else {
|
||||
\Log::info('Password reset attempt: User matching username '.$request->input('username').' NOT FOUND or user is inactive');
|
||||
Log::info('Password reset attempt: User matching username '.$request->input('username').' NOT FOUND or user is inactive');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Log;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Redirect;
|
||||
|
||||
/**
|
||||
@@ -122,7 +122,7 @@ class LoginController extends Controller
|
||||
Auth::login($user);
|
||||
} else {
|
||||
$username = $saml->getUsername();
|
||||
\Log::debug("SAML user '$username' could not be found in database.");
|
||||
Log::debug("SAML user '$username' could not be found in database.");
|
||||
$request->session()->flash('error', trans('auth/message.signin.error'));
|
||||
$saml->clearData();
|
||||
}
|
||||
@@ -137,7 +137,7 @@ class LoginController extends Controller
|
||||
$s->save();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('There was an error authenticating the SAML user: '.$e->getMessage());
|
||||
Log::debug('There was an error authenticating the SAML user: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ class LoginController extends Controller
|
||||
|
||||
// Better logging
|
||||
if (empty($samlData)) {
|
||||
\Log::debug("SAML page requested, but samlData seems empty.");
|
||||
Log::debug("SAML page requested, but samlData seems empty.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,19 +261,19 @@ class LoginController extends Controller
|
||||
/**
|
||||
* Account sign in form processing.
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
|
||||
//If the environment is set to ALWAYS require SAML, return access denied
|
||||
if (config('app.require_saml')) {
|
||||
\Log::debug('require SAML is enabled in the .env - return a 403');
|
||||
Log::debug('require SAML is enabled in the .env - return a 403');
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->login_common_disabled == '1') {
|
||||
\Log::debug('login_common_disabled is set to 1 - return a 403');
|
||||
Log::debug('login_common_disabled is set to 1 - return a 403');
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ class LoginController extends Controller
|
||||
/**
|
||||
* Two factor enrollment page
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function getTwoFactorEnroll()
|
||||
{
|
||||
@@ -389,7 +389,7 @@ class LoginController extends Controller
|
||||
/**
|
||||
* Two factor code form page
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function getTwoFactorAuth()
|
||||
{
|
||||
@@ -415,7 +415,7 @@ class LoginController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postTwoFactorAuth(Request $request)
|
||||
{
|
||||
@@ -451,7 +451,7 @@ class LoginController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
* @return Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
@@ -66,7 +66,7 @@ class ResetPasswordController extends Controller
|
||||
$credentials = $request->only('email', 'token');
|
||||
|
||||
if (is_null($this->broker()->getUser($credentials))) {
|
||||
\Log::debug('Password reset form FAILED - this token is not valid.');
|
||||
Log::debug('Password reset form FAILED - this token is not valid.');
|
||||
return redirect()->route('password.request')->with('error', trans('passwords.token'));
|
||||
}
|
||||
|
||||
@@ -89,10 +89,10 @@ class ResetPasswordController extends Controller
|
||||
|
||||
$request->validate($this->rules(), $request->all(), $this->validationErrorMessages());
|
||||
|
||||
\Log::debug('Checking if '.$request->input('username').' exists');
|
||||
Log::debug('Checking if '.$request->input('username').' exists');
|
||||
// Check to see if the user even exists - we'll treat the response the same to prevent user sniffing
|
||||
if ($user = User::where('username', '=', $request->input('username'))->where('activated', '1')->whereNotNull('email')->first()) {
|
||||
\Log::debug($user->username.' exists');
|
||||
Log::debug($user->username.' exists');
|
||||
|
||||
|
||||
// handle the password validation rules set by the admin settings
|
||||
@@ -112,17 +112,17 @@ class ResetPasswordController extends Controller
|
||||
|
||||
// Check if the password reset above actually worked
|
||||
if ($response == \Password::PASSWORD_RESET) {
|
||||
\Log::debug('Password reset for '.$user->username.' worked');
|
||||
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');
|
||||
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 '.$request->input('username').' FAILED - user does not exist or does not have an email address - but make it look like it succeeded');
|
||||
Log::debug('Password reset for '.$request->input('username').' FAILED - user does not exist or does not have an email address - but make it look like it succeeded');
|
||||
return redirect()->guest('login')->with('success', trans('passwords.reset'));
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Auth;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Saml;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This controller provides the endpoint for SAML communication and metadata.
|
||||
@@ -51,7 +51,7 @@ class SamlController extends Controller
|
||||
$metadata = $this->saml->getSPMetadata();
|
||||
|
||||
if (empty($metadata)) {
|
||||
\Log::debug('SAML metadata is empty - return a 403');
|
||||
Log::debug('SAML metadata is empty - return a 403');
|
||||
return response()->view('errors.403', [], 403);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class SamlController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
@@ -93,7 +93,7 @@ class SamlController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function acs(Request $request)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ class SamlController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function sls(Request $request)
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@ class BulkAssetModelsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Category as Category;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Str;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Companies for
|
||||
@@ -154,7 +155,7 @@ final class CompaniesController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('companies'.'/'.$company->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to Components for
|
||||
@@ -188,7 +189,7 @@ class ComponentsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('components/'.$component->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Component;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ComponentsFilesController extends Controller
|
||||
{
|
||||
@@ -84,7 +85,7 @@ class ComponentsFilesController extends Controller
|
||||
try {
|
||||
Storage::delete('components/'.$log->filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ class ComponentsFilesController extends Controller
|
||||
*/
|
||||
public function show($componentId = null, $fileId = null)
|
||||
{
|
||||
\Log::debug('Private filesystem is: '.config('filesystems.default'));
|
||||
Log::debug('Private filesystem is: '.config('filesystems.default'));
|
||||
$component = Component::find($componentId);
|
||||
|
||||
// the component is valid
|
||||
@@ -126,8 +127,8 @@ class ComponentsFilesController extends Controller
|
||||
$file = 'private_uploads/components/'.$log->filename;
|
||||
|
||||
if (Storage::missing($file)) {
|
||||
\Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
\Log::debug('URL should be '.Storage::url($file));
|
||||
Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
Log::debug('URL should be '.Storage::url($file));
|
||||
|
||||
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
|
||||
@@ -71,8 +71,14 @@ class ConsumableCheckoutController extends Controller
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
|
||||
// If the quantity is not present in the request or is not a positive integer, set it to 1
|
||||
$quantity = $request->input('qty');
|
||||
if (!isset($quantity) || !ctype_digit((string)$quantity) || $quantity <= 0) {
|
||||
$quantity = 1;
|
||||
}
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($consumable->numRemaining() <= 0) {
|
||||
if ($consumable->numRemaining() <= 0 || $quantity > $consumable->numRemaining()) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
|
||||
}
|
||||
|
||||
@@ -88,13 +94,14 @@ class ConsumableCheckoutController extends Controller
|
||||
// Update the consumable data
|
||||
$consumable->assigned_to = e($request->input('assigned_to'));
|
||||
|
||||
for($i = 0; $i < $quantity; $i++){
|
||||
$consumable->users()->attach($consumable->id, [
|
||||
'consumable_id' => $consumable->id,
|
||||
'user_id' => $admin_user->id,
|
||||
'assigned_to' => e($request->input('assigned_to')),
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
}
|
||||
event(new CheckoutableCheckedOut($consumable, $user, Auth::user(), $request->input('note')));
|
||||
|
||||
// Redirect to the new consumable page
|
||||
|
||||
@@ -10,7 +10,7 @@ use App\Models\Consumable;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Consumable\HttpFoundation\JsonResponse;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
class ConsumablesFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
@@ -83,7 +83,7 @@ class ConsumablesFilesController extends Controller
|
||||
try {
|
||||
Storage::delete('consumables/'.$log->filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ class ConsumablesFilesController extends Controller
|
||||
$file = 'private_uploads/consumables/'.$log->filename;
|
||||
|
||||
if (Storage::missing($file)) {
|
||||
\Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
\Log::debug('URL should be '.Storage::url($file));
|
||||
Log::debug('FILE DOES NOT EXISTS for '.$file);
|
||||
Log::debug('URL should be '.Storage::url($file));
|
||||
|
||||
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
|
||||
@@ -6,9 +6,10 @@ use App\Helpers\Helper;
|
||||
use App\Http\Requests\CustomFieldRequest;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\CustomFieldset;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Redirect;
|
||||
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Custom Asset Fields for
|
||||
@@ -21,6 +22,7 @@ use Redirect;
|
||||
*/
|
||||
class CustomFieldsController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a view with a listing of custom fields.
|
||||
*
|
||||
@@ -29,12 +31,16 @@ class CustomFieldsController extends Controller
|
||||
* @return \Illuminate\Support\Facades\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', CustomField::class);
|
||||
if ($request->input('tab') == 1) {
|
||||
// Users section, make sure to auto-create the first fieldset if so
|
||||
CustomFieldset::firstOrCreate(['type' => Helper::$itemtypes_having_custom_fields[1]], ['name' => 'default']);
|
||||
}
|
||||
|
||||
$fieldsets = CustomFieldset::with('fields', 'models')->get();
|
||||
$fields = CustomField::with('fieldset')->get();
|
||||
$fieldsets = CustomFieldset::with('fields')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab', 0)])->get(); //cannot eager-load 'customizable' because it's not a relation
|
||||
$fields = CustomField::with('fieldset')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab', 0)])->get();
|
||||
|
||||
return view('custom_fields.index')->with('custom_fieldsets', $fieldsets)->with('custom_fields', $fields);
|
||||
}
|
||||
@@ -67,7 +73,7 @@ class CustomFieldsController extends Controller
|
||||
public function create(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
$fieldsets = CustomFieldset::get();
|
||||
$fieldsets = CustomFieldset::where('type', Helper::$itemtypes_having_custom_fields[$request->get('tab')])->get();
|
||||
|
||||
return view('custom_fields.fields.edit', [
|
||||
'predefinedFormats' => Helper::predefined_formats(),
|
||||
@@ -111,8 +117,10 @@ 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()
|
||||
"user_id" => Auth::id(),
|
||||
]);
|
||||
// not mass-assignable; must be manual
|
||||
$field->type = Helper::$itemtypes_having_custom_fields[$request->get('tab')];
|
||||
|
||||
|
||||
if ($request->filled('custom_format')) {
|
||||
@@ -125,14 +133,17 @@ class CustomFieldsController extends Controller
|
||||
|
||||
// Sync fields with fieldsets
|
||||
$fieldset_array = $request->input('associate_fieldsets');
|
||||
if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) {
|
||||
if ($request->get('tab') == 1) {
|
||||
$fieldset_array = [CustomFieldset::firstOrCreate(['type' => User::class], ['name' => 'default'])->id => true];
|
||||
}
|
||||
if (($request->has('associate_fieldsets') || $request->get('tab') == 1) && (is_array($fieldset_array))) {
|
||||
$field->fieldset()->sync(array_keys($fieldset_array));
|
||||
} else {
|
||||
$field->fieldset()->sync([]);
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.create.success'));
|
||||
return redirect()->route('fields.index', ['tab' => $request->get('tab', 0)])->with('success', trans('admin/custom_fields/message.field.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('selected_fieldsets', $request->input('associate_fieldsets'))->withInput()
|
||||
@@ -185,12 +196,16 @@ class CustomFieldsController extends Controller
|
||||
if ($field = CustomField::find($field_id)) {
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->type == User::class) {
|
||||
$field->fieldset()->detach(); // remove from 'default' group (and others, if they exist in the future!)
|
||||
}
|
||||
if (($field->fieldset) && ($field->fieldset->count() > 0)) {
|
||||
return redirect()->back()->withErrors(['message' => 'Field is in-use']);
|
||||
}
|
||||
$type = $field->type;
|
||||
$field->delete();
|
||||
return redirect()->route("fields.index")
|
||||
->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
return redirect()->route('fields.index', ['tab' => array_search($type, Helper::$itemtypes_having_custom_fields)])
|
||||
->with('success', trans('admin/custom_fields/message.field.delete.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withErrors(['message' => 'Field does not exist']);
|
||||
@@ -260,7 +275,7 @@ class CustomFieldsController extends Controller
|
||||
|
||||
$field->name = trim(e($request->get("name")));
|
||||
$field->element = e($request->get("element"));
|
||||
$field->field_values = e($request->get("field_values"));
|
||||
$field->field_values = $request->get("field_values");
|
||||
$field->user_id = Auth::id();
|
||||
$field->help_text = $request->get("help_text");
|
||||
$field->show_in_email = $show_in_email;
|
||||
@@ -291,7 +306,7 @@ class CustomFieldsController extends Controller
|
||||
$field->fieldset()->sync([]);
|
||||
}
|
||||
|
||||
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.update.success'));
|
||||
return redirect()->route('fields.index', ['tab' => $request->get('tab', 0)])->with('success', trans('admin/custom_fields/message.field.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\CustomFieldset;
|
||||
@@ -38,7 +39,7 @@ class CustomFieldsetsController extends Controller
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function show($id)
|
||||
public function show( $id)
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')
|
||||
->where('id', '=', $id)->orderBy('id', 'ASC')->first();
|
||||
@@ -46,7 +47,7 @@ class CustomFieldsetsController extends Controller
|
||||
$this->authorize('view', $cfset);
|
||||
|
||||
if ($cfset) {
|
||||
$custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::pluck('name', 'id')->toArray();
|
||||
$custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::where('type', $cfset->type)->pluck('name', 'id')->toArray();
|
||||
|
||||
$maxid = 0;
|
||||
foreach ($cfset->fields as $field) {
|
||||
@@ -86,7 +87,7 @@ class CustomFieldsetsController extends Controller
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param Request $request
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(Request $request)
|
||||
@@ -96,6 +97,8 @@ class CustomFieldsetsController extends Controller
|
||||
$fieldset = new CustomFieldset([
|
||||
'name' => $request->get('name'),
|
||||
'user_id' => Auth::user()->id,
|
||||
'type' => Helper::$itemtypes_having_custom_fields[$request->get('tab')]
|
||||
// 'sub' =>
|
||||
]);
|
||||
|
||||
$validator = Validator::make($request->all(), $fieldset->rules);
|
||||
@@ -126,7 +129,7 @@ class CustomFieldsetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v6.0.14]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function edit($id)
|
||||
@@ -147,7 +150,7 @@ class CustomFieldsetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v6.0.14]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Department;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DepartmentsController extends Controller
|
||||
{
|
||||
@@ -129,7 +130,7 @@ class DepartmentsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('departments'.'/'.$department->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
$department->delete();
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Laravel\Socialite\Two\InvalidStateException;
|
||||
use App\Models\Setting;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class GoogleAuthController extends Controller
|
||||
{
|
||||
@@ -34,9 +34,9 @@ class GoogleAuthController extends Controller
|
||||
{
|
||||
try {
|
||||
$socialUser = Socialite::driver('google')->user();
|
||||
\Log::debug('Google user found in Google Workspace');
|
||||
Log::debug('Google user found in Google Workspace');
|
||||
} catch (InvalidStateException $exception) {
|
||||
\Log::debug('Google user NOT found in Google Workspace');
|
||||
Log::debug('Google user NOT found in Google Workspace');
|
||||
return redirect()->route('login')
|
||||
->withErrors(
|
||||
[
|
||||
@@ -52,7 +52,7 @@ class GoogleAuthController extends Controller
|
||||
|
||||
|
||||
if ($user) {
|
||||
\Log::debug('Google user '.$socialUser->getEmail().' found in Snipe-IT');
|
||||
Log::debug('Google user '.$socialUser->getEmail().' found in Snipe-IT');
|
||||
$user->update([
|
||||
'avatar' => $socialUser->avatar,
|
||||
]);
|
||||
@@ -61,7 +61,7 @@ class GoogleAuthController extends Controller
|
||||
return redirect()->route('home');
|
||||
}
|
||||
|
||||
\Log::debug('Google user '.$socialUser->getEmail().' NOT found in Snipe-IT');
|
||||
Log::debug('Google user '.$socialUser->getEmail().' NOT found in Snipe-IT');
|
||||
return redirect()->route('login')
|
||||
->withErrors(
|
||||
[
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Http\Controllers;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Group;
|
||||
use Illuminate\Http\Request;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Groups for
|
||||
|
||||
@@ -48,7 +48,7 @@ class CheckoutKitController extends Controller
|
||||
* Validate and process the new Predefined Kit data.
|
||||
*
|
||||
* @author [D. Minaev.] [<dmitriy.minaev.v@gmail.com>]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request, $kit_id)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ class PredefinedKitsController extends Controller
|
||||
* Validate and process the new Predefined Kit data.
|
||||
*
|
||||
* @author [D. Minaev] [<dmitriy.minaev.v@gmail.com>]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
@@ -95,7 +95,7 @@ class PredefinedKitsController extends Controller
|
||||
* @author [D. Minaev] [<dmitriy.minaev.v@gmail.com>]
|
||||
* @since [v1.0]
|
||||
* @param int $kit_id
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $kit_id = null)
|
||||
{
|
||||
@@ -122,7 +122,7 @@ class PredefinedKitsController extends Controller
|
||||
* @author [D. Minaev] [<dmitriy.minaev.v@gmail.com>]
|
||||
* @since [v1.0]
|
||||
* @param int $kit_id
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($kit_id)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Models\Setting;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use App\View\Label as LabelView;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class LabelsController extends Controller
|
||||
@@ -21,9 +22,9 @@ class LabelsController extends Controller
|
||||
/**
|
||||
* Returns the Label view with test data
|
||||
*
|
||||
* @author Grant Le Roux <grant.leroux+snipe-it@gmail.com>
|
||||
* @param string $labelName
|
||||
* @param string $labelName
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @author Grant Le Roux <grant.leroux+snipe-it@gmail.com>
|
||||
*/
|
||||
public function show(string $labelName)
|
||||
{
|
||||
@@ -66,16 +67,18 @@ class LabelsController extends Controller
|
||||
$exampleAsset->model->category->id = 999999;
|
||||
$exampleAsset->model->category->name = trans('admin/labels/table.example_category');
|
||||
|
||||
$customFieldColumns = CustomField::all()->pluck('db_column');
|
||||
$customFieldColumns = CustomField::where('field_encrypted', '=', 0)->pluck('db_column');
|
||||
|
||||
collect(explode(';', Setting::getSettings()->label2_fields))
|
||||
->filter()
|
||||
->each(function ($item) use ($customFieldColumns, $exampleAsset) {
|
||||
$pair = explode('=', $item);
|
||||
|
||||
if ($customFieldColumns->contains($pair[1])) {
|
||||
$exampleAsset->{$pair[1]} = "{{$pair[0]}}";
|
||||
}
|
||||
$pair = explode('=', $item);
|
||||
|
||||
if (array_key_exists(1, $pair)) {
|
||||
if ($customFieldColumns->contains($pair[1])) {
|
||||
$exampleAsset->{$pair[1]} = "{{$pair[0]}}";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
@@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LicenseCheckinController extends Controller
|
||||
{
|
||||
@@ -145,7 +146,7 @@ class LicenseCheckinController extends Controller
|
||||
$user_seat->assigned_to = null;
|
||||
|
||||
if ($user_seat->save()) {
|
||||
\Log::debug('Checking in '.$license->name.' from user '.$user_seat->username);
|
||||
Log::debug('Checking in '.$license->name.' from user '.$user_seat->username);
|
||||
$user_seat->logCheckin($user_seat->user, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
||||
}
|
||||
}
|
||||
@@ -160,7 +161,7 @@ class LicenseCheckinController extends Controller
|
||||
$asset_seat->asset_id = null;
|
||||
|
||||
if ($asset_seat->save()) {
|
||||
\Log::debug('Checking in '.$license->name.' from asset '.$asset_seat->asset_tag);
|
||||
Log::debug('Checking in '.$license->name.' from asset '.$asset_seat->asset_tag);
|
||||
$asset_seat->logCheckin($asset_seat->asset, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
||||
$count++;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LicenseCheckoutController extends Controller
|
||||
{
|
||||
@@ -155,16 +156,16 @@ class LicenseCheckoutController extends Controller
|
||||
|
||||
public function bulkCheckout($licenseId) {
|
||||
|
||||
\Log::debug('Checking out '.$licenseId.' via bulk');
|
||||
Log::debug('Checking out '.$licenseId.' via bulk');
|
||||
$license = License::findOrFail($licenseId);
|
||||
$this->authorize('checkin', $license);
|
||||
$avail_count = $license->getAvailSeatsCountAttribute();
|
||||
|
||||
$users = User::whereNull('deleted_at')->where('autoassign_licenses', '=', 1)->with('licenses')->get();
|
||||
\Log::debug($avail_count.' will be assigned');
|
||||
Log::debug($avail_count.' will be assigned');
|
||||
|
||||
if ($users->count() > $avail_count) {
|
||||
\Log::debug('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
|
||||
Log::debug('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
|
||||
}
|
||||
|
||||
// If the license is valid, check that there is an available seat
|
||||
@@ -179,7 +180,7 @@ class LicenseCheckoutController extends Controller
|
||||
|
||||
// Check to make sure this user doesn't already have this license checked out to them
|
||||
if ($user->licenses->where('id', '=', $licenseId)->count()) {
|
||||
\Log::debug($user->username.' already has this license checked out to them. Skipping... ');
|
||||
Log::debug($user->username.' already has this license checked out to them. Skipping... ');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ class LicenseCheckoutController extends Controller
|
||||
$avail_count--;
|
||||
$assigned_count++;
|
||||
$licenseSeat->logCheckout(trans('admin/licenses/general.bulk.checkout_all.log_msg'), $user);
|
||||
\Log::debug('License '.$license->name.' seat '.$licenseSeat->id.' checked out to '.$user->username);
|
||||
Log::debug('License '.$license->name.' seat '.$licenseSeat->id.' checked out to '.$user->username);
|
||||
}
|
||||
|
||||
if ($avail_count == 0) {
|
||||
|
||||
@@ -7,9 +7,8 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\License;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LicenseFilesController extends Controller
|
||||
{
|
||||
@@ -20,7 +19,7 @@ class LicenseFilesController extends Controller
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*@author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @todo Switch to using the AssetFileRequest form request validator.
|
||||
*/
|
||||
@@ -78,7 +77,7 @@ class LicenseFilesController extends Controller
|
||||
try {
|
||||
Storage::delete('licenses/'.$log->filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +120,8 @@ class LicenseFilesController extends Controller
|
||||
$file = 'private_uploads/licenses/'.$log->filename;
|
||||
|
||||
if (Storage::missing($file)) {
|
||||
\Log::debug('NOT EXISTS for '.$file);
|
||||
\Log::debug('NOT EXISTS URL should be '.Storage::url($file));
|
||||
Log::debug('NOT EXISTS for '.$file);
|
||||
Log::debug('NOT EXISTS URL should be '.Storage::url($file));
|
||||
|
||||
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Licenses for
|
||||
@@ -289,4 +290,106 @@ class LicensesController extends Controller
|
||||
->with('item', $license)
|
||||
->with('maintained_list', $maintained_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports Licenses to CSV
|
||||
*
|
||||
* @author [G. Martinez]
|
||||
* @since [v6.3]
|
||||
* @return StreamedResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function getExportLicensesCsv()
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
\Debugbar::disable();
|
||||
|
||||
$response = new StreamedResponse(function () {
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
$licenses= License::with('company',
|
||||
'manufacturer',
|
||||
'category',
|
||||
'supplier',
|
||||
'adminuser',
|
||||
'assignedusers')
|
||||
->orderBy('created_at', 'DESC');
|
||||
Company::scopeCompanyables($licenses)
|
||||
->chunk(500, function ($licenses) use ($handle) {
|
||||
$headers = [
|
||||
// strtolower to prevent Excel from trying to open it as a SYLK file
|
||||
strtolower(trans('general.id')),
|
||||
trans('general.company'),
|
||||
trans('general.name'),
|
||||
trans('general.serial_number'),
|
||||
trans('general.purchase_date'),
|
||||
trans('general.purchase_cost'),
|
||||
trans('general.order_number'),
|
||||
trans('general.licenses_available'),
|
||||
trans('admin/licenses/table.seats'),
|
||||
trans('general.created_by'),
|
||||
trans('general.depreciation'),
|
||||
trans('general.updated_at'),
|
||||
trans('admin/licenses/table.deleted_at'),
|
||||
trans('general.email'),
|
||||
trans('admin/hardware/form.fully_depreciated'),
|
||||
trans('general.supplier'),
|
||||
trans('admin/licenses/form.expiration'),
|
||||
trans('admin/licenses/form.purchase_order'),
|
||||
trans('admin/licenses/form.termination_date'),
|
||||
trans('admin/licenses/form.maintained'),
|
||||
trans('general.manufacturer'),
|
||||
trans('general.category'),
|
||||
trans('general.min_amt'),
|
||||
trans('admin/licenses/form.reassignable'),
|
||||
trans('general.notes'),
|
||||
trans('general.created_at'),
|
||||
];
|
||||
|
||||
fputcsv($handle, $headers);
|
||||
|
||||
foreach ($licenses as $license) {
|
||||
// Add a new row with data
|
||||
$values = [
|
||||
$license->id,
|
||||
$license->company ? $license->company->name: '',
|
||||
$license->name,
|
||||
$license->serial,
|
||||
$license->purchase_date,
|
||||
$license->purchase_cost,
|
||||
$license->order_number,
|
||||
$license->free_seat_count,
|
||||
$license->seats,
|
||||
($license->adminuser ? $license->adminuser->present()->fullName() : trans('admin/reports/general.deleted_user')),
|
||||
$license->depreciation ? $license->depreciation->name: '',
|
||||
$license->updated_at,
|
||||
$license->deleted_at,
|
||||
$license->email,
|
||||
( $license->depreciate == '1') ? trans('general.yes') : trans('general.no'),
|
||||
($license->supplier) ? $license->supplier->name: '',
|
||||
$license->expiration_date,
|
||||
$license->purchase_order,
|
||||
$license->termination_date,
|
||||
( $license->maintained == '1') ? trans('general.yes') : trans('general.no'),
|
||||
$license->manufacturer ? $license->manufacturer->name: '',
|
||||
$license->category ? $license->category->name: '',
|
||||
$license->min_amt,
|
||||
( $license->reassignable == '1') ? trans('general.yes') : trans('general.no'),
|
||||
$license->notes,
|
||||
$license->created_at,
|
||||
];
|
||||
|
||||
fputcsv($handle, $values);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the output stream
|
||||
fclose($handle);
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/csv; charset=UTF-8',
|
||||
'Content-Disposition' => 'attachment; filename="licenses-'.date('Y-m-d-his').'.csv"',
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Locations for
|
||||
@@ -186,7 +187,7 @@ class LocationsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('locations/'.$location->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
Log::error($e);
|
||||
}
|
||||
}
|
||||
$location->delete();
|
||||
@@ -320,7 +321,12 @@ class LocationsController extends Controller
|
||||
$locations_raw_array = $request->input('ids');
|
||||
|
||||
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||
$locations = Location::whereIn('id', $locations_raw_array)->get();
|
||||
$locations = Location::whereIn('id', $locations_raw_array)
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')->get();
|
||||
|
||||
$success_count = 0;
|
||||
$error_count = 0;
|
||||
@@ -336,8 +342,8 @@ class LocationsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
\Log::debug('Success count: '.$success_count);
|
||||
\Log::debug('Error count: '.$error_count);
|
||||
Log::debug('Success count: '.$success_count);
|
||||
Log::debug('Error count: '.$error_count);
|
||||
// Complete success
|
||||
if ($success_count == count($locations_raw_array)) {
|
||||
return redirect()
|
||||
@@ -351,7 +357,7 @@ class LocationsController extends Controller
|
||||
if ($error_count > 0) {
|
||||
return redirect()
|
||||
->route('locations.index')
|
||||
->with('warning', trans('general.bulk.partial_success',
|
||||
->with('warning', trans('general.bulk.delete.partial',
|
||||
['success' => $success_count, 'error' => $error_count, 'object_type' => trans('general.locations')]
|
||||
));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Redirect;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Manufacturers for
|
||||
@@ -174,7 +175,7 @@ class ManufacturersController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +220,7 @@ class ManufacturersController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.1.15]
|
||||
* @param int $manufacturers_id
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function restore($id)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CurrentInventory;
|
||||
@@ -11,10 +10,6 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Image;
|
||||
use Redirect;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Profiles for
|
||||
@@ -72,7 +67,7 @@ class ProfileController extends Controller
|
||||
|
||||
|
||||
if ($user->save()) {
|
||||
return redirect()->route('profile')->with('success', 'Account successfully updated');
|
||||
return redirect()->route('profile')->with('success', trans('account.general.profile_updated'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
@@ -87,11 +82,9 @@ class ProfileController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
public function api()
|
||||
public function api(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
|
||||
// Make sure the self.api permission has been granted
|
||||
if (!Gate::allows('self.api')) {
|
||||
abort(403);
|
||||
@@ -115,7 +108,7 @@ class ProfileController extends Controller
|
||||
/**
|
||||
* Users change password form processing page.
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function passwordSave(Request $request)
|
||||
{
|
||||
|
||||
@@ -19,11 +19,11 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Input;
|
||||
use League\Csv\Reader;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use League\Csv\EscapeFormula;
|
||||
use App\Http\Requests\CustomAssetReportRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
||||
/**
|
||||
@@ -235,7 +235,7 @@ class ReportsController extends Controller
|
||||
|
||||
\Debugbar::disable();
|
||||
$response = new StreamedResponse(function () {
|
||||
\Log::debug('Starting streamed response');
|
||||
Log::debug('Starting streamed response');
|
||||
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
@@ -259,16 +259,16 @@ class ReportsController extends Controller
|
||||
|
||||
];
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Starting headers: '.$executionTime);
|
||||
Log::debug('Starting headers: '.$executionTime);
|
||||
fputcsv($handle, $header);
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Added headers: '.$executionTime);
|
||||
Log::debug('Added headers: '.$executionTime);
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target', 'location')
|
||||
->orderBy('created_at', 'DESC')
|
||||
->chunk(20, function ($actionlogs) use ($handle) {
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Walking results: '.$executionTime);
|
||||
Log::debug('Walking results: '.$executionTime);
|
||||
$count = 0;
|
||||
|
||||
foreach ($actionlogs as $actionlog) {
|
||||
@@ -312,7 +312,7 @@ class ReportsController extends Controller
|
||||
// Close the output stream
|
||||
fclose($handle);
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('-- SCRIPT COMPLETED IN '.$executionTime);
|
||||
Log::debug('-- SCRIPT COMPLETED IN '.$executionTime);
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/csv',
|
||||
'Content-Disposition' => 'attachment; filename="activity-report-'.date('Y-m-d-his').'.csv"',
|
||||
@@ -425,8 +425,8 @@ class ReportsController extends Controller
|
||||
\Debugbar::disable();
|
||||
$customfields = CustomField::get();
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
\Log::debug('Starting streamed response');
|
||||
\Log::debug('CSV escaping is set to: '.config('app.escape_formulas'));
|
||||
Log::debug('Starting streamed response');
|
||||
Log::debug('CSV escaping is set to: '.config('app.escape_formulas'));
|
||||
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
@@ -627,10 +627,10 @@ class ReportsController extends Controller
|
||||
}
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Starting headers: '.$executionTime);
|
||||
Log::debug('Starting headers: '.$executionTime);
|
||||
fputcsv($handle, $header);
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Added headers: '.$executionTime);
|
||||
Log::debug('Added headers: '.$executionTime);
|
||||
|
||||
$assets = Asset::select('assets.*')->with(
|
||||
'location', 'assetstatus', 'company', 'defaultLoc', 'assignedTo',
|
||||
@@ -696,16 +696,17 @@ class ReportsController extends Controller
|
||||
->whereBetween('action_date',[$checkout_start, $checkout_end])
|
||||
->pluck('item_id');
|
||||
|
||||
$assets->whereIn('id',$actionlogassets);
|
||||
$assets->whereIn('assets.id',$actionlogassets);
|
||||
}
|
||||
|
||||
if (($request->filled('checkin_date_start'))) {
|
||||
$assets->whereBetween('last_checkin', [
|
||||
Carbon::parse($request->input('checkin_date_start'))->startOfDay(),
|
||||
// use today's date if `checkin_date_end` is not provided
|
||||
Carbon::parse($request->input('checkin_date_end', now()))->endOfDay(),
|
||||
]);
|
||||
$checkin_start = \Carbon::parse($request->input('checkin_date_start'))->startOfDay();
|
||||
// use today's date is `checkin_date_end` is not provided
|
||||
$checkin_end = \Carbon::parse($request->input('checkin_date_end', now()))->endOfDay();
|
||||
|
||||
$assets->whereBetween('assets.last_checkin', [$checkin_start, $checkin_end ]);
|
||||
}
|
||||
//last checkin is exporting, but currently is a date and not a datetime in the custom report ONLY.
|
||||
|
||||
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
@@ -731,11 +732,11 @@ class ReportsController extends Controller
|
||||
$assets->onlyTrashed();
|
||||
}
|
||||
|
||||
\Log::debug($assets->toSql());
|
||||
Log::debug($assets->toSql());
|
||||
$assets->orderBy('assets.id', 'ASC')->chunk(20, function ($assets) use ($handle, $customfields, $request) {
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('Walking results: '.$executionTime);
|
||||
Log::debug('Walking results: '.$executionTime);
|
||||
$count = 0;
|
||||
|
||||
$formatter = new EscapeFormula("`");
|
||||
@@ -995,14 +996,14 @@ class ReportsController extends Controller
|
||||
}
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('-- Record '.$count.' Asset ID:'.$asset->id.' in '.$executionTime);
|
||||
Log::debug('-- Record '.$count.' Asset ID:'.$asset->id.' in '.$executionTime);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the output stream
|
||||
fclose($handle);
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
\Log::debug('-- SCRIPT COMPLETED IN '.$executionTime);
|
||||
Log::debug('-- SCRIPT COMPLETED IN '.$executionTime);
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/csv',
|
||||
'Content-Disposition' => 'attachment; filename="custom-assets-report-'.date('Y-m-d-his').'.csv"',
|
||||
@@ -1140,23 +1141,23 @@ class ReportsController extends Controller
|
||||
$this->authorize('reports.view');
|
||||
|
||||
if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
|
||||
\Log::debug('No pending acceptances');
|
||||
Log::debug('No pending acceptances');
|
||||
// Redirect to the unaccepted assets report page with error
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
|
||||
$assetItem = $acceptance->checkoutable;
|
||||
|
||||
\Log::debug(print_r($assetItem, true));
|
||||
Log::debug(print_r($assetItem, true));
|
||||
|
||||
if (is_null($acceptance->created_at)){
|
||||
\Log::debug('No acceptance created_at');
|
||||
Log::debug('No acceptance created_at');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
} else {
|
||||
$logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
|
||||
|
||||
if ($logItem_res->isEmpty()){
|
||||
\Log::debug('Acceptance date mismatch');
|
||||
Log::debug('Acceptance date mismatch');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$logItem = $logItem_res[0];
|
||||
|
||||
@@ -14,21 +14,21 @@ use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
use App\Notifications\FirstAdminNotification;
|
||||
use App\Notifications\MailTest;
|
||||
use Auth;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Http\Client\HttpClientException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Image;
|
||||
use Input;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Redirect;
|
||||
use Response;
|
||||
use App\Http\Requests\SlackSettingsRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Settings for
|
||||
@@ -92,22 +92,9 @@ class SettingsController extends Controller
|
||||
$start_settings['php_version_min'] = true;
|
||||
|
||||
// Curl the .env file to make sure it's not accessible via a browser
|
||||
$ch = curl_init($protocol.$host.'/.env');
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // we want headers
|
||||
curl_setopt($ch, CURLOPT_NOBODY, true); // we don't need body
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$output = curl_exec($ch);
|
||||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
$start_settings['env_exposed'] = $this->dotEnvFileIsExposed();
|
||||
|
||||
if (404 == $httpcode || 403 == $httpcode || 0 == $httpcode) {
|
||||
$start_settings['env_exposed'] = false;
|
||||
} else {
|
||||
$start_settings['env_exposed'] = true;
|
||||
}
|
||||
|
||||
if (\App::Environment('production') && (true == config('app.debug'))) {
|
||||
if (App::Environment('production') && (true == config('app.debug'))) {
|
||||
$start_settings['debug_exposed'] = true;
|
||||
} else {
|
||||
$start_settings['debug_exposed'] = false;
|
||||
@@ -158,6 +145,25 @@ class SettingsController extends Controller
|
||||
->with('section', 'Pre-Flight Check');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the .env file accessible via a browser.
|
||||
*
|
||||
* @return bool This method will return true when exceptions (such as curl exception) is thrown.
|
||||
* Check the log files to see more details about the exception.
|
||||
*/
|
||||
protected function dotEnvFileIsExposed()
|
||||
{
|
||||
try {
|
||||
return Http::timeout(10)
|
||||
->accept('*/*')
|
||||
->get(URL::to('.env'))
|
||||
->successful();
|
||||
} catch (HttpClientException $e) {
|
||||
Log::debug($e->getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the first admin user from Setup.
|
||||
*
|
||||
@@ -165,7 +171,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postSaveFirstAdmin(SetupUserRequest $request)
|
||||
{
|
||||
@@ -424,6 +430,7 @@ class SettingsController extends Controller
|
||||
// Because public demos make people act like dicks
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$request->validate(['site_name' => 'required']);
|
||||
$setting->site_name = $request->input('site_name');
|
||||
$setting->custom_css = $request->input('custom_css');
|
||||
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
|
||||
@@ -460,7 +467,6 @@ class SettingsController extends Controller
|
||||
Storage::disk('public')->delete($setting->favicon);
|
||||
$setting->favicon = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
@@ -499,6 +505,19 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function postSecurity(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'pwd_secure_complexity' => 'array',
|
||||
'pwd_secure_complexity.*' => [
|
||||
Rule::in([
|
||||
'disallow_same_pwd_as_user_fields',
|
||||
'letters',
|
||||
'numbers',
|
||||
'symbols',
|
||||
'case_diff',
|
||||
])
|
||||
]
|
||||
]);
|
||||
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
@@ -627,9 +646,9 @@ class SettingsController extends Controller
|
||||
['next_audit_date' => DB::raw('DATE_ADD(next_audit_date, INTERVAL '.$audit_diff_months.' MONTH)')]
|
||||
);
|
||||
|
||||
\Log::debug($affected .' assets affected by audit interval update');
|
||||
Log::debug($affected .' assets affected by audit interval update');
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
$alert_email = rtrim($request->input('alert_email'), ',');
|
||||
@@ -790,10 +809,9 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function getLabels()
|
||||
{
|
||||
return view('settings.labels', [
|
||||
'setting' => Setting::getSettings(),
|
||||
'customFields' => CustomField::all(),
|
||||
]);
|
||||
return view('settings.labels')
|
||||
->with('setting', Setting::getSettings())
|
||||
->with('customFields', CustomField::where('field_encrypted', '=', 0)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -956,8 +974,6 @@ class SettingsController extends Controller
|
||||
$setting->ldap_dept = $request->input('ldap_dept');
|
||||
$setting->ldap_client_tls_cert = $request->input('ldap_client_tls_cert');
|
||||
$setting->ldap_client_tls_key = $request->input('ldap_client_tls_key');
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
@@ -1101,11 +1117,9 @@ class SettingsController extends Controller
|
||||
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
|
||||
'modified_value' => $file_timestamp,
|
||||
'modified_display' => date($settings->date_display_format.' '.$settings->time_display_format, $file_timestamp),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1122,7 +1136,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v1.8]
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postBackups()
|
||||
{
|
||||
@@ -1197,9 +1211,8 @@ class SettingsController extends Controller
|
||||
Storage::delete($path . '/' . $filename);
|
||||
return redirect()->route('settings.backups.index')->with('success', trans('admin/settings/message.backup.file_deleted'));
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
} else {
|
||||
return redirect()->route('settings.backups.index')->with('error', trans('admin/settings/message.backup.file_not_found'));
|
||||
}
|
||||
@@ -1209,7 +1222,7 @@ class SettingsController extends Controller
|
||||
}
|
||||
|
||||
// Hell to the no
|
||||
\Log::warning('User ID '.Auth::user()->id.' is attempting to delete backup file '.$filename.' and is not authorized to.');
|
||||
Log::warning('User ID '.Auth::user()->id.' is attempting to delete backup file '.$filename.' and is not authorized to.');
|
||||
return redirect()->route('settings.backups.index')->with('error', trans('general.backup_delete_not_allowed'));
|
||||
}
|
||||
|
||||
@@ -1221,7 +1234,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v6.0]
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
|
||||
public function postUploadBackup(Request $request) {
|
||||
@@ -1241,20 +1254,15 @@ class SettingsController extends Controller
|
||||
$upload_filename = 'uploaded-'.date('U').'-'.Str::slug(pathinfo($request->file('file')->getClientOriginalName(), PATHINFO_FILENAME)).'.zip';
|
||||
|
||||
Storage::putFileAs('app/backups', $request->file('file'), $upload_filename);
|
||||
|
||||
|
||||
return redirect()->route('settings.backups.index')->with('success', 'File uploaded');
|
||||
}
|
||||
|
||||
return redirect()->route('settings.backups.index')->withErrors($validator);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
return redirect()->route('settings.backups.index')->with('error', trans('general.feature_disabled'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1268,7 +1276,7 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function postRestore($filename = null)
|
||||
{
|
||||
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$path = 'app/backups';
|
||||
|
||||
@@ -1284,13 +1292,13 @@ class SettingsController extends Controller
|
||||
'--force' => true,
|
||||
]);
|
||||
|
||||
\Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
|
||||
Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
|
||||
|
||||
// run the restore command
|
||||
Artisan::call('snipeit:restore',
|
||||
Artisan::call('snipeit:restore',
|
||||
[
|
||||
'--force' => true,
|
||||
'--no-progress' => true,
|
||||
'--force' => true,
|
||||
'--no-progress' => true,
|
||||
'filename' => storage_path($path).'/'.$filename
|
||||
]);
|
||||
|
||||
@@ -1298,29 +1306,28 @@ class SettingsController extends Controller
|
||||
$output = Artisan::output();
|
||||
|
||||
/* Run migrations */
|
||||
\Log::debug('Migrating database...');
|
||||
Log::debug('Migrating database...');
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
$migrate_output = Artisan::output();
|
||||
\Log::debug($migrate_output);
|
||||
Log::debug($migrate_output);
|
||||
|
||||
$find_user = DB::table('users')->where('username', $user->username)->exists();
|
||||
|
||||
|
||||
if (!$find_user){
|
||||
\Log::warning('Attempting to restore user: ' . $user->username);
|
||||
Log::warning('Attempting to restore user: ' . $user->username);
|
||||
$new_user = $user->replicate();
|
||||
$new_user->push();
|
||||
} else {
|
||||
\Log::debug('User: ' . $user->username .' already exists.');
|
||||
Log::debug('User: ' . $user->username .' already exists.');
|
||||
}
|
||||
|
||||
\Log::debug('Logging all users out..');
|
||||
Log::debug('Logging all users out..');
|
||||
Artisan::call('snipeit:global-logout', ['--force' => true]);
|
||||
|
||||
DB::table('users')->update(['remember_token' => null]);
|
||||
\Auth::logout();
|
||||
Auth::logout();
|
||||
|
||||
return redirect()->route('login')->with('success', 'Your system has been restored. Please login again.');
|
||||
|
||||
} else {
|
||||
return redirect()->route('settings.backups.index')->with('error', trans('admin/settings/message.backup.file_not_found'));
|
||||
}
|
||||
@@ -1341,14 +1348,13 @@ class SettingsController extends Controller
|
||||
public function getPurge()
|
||||
{
|
||||
|
||||
\Log::warning('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting a PURGE');
|
||||
Log::warning('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting a PURGE');
|
||||
|
||||
if (config('app.allow_purge')=='true') {
|
||||
return view('settings.purge-form');
|
||||
}
|
||||
|
||||
return redirect()->route('settings.index')->with('error', trans('general.purge_not_allowed'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1362,16 +1368,16 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function postPurge(Request $request)
|
||||
{
|
||||
\Log::warning('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting a PURGE');
|
||||
Log::warning('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting a PURGE');
|
||||
|
||||
if (config('app.allow_purge')=='true') {
|
||||
\Log::debug('Purging is not allowed via the .env');
|
||||
Log::debug('Purging is not allowed via the .env');
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
if ($request->input('confirm_purge')=='DELETE') {
|
||||
|
||||
\Log::warning('User ID ' . Auth::user()->id . ' initiated a PURGE!');
|
||||
Log::warning('User ID ' . Auth::user()->id . ' initiated a PURGE!');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
Artisan::call('snipeit:purge', ['--force' => 'true', '--no-interaction' => true]);
|
||||
@@ -1379,7 +1385,6 @@ class SettingsController extends Controller
|
||||
|
||||
return redirect()->route('settings.index')
|
||||
->with('output', $output)->with('success', trans('admin/settings/message.purge.success'));
|
||||
|
||||
} else {
|
||||
return redirect()->route('settings.purge.index')
|
||||
->with('error', trans('admin/settings/message.purge.validation_failed'));
|
||||
@@ -1390,7 +1395,7 @@ class SettingsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
\Log::error('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting to purge deleted data and is not authorized to.');
|
||||
Log::error('User '.Auth::user()->username.' (ID'.Auth::user()->id.') is attempting to purge deleted data and is not authorized to.');
|
||||
|
||||
|
||||
// Nope.
|
||||
@@ -1422,7 +1427,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function ajaxTestEmail()
|
||||
{
|
||||
@@ -1433,7 +1438,7 @@ class SettingsController extends Controller
|
||||
])->notify(new MailTest());
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, 'Maiol sent!'));
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BulkUsersController extends Controller
|
||||
{
|
||||
@@ -323,18 +324,18 @@ class BulkUsersController extends Controller
|
||||
foreach ($users_to_merge as $user_to_merge) {
|
||||
|
||||
foreach ($user_to_merge->assets as $asset) {
|
||||
\Log::debug('Updating asset: '.$asset->asset_tag . ' to '.$merge_into_user->id);
|
||||
Log::debug('Updating asset: '.$asset->asset_tag . ' to '.$merge_into_user->id);
|
||||
$asset->assigned_to = $request->input('merge_into_id');
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
foreach ($user_to_merge->licenses as $license) {
|
||||
\Log::debug('Updating license pivot: '.$license->id . ' to '.$merge_into_user->id);
|
||||
Log::debug('Updating license pivot: '.$license->id . ' to '.$merge_into_user->id);
|
||||
$user_to_merge->licenses()->updateExistingPivot($license->id, ['assigned_to' => $merge_into_user->id]);
|
||||
}
|
||||
|
||||
foreach ($user_to_merge->consumables as $consumable) {
|
||||
\Log::debug('Updating consumable pivot: '.$consumable->id . ' to '.$merge_into_user->id);
|
||||
Log::debug('Updating consumable pivot: '.$consumable->id . ' to '.$merge_into_user->id);
|
||||
$user_to_merge->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $merge_into_user->id]);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,24 +78,28 @@ class UserFilesController extends Controller
|
||||
*/
|
||||
public function destroy($userId = null, $fileId = null)
|
||||
{
|
||||
$user = User::find($userId);
|
||||
$destinationPath = config('app.private_uploads').'/users';
|
||||
if ($user = User::find($userId)) {
|
||||
|
||||
$this->authorize('delete', $user);
|
||||
$rel_path = 'private_uploads/users';
|
||||
|
||||
|
||||
if ($log = Actionlog::find($fileId)) {
|
||||
$filename = $log->filename;
|
||||
$log->delete();
|
||||
|
||||
if (Storage::exists($rel_path.'/'.$filename)) {
|
||||
Storage::delete($rel_path.'/'.$filename);
|
||||
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
|
||||
}
|
||||
|
||||
if (isset($user->id)) {
|
||||
$this->authorize('update', $user);
|
||||
$log = Actionlog::find($fileId);
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
|
||||
// The log record doesn't exist somehow
|
||||
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.user_not_found', ['id' => $userId]);
|
||||
// Redirect to the licence management page
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Users;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\UserNotFoundException;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Actionlog;
|
||||
@@ -14,15 +13,13 @@ use App\Models\Group;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
use Auth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Str;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use View;
|
||||
use App\Notifications\CurrentInventory;
|
||||
|
||||
/**
|
||||
@@ -133,6 +130,9 @@ class UsersController extends Controller
|
||||
// we have to invoke the
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
\Log::info("About to call customFill, in the 'store' controller!!!");
|
||||
$user->customFill($request, Auth::user());
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
@@ -152,7 +152,7 @@ class UsersController extends Controller
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
return redirect::route('users.index')->with('success', trans('admin/users/message.success.create'));
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.create'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
@@ -182,8 +182,13 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
if ($user = User::find($id)) {
|
||||
$this->authorize('update', $user);
|
||||
|
||||
$this->authorize('update', User::class);
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
$permissions = config('permissions');
|
||||
$groups = Group::pluck('name', 'id');
|
||||
|
||||
@@ -210,87 +215,119 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function update(SaveUserRequest $request, $id = null)
|
||||
{
|
||||
// We need to reverse the UI specific logic for our
|
||||
// permissions here before we update the user.
|
||||
$permissions = $request->input('permissions', []);
|
||||
app('request')->request->set('permissions', $permissions);
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
// This is a janky hack to prevent people from changing admin demo user data on the public demo.
|
||||
// The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
|
||||
// Thanks, jerks. You are why we can't have nice things. - snipe
|
||||
|
||||
if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) {
|
||||
return redirect()->route('users.index')->with('error', 'Permission denied. You cannot update user information for superadmins on the demo.');
|
||||
return redirect()->route('users.index')->with('error', trans('general.permission_denied_superuser_demo'));
|
||||
}
|
||||
|
||||
try {
|
||||
$user = User::findOrFail($id);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
$this->authorize('update', $user);
|
||||
// Figure out of this user was an admin before this edit
|
||||
$orig_permissions_array = $user->decodePermissions();
|
||||
$orig_superuser = '0';
|
||||
if (is_array($orig_permissions_array)) {
|
||||
if (array_key_exists('superuser', $orig_permissions_array)) {
|
||||
$orig_superuser = $orig_permissions_array['superuser'];
|
||||
// We need to reverse the UI specific logic for our
|
||||
// permissions here before we update the user.
|
||||
$permissions = $request->input('permissions', []);
|
||||
app('request')->request->set('permissions', $permissions);
|
||||
|
||||
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
|
||||
// User is valid - continue...
|
||||
if ($user) {
|
||||
$this->authorize('update', $user);
|
||||
|
||||
// Figure out of this user was an admin before this edit
|
||||
$orig_permissions_array = $user->decodePermissions();
|
||||
$orig_superuser = '0';
|
||||
if (is_array($orig_permissions_array)) {
|
||||
if (array_key_exists('superuser', $orig_permissions_array)) {
|
||||
$orig_superuser = $orig_permissions_array['superuser'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only save groups if the user is a super user
|
||||
if (Auth::user()->isSuperUser()) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
// Only save groups if the user is a superuser
|
||||
if (Auth::user()->isSuperUser()) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
|
||||
// Update the user
|
||||
if ($request->filled('username')) {
|
||||
// Update the user fields
|
||||
$user->username = trim($request->input('username'));
|
||||
}
|
||||
$user->email = trim($request->input('email'));
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle', null);
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
$user->company_id = Company::getIdForUser($request->input('company_id', null));
|
||||
$user->manager_id = $request->input('manager_id', null);
|
||||
$user->notes = $request->input('notes');
|
||||
$user->department_id = $request->input('department_id', null);
|
||||
$user->address = $request->input('address', null);
|
||||
$user->city = $request->input('city', null);
|
||||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
// if a user is editing themselves we should always keep activated true
|
||||
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
$user->remote = $request->input('remote', 0);
|
||||
$user->vip = $request->input('vip', 0);
|
||||
$user->website = $request->input('website', null);
|
||||
$user->start_date = $request->input('start_date', null);
|
||||
$user->end_date = $request->input('end_date', null);
|
||||
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
|
||||
$user->email = trim($request->input('email'));
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle', null);
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
$user->company_id = Company::getIdForUser($request->input('company_id', null));
|
||||
$user->manager_id = $request->input('manager_id', null);
|
||||
$user->notes = $request->input('notes');
|
||||
$user->department_id = $request->input('department_id', null);
|
||||
$user->address = $request->input('address', null);
|
||||
$user->city = $request->input('city', null);
|
||||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
// if a user is editing themselves we should always keep activated true
|
||||
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
$user->remote = $request->input('remote', 0);
|
||||
$user->vip = $request->input('vip', 0);
|
||||
$user->website = $request->input('website', null);
|
||||
$user->start_date = $request->input('start_date', null);
|
||||
$user->end_date = $request->input('end_date', null);
|
||||
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)
|
||||
->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
// Do we want to update the user password?
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)
|
||||
->update(['location_id' => $request->input('location_id', null)]);
|
||||
->update(['location_id' => $user->location_id]);
|
||||
|
||||
$permissions_array = $request->input('permission');
|
||||
|
||||
|
||||
// Strip out the superuser permission if the user isn't a superadmin
|
||||
if (! Auth::user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
$permissions_array['superuser'] = $orig_superuser;
|
||||
}
|
||||
|
||||
$user->permissions = json_encode($permissions_array);
|
||||
|
||||
// Handle uploaded avatar
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
if ($user->save()) {
|
||||
// Redirect to the user page
|
||||
return redirect()->route('users.index')
|
||||
->with('success', trans('admin/users/message.success.update'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
|
||||
|
||||
// Do we want to update the user password?
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
$permissions_array = $request->input('permission');
|
||||
|
||||
// Strip out the superuser permission if the user isn't a superadmin
|
||||
if (! Auth::user()->isSuperUser()) {
|
||||
if (!Auth::user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
$permissions_array['superuser'] = $orig_superuser;
|
||||
}
|
||||
@@ -300,6 +337,8 @@ class UsersController extends Controller
|
||||
// Handle uploaded avatar
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
\Log::debug("calling custom fill from the UPDATE method!");
|
||||
$user->customFill($request, Auth::user());
|
||||
//\Log::debug(print_r($user, true));
|
||||
|
||||
// Was the user updated?
|
||||
@@ -323,55 +362,54 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function destroy($id = null)
|
||||
{
|
||||
try {
|
||||
// Get user information
|
||||
$user = User::findOrFail($id);
|
||||
// Authorize takes care of many of our logic checks now.
|
||||
$this->authorize('delete', User::class);
|
||||
|
||||
$this->authorize('delete', User::class);
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($id);
|
||||
|
||||
|
||||
if ($user) {
|
||||
// Check if we are not trying to delete ourselves
|
||||
if ($user->id === Auth::id()) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', 'We would feel really bad if you deleted yourself, please reconsider.');
|
||||
->with('error', trans('admin/users/message.error.cannot_delete_yourself'));
|
||||
}
|
||||
|
||||
if (($user->assets()) && (($assetsCount = $user->assets()->count()) > 0)) {
|
||||
if (($user->assets()) && ($user->assets()->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', 'This user still has '.$assetsCount.' assets associated with them.');
|
||||
->with('error', trans_choice('admin/users/message.error.delete_has_assets_var', $user->assets()->count(), ['count'=> $user->assets()->count()]));
|
||||
}
|
||||
|
||||
if (($user->licenses()) && (($licensesCount = $user->licenses()->count())) > 0) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', 'This user still has '.$licensesCount.' licenses associated with them.');
|
||||
if (($user->licenses()) && ($user->licenses()->count() > 0)) {
|
||||
return redirect()->route('users.index')->with('error', trans_choice('admin/users/message.error.delete_has_licenses_var', $user->licenses()->count(), ['count'=> $user->licenses()->count()]));
|
||||
}
|
||||
|
||||
if (($user->accessories()) && (($accessoriesCount = $user->accessories()->count()) > 0)) {
|
||||
if (($user->accessories()) && ($user->accessories()->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', 'This user still has '.$accessoriesCount.' accessories associated with them.');
|
||||
return redirect()->route('users.index')->with('error', trans_choice('admin/users/message.error.delete_has_accessories_var', $user->accessories()->count(), ['count'=> $user->accessories()->count()]));
|
||||
}
|
||||
|
||||
if (($user->managedLocations()) && (($managedLocationsCount = $user->managedLocations()->count())) > 0) {
|
||||
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', 'This user still has '.$managedLocationsCount.' locations that they manage.');
|
||||
->with('error', trans_choice('admin/users/message.error.delete_has_locations_var', $user->managedLocations()->count(), ['count'=> $user->managedLocations()->count()]));
|
||||
}
|
||||
|
||||
if (($user->managesUsers()) && ($user->managesUsers()->count() > 0)) {
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans_choice('admin/users/message.error.delete_has_users_var', $user->managesUsers()->count(), ['count'=> $user->managesUsers()->count()]));
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
$user->delete();
|
||||
|
||||
// Prepare the success message
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.delete'));
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// Prepare the error message
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,59 +465,25 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function show($userId = null)
|
||||
{
|
||||
if (! $user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($userId)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
|
||||
}
|
||||
// Make sure the user can view users at all
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$userlog = $user->userlog->load('item');
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user = Company::scopeCompanyables($user)->find($userId);
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $user);
|
||||
|
||||
return view('users/view', compact('user', 'userlog'))
|
||||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsuspend a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $id
|
||||
* @return Redirect
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function getUnsuspend($id = null)
|
||||
{
|
||||
try {
|
||||
// Get user information
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('update', $user);
|
||||
|
||||
// Check if we are not trying to unsuspend ourselves
|
||||
if ($user->id === Auth::id()) {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.error.unsuspend');
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
}
|
||||
|
||||
// Do we have permission to unsuspend this user?
|
||||
if ($user->isSuperUser() && ! Auth::user()->isSuperUser()) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'Insufficient permissions!');
|
||||
}
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.unsuspend'));
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
if ($user) {
|
||||
$userlog = $user->userlog->load('item');
|
||||
return view('users/view', compact('user', 'userlog'))->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a view containing a pre-populated new user form,
|
||||
* populated with some fields from an existing user.
|
||||
@@ -493,22 +497,34 @@ class UsersController extends Controller
|
||||
public function getClone(Request $request, $id = null)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
// We need to reverse the UI specific logic for our
|
||||
// permissions here before we update the user.
|
||||
$permissions = $request->input('permissions', []);
|
||||
app('request')->request->set('permissions', $permissions);
|
||||
|
||||
try {
|
||||
// Get the user information
|
||||
$user_to_clone = User::withTrashed()->find($id);
|
||||
|
||||
$user_to_clone = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed();
|
||||
$user_to_clone = Company::scopeCompanyables($user_to_clone)->find($id);
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $user_to_clone);
|
||||
|
||||
|
||||
if ($user_to_clone) {
|
||||
|
||||
|
||||
$user = clone $user_to_clone;
|
||||
|
||||
// Blank out some fields
|
||||
$user->first_name = '';
|
||||
$user->last_name = '';
|
||||
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
|
||||
$user->id = null;
|
||||
|
||||
// Get this user groups
|
||||
// Get this user's groups
|
||||
$userGroups = $user_to_clone->groups()->pluck('name', 'id');
|
||||
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$clonedPermissions = $user_to_clone->decodePermissions();
|
||||
@@ -517,16 +533,14 @@ class UsersController extends Controller
|
||||
|
||||
// Show the page
|
||||
return view('users/edit', compact('permissions', 'userPermissions'))
|
||||
->with('user', $user)
|
||||
->with('groups', Group::pluck('name', 'id'))
|
||||
->with('userGroups', $userGroups)
|
||||
->with('clone_user', $user_to_clone);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// Prepare the error message
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
->with('user', $user)
|
||||
->with('groups', Group::pluck('name', 'id'))
|
||||
->with('userGroups', $userGroups)
|
||||
->with('clone_user', $user_to_clone);
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,8 +560,20 @@ class UsersController extends Controller
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
|
||||
User::with('assets', 'accessories', 'consumables', 'department', 'licenses', 'manager', 'groups', 'userloc', 'company')
|
||||
->orderBy('created_at', 'DESC')
|
||||
$users = User::with(
|
||||
'assets',
|
||||
'accessories',
|
||||
'consumables',
|
||||
'department',
|
||||
'licenses',
|
||||
'manager',
|
||||
'groups',
|
||||
'userloc',
|
||||
'company'
|
||||
)->orderBy('created_at', 'DESC');
|
||||
|
||||
// FMCS scoping
|
||||
Company::scopeCompanyables($users)
|
||||
->chunk(500, function ($users) use ($handle) {
|
||||
$headers = [
|
||||
// strtolower to prevent Excel from trying to open it as a SYLK file
|
||||
@@ -565,7 +591,8 @@ class UsersController extends Controller
|
||||
trans('general.licenses'),
|
||||
trans('general.accessories'),
|
||||
trans('general.consumables'),
|
||||
trans('admin/users/table.groups'),
|
||||
trans('general.groups'),
|
||||
trans('general.permissions'),
|
||||
trans('general.notes'),
|
||||
trans('admin/users/table.activated'),
|
||||
trans('general.created_at'),
|
||||
@@ -580,6 +607,19 @@ class UsersController extends Controller
|
||||
$user_groups .= $user_group->name.', ';
|
||||
}
|
||||
|
||||
|
||||
$permissionstring = "";
|
||||
|
||||
if($user->isSuperUser()) {
|
||||
$permissionstring = trans('general.superuser');
|
||||
}
|
||||
elseif($user->hasAccess('admin')) {
|
||||
$permissionstring = trans('general.admin');
|
||||
}
|
||||
else {
|
||||
$permissionstring = trans('general.user');
|
||||
}
|
||||
|
||||
// Add a new row with data
|
||||
$values = [
|
||||
$user->id,
|
||||
@@ -597,6 +637,7 @@ class UsersController extends Controller
|
||||
$user->accessories->count(),
|
||||
$user->consumables->count(),
|
||||
$user_groups,
|
||||
$permissionstring,
|
||||
$user->notes,
|
||||
($user->activated == '1') ? trans('general.yes') : trans('general.no'),
|
||||
$user->created_at,
|
||||
@@ -626,7 +667,11 @@ class UsersController extends Controller
|
||||
public function printInventory($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$show_user = User::where('id', $id)->withTrashed()->first();
|
||||
$show_user = Company::scopeCompanyables(User::where('id', $id)->withTrashed()->first());
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $show_user);
|
||||
|
||||
$assets = Asset::where('assigned_to', $id)->where('assigned_type', User::class)->with('model', 'model.category')->get();
|
||||
$accessories = $show_user->accessories()->get();
|
||||
$consumables = $show_user->consumables()->get();
|
||||
@@ -651,16 +696,23 @@ class UsersController extends Controller
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
if (!$user = User::find($id)) {
|
||||
return redirect()->back()
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
|
||||
}
|
||||
if (empty($user->email)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
$user = Company::scopeCompanyables(User::find($id));
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $user);
|
||||
|
||||
if ($user) {
|
||||
|
||||
if (empty($user->email)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
}
|
||||
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
}
|
||||
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -672,19 +724,19 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function sendPasswordReset($id)
|
||||
{
|
||||
if (($user = User::find($id)) && ($user->activated == '1') && ($user->email != '') && ($user->ldap_import == '0')) {
|
||||
if (($user = Company::scopeCompanyables(User::find($id))) && ($user->activated == '1') && ($user->email != '') && ($user->ldap_import == '0')) {
|
||||
$credentials = ['email' => trim($user->email)];
|
||||
|
||||
try {
|
||||
|
||||
Password::sendResetLink($credentials);
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/users/message.password_reset_sent', ['email' => $user->email]));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->back()->with('error', ' Error sending email. :( ');
|
||||
return redirect()->back()->with('error', trans('general.error_sending_email'));
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'User is not activated, is LDAP synced, or does not have an email address ');
|
||||
return redirect()->back()->with('error', trans('general.pwd_reset_not_sent'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ViewAssetsController extends Controller
|
||||
/**
|
||||
* Redirect to the profile page.
|
||||
*
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user