Compare commits
1642 Commits
dev
...
snyk-upgra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61cf9dcfe0 | ||
| 5fbca80a3c | |||
| 4fa1410790 | |||
| 411fc5f37f | |||
| f7c20719f7 | |||
| e5a7aeede8 | |||
|
|
cfbf3a4033 | ||
| 89cda7c415 | |||
| e211436675 | |||
| d1be96a1df | |||
|
|
7e87312d99 | ||
|
|
73c277acfb | ||
| 0728233206 | |||
| b127ce0514 | |||
| 5924222296 | |||
| 6d2c601b5d | |||
| f2fb993596 | |||
| c40232099f | |||
|
|
17d33a9ad2 | ||
|
|
e380bafbb0 | ||
|
|
4c63e68625 | ||
|
|
8b5d150fe2 | ||
|
|
10808f3622 | ||
|
|
37ba8f0f29 | ||
|
|
51fb0150d9 | ||
|
|
03dd12cea1 | ||
|
|
56ae719a2e | ||
|
|
68b7e50b9e | ||
|
|
386e2009c4 | ||
|
|
3da55bca81 | ||
|
|
5d8f1457d4 | ||
|
|
39d9ebc549 | ||
|
|
b2f3664909 | ||
|
|
06c2f0e3d0 | ||
|
|
2d2392c8ae | ||
|
|
5e7061bc61 | ||
|
|
8451a2bd4b | ||
|
|
dd135f10ec | ||
|
|
247eb03cea | ||
|
|
19c16cfbf1 | ||
|
|
456687edc5 | ||
|
|
aab63bb627 | ||
|
|
efb3cac129 | ||
|
|
36314339fb | ||
|
|
c6247e445f | ||
| b028856eb5 | |||
| 1925b2ef0c | |||
|
|
0d1bd4522e | ||
| f2e84b63fa | |||
| ac6257ed6d | |||
| 2486dec824 | |||
| 04e6c0d0fc | |||
| b948c42fe2 | |||
| 5d7be1a482 | |||
| 4e81d06a6e | |||
|
|
0f2400ff2b | ||
| 7ed2332a3e | |||
|
|
c432b32d08 | ||
|
|
8cf628153c | ||
|
|
ab0bd9e7d9 | ||
|
|
51cd70f2ec | ||
| 235d4e3784 | |||
|
|
dd0bbad18f | ||
| f32059c3e7 | |||
|
|
92578ef738 | ||
| 367205f955 | |||
| 1fdf36669f | |||
|
|
dfa06b148f | ||
|
|
139b437118 | ||
| 4dca636359 | |||
| 4f1d2a179b | |||
|
|
ede28f375a | ||
|
|
06dfb8d7f6 | ||
| 2d4090922f | |||
| 3d58a09c21 | |||
| 05da5f016c | |||
|
|
256eead944 | ||
|
|
dcb8ffaa0d | ||
|
|
def671dd47 | ||
| 717f360952 | |||
|
|
1c0b91f52c | ||
| c733d39d30 | |||
| 87bdea3bba | |||
| efe80694f9 | |||
| be7e6ee400 | |||
| 319a7b27c5 | |||
| dbe20ac724 | |||
|
|
dc6ed3f43a | ||
| ecddb5c6d3 | |||
| 34e5a37e24 | |||
| eaca6bcc72 | |||
|
|
bad588d4b2 | ||
|
|
d5daf20351 | ||
|
|
f512ab96fc | ||
|
|
0cfa9a5483 | ||
|
|
979f934f22 | ||
|
|
1fa5820550 | ||
| 8233afa825 | |||
| ba6f67798d | |||
| daabbaa3e4 | |||
| caa68b24d6 | |||
| 114db744b4 | |||
| 8c910ed5f3 | |||
| 1ef4332df6 | |||
| 6e98269a0b | |||
|
|
c918b91438 | ||
| 2a1e634b34 | |||
|
|
d45165d759 | ||
| a0a885e004 | |||
| 52f7070311 | |||
| e0650da5e9 | |||
| bf5cf3bce4 | |||
| 07d1dc4955 | |||
| 11db41b900 | |||
| 3bca959d49 | |||
| 3fee673314 | |||
| 3121d12f3a | |||
| b8731c3f37 | |||
| b445a42896 | |||
| dbab224a9d | |||
| 03f9659243 | |||
| 0fb56133ff | |||
| afa7ca629c | |||
| 9a9c382d7f | |||
| 518b8d67bc | |||
| ff0f8194e0 | |||
| f84f982c29 | |||
| 420d81866c | |||
| 1cd78ce93a | |||
| 7cae8af89a | |||
| 5088cea413 | |||
| d670d52083 | |||
| 004aa116fb | |||
| 32a08e1e10 | |||
| 30d39c5326 | |||
| 8affea3360 | |||
| 3b6b4dc388 | |||
| 9a9e75d96d | |||
| ad6e4f0a23 | |||
| f66bc6d8aa | |||
| 845bb4763b | |||
| e476b203c7 | |||
| dd163630cb | |||
| 5d8a5ede7d | |||
| 35ccb03c2b | |||
| f6df068787 | |||
| b60a3601cb | |||
| 8676844930 | |||
| 7e80e19eec | |||
| e21c7ce1d0 | |||
| 42f2baca5e | |||
| d9c0c8f1d8 | |||
| b98e184d32 | |||
| f764466199 | |||
| 6adfd9f3fa | |||
| dbf9db1c92 | |||
| 6cb7a8cb43 | |||
| 2913960bcb | |||
| 8d0382a279 | |||
| aa5ef50ed0 | |||
| d87b869633 | |||
| 99782bdf67 | |||
| 6fa459e88e | |||
| 954f2beea7 | |||
| f216413a23 | |||
| 63572a2740 | |||
| aeb7595166 | |||
| f507d198a7 | |||
| 9b5166c570 | |||
| 2271aae557 | |||
| 8ed07e539f | |||
| b029574fa5 | |||
| e9d903ce79 | |||
| 1c9100e5de | |||
| 1a59ac955d | |||
| 4d8e58f920 | |||
|
|
55ce8e0d9e | ||
|
|
0d1a3f8dbf | ||
|
|
9687a3c7a8 | ||
|
|
471534021e | ||
| 8c75abf16a | |||
| 472abaf30b | |||
| 35426e1189 | |||
| 6507d57424 | |||
| 4252878019 | |||
|
|
8451cc1547 | ||
|
|
70ecefb007 | ||
| 4e7be1a18c | |||
| d1cc468dfa | |||
| 484512f2c7 | |||
| 83b4df3462 | |||
| 8ea46b4ea8 | |||
| a68faa11b0 | |||
| 541cd93c99 | |||
| c5a7982945 | |||
| 5cee36453e | |||
| 47ea87b614 | |||
| 4ad0df2c39 | |||
| f2ff8398eb | |||
| 3f915d6964 | |||
| 3e6a9da0d1 | |||
|
|
ca5f4ffd84 | ||
|
|
b66f9f79d2 | ||
|
|
dc0bfe6d7a | ||
|
|
5de463bd02 | ||
|
|
f3d858d785 | ||
|
|
0e537a6b46 | ||
| 8d78b15751 | |||
|
|
e1fc2fa271 | ||
|
|
27466ad3a6 | ||
|
|
f0d63b4120 | ||
|
|
163dd98b7b | ||
| 8610a1678d | |||
|
|
d20c7832da | ||
|
|
fcba4015ed | ||
|
|
bb34a3c33a | ||
|
|
dbded3ba14 | ||
|
|
3a654e0480 | ||
|
|
4b358fba4f | ||
|
|
5aaeab686d | ||
|
|
5ff0a4e86f | ||
|
|
0c866ed009 | ||
|
|
f09bf02c10 | ||
|
|
ee2c30f938 | ||
|
|
82f28bc712 | ||
|
|
8020baf9d1 | ||
|
|
b1cac44432 | ||
|
|
ab46b6118d | ||
|
|
52a1bfe804 | ||
|
|
c7c5b5298c | ||
|
|
4c07917542 | ||
|
|
fe06a2dd55 | ||
| 143f002ead | |||
| 83c0ae5841 | |||
| 0c05386804 | |||
| 9a70d4de0d | |||
| 8950b5f50b | |||
| 59e077fd6a | |||
| c9fc5b2157 | |||
| e128bcfaf9 | |||
| 7cf6b0bba6 | |||
| c36d4783e7 | |||
| 29acd76051 | |||
| eacf33b642 | |||
| 46666db231 | |||
| 3dfa753f33 | |||
| 41ae64a8c9 | |||
| fcf5400f71 | |||
| 2257736ad9 | |||
| 147b55ae78 | |||
| 5b6c951dc0 | |||
| 0840d65011 | |||
| 6394234960 | |||
| e1dde655cb | |||
| e01d59cbd8 | |||
| 22d1e195ed | |||
| c776e7b102 | |||
|
|
3cd95a5a6b | ||
| aba7845a4b | |||
| f23c4c42b5 | |||
| 05149dc723 | |||
| a615c9d1d8 | |||
| eed9ac8ff4 | |||
| 258e7fe307 | |||
| a49a86a597 | |||
| b01c5d57c7 | |||
| 4b0c113941 | |||
| 277a0ae2e3 | |||
| a61aafdc6a | |||
| 3a2899f913 | |||
| 05b172cd47 | |||
| 351259909c | |||
| 0af44ffe02 | |||
| 06799a90b0 | |||
| c7fc9a1480 | |||
| d5b8a89776 | |||
| eada83aaf8 | |||
| 9a63a9de80 | |||
| b4859fd97f | |||
| 794a18cfa0 | |||
| 581b732b05 | |||
| 0119875d22 | |||
| 0f34c7dfb9 | |||
| 04be7fdb3c | |||
| 123d45d172 | |||
|
|
864ae189af | ||
|
|
13b2620d2e | ||
|
|
75c4ad73ed | ||
|
|
2c2f5621df | ||
|
|
855accab4f | ||
|
|
afaa702a02 | ||
|
|
15637d398e | ||
|
|
f346625bb1 | ||
| 9764fef01c | |||
| 338d6c6f9d | |||
| 9da6f39e6e | |||
| 5759794f71 | |||
| ee9d96a5bf | |||
| 9197a86670 | |||
| 64e45b1892 | |||
| 6890a0939d | |||
| 61c78534d8 | |||
| 5a611f96a8 | |||
| 3e7f34d5c4 | |||
|
|
78d16bec5f | ||
| 33f658e088 | |||
| 0b46c34abe | |||
| f1ad20cb0b | |||
| 511da54909 | |||
| 104407646c | |||
| 622229cfc9 | |||
| f50e6ad209 | |||
| d7529cef80 | |||
|
|
f7432158e1 | ||
|
|
2df6597771 | ||
| 60a15c2227 | |||
| a369db9ca1 | |||
|
|
ac35d61ae2 | ||
| 489668a2ff | |||
| c9087d5df4 | |||
| ac06b3700c | |||
| 31578f5ed3 | |||
| 9b178ade4b | |||
| 92522b5494 | |||
| ccebb4eb73 | |||
| 16873e7477 | |||
| 79318e2f28 | |||
| f44a4f5d9d | |||
| b28925079b | |||
| cbf6803812 | |||
|
|
a0e804aa62 | ||
| d1b87bd64b | |||
| 5b783035a3 | |||
| a06363da75 | |||
| a68c691f75 | |||
|
|
8a61d258fb | ||
|
|
fd3f220752 | ||
|
|
0a0b2927ae | ||
|
|
4c19773818 | ||
|
|
071af03577 | ||
|
|
58da0b54b3 | ||
|
|
528ea4bcfb | ||
|
|
ca124d34cd | ||
| 70b53ce21a | |||
| 6dc226ab0b | |||
| d6524942f7 | |||
| 3ca5e81e34 | |||
| 561b5ad1b9 | |||
| 229bb9a5e0 | |||
| 280d3e9043 | |||
| a2384f4b35 | |||
| d65bf5bcd5 | |||
| e763a9140c | |||
| 3a06a375ef | |||
| bdf3f38190 | |||
| 5b2f6e44b1 | |||
| 250401fd62 | |||
| c1fb03add1 | |||
|
|
56bf7efcef | ||
| cc0508683e | |||
|
|
b232b65860 | ||
| 406d1d31c0 | |||
| f8fe5c92aa | |||
| 69623bd248 | |||
| 6d4995074b | |||
| ac94068864 | |||
| 31ed355d64 | |||
| 8a54fb272a | |||
| 6ecf807081 | |||
| 728472b897 | |||
| 30a8c8a03a | |||
| 00e7490037 | |||
| 1d152d730a | |||
| a010d91da2 | |||
| 3717248764 | |||
| 27753e903b | |||
| b621f71b54 | |||
| d4b3300b88 | |||
| 242fb9dda0 | |||
| 4f505cd155 | |||
| dca70a0f53 | |||
| b9a8c826ec | |||
| 363deb3ca7 | |||
| f77b5f7556 | |||
| d60efe38e2 | |||
| f779342e02 | |||
| 204e62b3bb | |||
| 5adc24f997 | |||
| ab25b0f64a | |||
| dd8e59cd4f | |||
| 0c4c36eb74 | |||
|
|
3481f10033 | ||
|
|
fdf61fef3d | ||
|
|
30c3c5d33f | ||
|
|
6cd7df79d9 | ||
|
|
b5b0b5d7bd | ||
|
|
d0266c7d0c | ||
|
|
283fd30c19 | ||
|
|
5d13135bd0 | ||
|
|
6c2961eef6 | ||
|
|
f9c006403e | ||
|
|
1f65f0fe30 | ||
|
|
a49a287855 | ||
| bf7f830f9e | |||
| ffce564a47 | |||
| 2cdcd9a424 | |||
| ffbc733571 | |||
| 3ea7188253 | |||
| 97c6a4c4ef | |||
| f3fe112427 | |||
| 189e4ef230 | |||
| d35b895ff4 | |||
| c47c511674 | |||
| 20be21c87d | |||
| 1d57dc519f | |||
| 644b42afe9 | |||
| 9ad11f24c2 | |||
| 1e2564f753 | |||
| f1ab544aae | |||
|
|
aa85d24952 | ||
|
|
b0f8817aae | ||
|
|
b9e3e9f5f2 | ||
|
|
ea05f5992d | ||
|
|
9c138d6bee | ||
|
|
7325880a70 | ||
|
|
320631e91d | ||
|
|
0997864cd6 | ||
|
|
9e0451d541 | ||
|
|
84245c5584 | ||
|
|
d72c0ea9f5 | ||
|
|
bf9500f960 | ||
|
|
f18cda6c00 | ||
| 0ab20a75ac | |||
| 133a6ed4ea | |||
| bc424b7a00 | |||
| be3c6d89ee | |||
| d814ed1972 | |||
| 8322229001 | |||
| da2a76d6cf | |||
|
|
abf7a2783c | ||
| 6690c69efe | |||
| 67044b7cf9 | |||
| 048d3af39b | |||
| 816989d472 | |||
| 8f6310b463 | |||
| 3cebdd838b | |||
| 9996404e29 | |||
| 1598efac35 | |||
| bfacb86f35 | |||
|
|
e5e5c7e19c | ||
|
|
9ede1d7eea | ||
| ff8cf83f1b | |||
| 326c8e7b81 | |||
| daeecfbd4f | |||
| 15c330c784 | |||
| 7ba80ccc58 | |||
| ef4efe723f | |||
| 64ef8e9acd | |||
| d88b6f6e95 | |||
| 440d66322c | |||
|
|
469a646ae3 | ||
| 9cb9ebbe4f | |||
|
|
d004855dd8 | ||
|
|
911645c9f5 | ||
|
|
6a16e78d80 | ||
| 937c5024cb | |||
|
|
7e12ba1a00 | ||
| 89d57bb915 | |||
| a111387a3f | |||
| 234fcb13af | |||
| 377fdad6e0 | |||
| 68020b0201 | |||
| f0628c5c05 | |||
| 66d2a2c48c | |||
| 6f1229418e | |||
| a430fee568 | |||
| 53b7ce651e | |||
| 1028064abe | |||
| ab285870e3 | |||
| 1a02d46151 | |||
| 1bb70bbe66 | |||
| c158155194 | |||
| 7bfd4c1198 | |||
| ea959d1a10 | |||
| 9fc5d81877 | |||
| 719aac6727 | |||
| c8398920c5 | |||
|
|
55bd914b8f | ||
|
|
2b06a52705 | ||
|
|
2c9c997d5a | ||
|
|
17f305d9cb | ||
|
|
44d071696f | ||
|
|
fa8f44135c | ||
|
|
b38e79ef8f | ||
|
|
e2e39fbdf5 | ||
|
|
a8262932cc | ||
|
|
6d3e9b3c1d | ||
|
|
b91619801d | ||
|
|
f1e32096f3 | ||
|
|
f5a327e72c | ||
| 4fe05e39f7 | |||
|
|
c3779bf371 | ||
| 54aa7e249e | |||
| 1744d192b5 | |||
| 3f7426c550 | |||
| c4e017c7b7 | |||
| e723831548 | |||
| 56b5dcd7d8 | |||
|
|
913d229630 | ||
| 3dfb11d692 | |||
| 759ab14899 | |||
| 329ff7c478 | |||
| a6022f7251 | |||
| d345be889e | |||
| 2dbe9955d9 | |||
|
|
8fcd33e0a1 | ||
| 14aa5e3b28 | |||
| 2cb9aac070 | |||
| 1c6a5c9d04 | |||
| 936aabf412 | |||
| fa7478a5b5 | |||
| 8992554ba6 | |||
| c8895137fa | |||
| f8bc75f1f9 | |||
| 43faa0f020 | |||
| 8e76a18590 | |||
| 9c1df26b4b | |||
| 6d89a499b1 | |||
| 03430c272f | |||
| baf15296ce | |||
| 56f74964ed | |||
| afad2df8f5 | |||
| d17ff1f2e7 | |||
| 4d9be34b4e | |||
|
|
5213b28c2e | ||
|
|
e0df5a53dc | ||
|
|
f13022ee1d | ||
|
|
c063badb38 | ||
|
|
693cf4cd2a | ||
| cc70b1a67f | |||
| 08d3366fe1 | |||
| 080b2fcfc4 | |||
| 5b1d02e3c1 | |||
| b322a405c7 | |||
| 0ed05b103b | |||
| 5431da3930 | |||
| 2a6e5b7992 | |||
| 85af95a8a0 | |||
| e0b04a57ae | |||
| 0533cd24fe | |||
| 9598031dd1 | |||
| 9f808717d2 | |||
| b4aa71b81b | |||
|
|
321a8f0946 | ||
|
|
93124cdad0 | ||
|
|
26eae9b1cf | ||
| f803bc0a59 | |||
| be5925f40d | |||
| b25b697e7b | |||
| 66e2a68cde | |||
| 673f6051c1 | |||
| 0478ab9eb2 | |||
| b7fde520be | |||
| 6f031f78e8 | |||
| 2f850ce152 | |||
|
|
a6240a3ca9 | ||
| 8906936e51 | |||
| fd6867740e | |||
| 183231206b | |||
| d4e832b526 | |||
| 667ff5cf94 | |||
| 6d2db21a62 | |||
| df398faddb | |||
| e07e2d2e2c | |||
| ca29189f9e | |||
|
|
15a1a3f666 | ||
|
|
8c4c09bc90 | ||
|
|
5a926d2d41 | ||
|
|
5127d8b283 | ||
|
|
c043a43fc5 | ||
|
|
013930d7c3 | ||
|
|
6b0d66be25 | ||
|
|
3b492d2005 | ||
|
|
29f4bfd3cc | ||
|
|
86e42c36cb | ||
|
|
c1d81c65d2 | ||
|
|
fd864b03d9 | ||
|
|
97eb51a434 | ||
|
|
76f9b96369 | ||
|
|
13e5cf2133 | ||
|
|
1c92f6449e | ||
|
|
3ed6cc8d5c | ||
|
|
5b79e415e0 | ||
|
|
98036c7266 | ||
|
|
8c8c59d8bd | ||
| fdbadbac7f | |||
| 9d788f919a | |||
| fac00bc4f6 | |||
| 6e511fc4a9 | |||
| 2f6214c48d | |||
| b2f791fc5f | |||
| 39140611ca | |||
| 567ee3062b | |||
| 584e0903c8 | |||
| 8990006d4c | |||
| 7feb78ba70 | |||
| d67ca133eb | |||
| f95557aa3a | |||
| f539ab7294 | |||
|
|
79284e2271 | ||
|
|
e303b565fc | ||
|
|
985e8a5cd6 | ||
|
|
541e1c4c2c | ||
|
|
10c1d500f3 | ||
|
|
678f16788c | ||
|
|
8871a1b7ae | ||
|
|
05c2f28f25 | ||
|
|
30179fe353 | ||
|
|
6846dd760d | ||
|
|
408f285cf3 | ||
|
|
fa76fc0f78 | ||
|
|
ade9cd6b05 | ||
|
|
933d18b784 | ||
| 48bae3b181 | |||
| 78f6ed1ef6 | |||
| 6d503939a5 | |||
| 3677f1c45b | |||
|
|
a23ce4e0e6 | ||
|
|
e7c43fa53a | ||
|
|
0de6735855 | ||
|
|
aa274b1df4 | ||
|
|
51261a4a5c | ||
|
|
9e92561502 | ||
| 2b28ebc014 | |||
| 196d9c358c | |||
|
|
38d58d92b4 | ||
| e136c910b5 | |||
| d0c4f7eea2 | |||
| bf9b0f3973 | |||
| 4534b46e97 | |||
| 68affe4e4c | |||
| aa2671f167 | |||
| 2178b61602 | |||
| 6a6b8ed9b2 | |||
| 784e8cf787 | |||
| 89a8b6926a | |||
| 9c926d15c1 | |||
|
|
1b1144c628 | ||
|
|
e9b1802af3 | ||
|
|
fe7c1d93ae | ||
|
|
8d1be827a7 | ||
|
|
cd4fe13117 | ||
|
|
34e169adba | ||
|
|
d064756cc6 | ||
|
|
bf7e1658f4 | ||
| 8de3c5dcba | |||
| e8827cbd50 | |||
| f839003f5e | |||
| b68d97455b | |||
|
|
e9dcb8b3f0 | ||
|
|
5e838ad61e | ||
|
|
a365977702 | ||
| 8b2542ebef | |||
|
|
69c5018367 | ||
|
|
4f536ae5a9 | ||
|
|
95395d1da7 | ||
|
|
c6c639afc2 | ||
| 41147b26f2 | |||
|
|
fbf437ac99 | ||
|
|
5faf49688d | ||
|
|
4d7d0ed74d | ||
|
|
979b9f704c | ||
|
|
4124cf39db | ||
|
|
c83e21d588 | ||
|
|
c88630e9af | ||
|
|
40b265e145 | ||
|
|
3ad2b2fb8e | ||
|
|
8b671065e9 | ||
|
|
028e1a191e | ||
|
|
a133f82997 | ||
|
|
c4f3eb9a4e | ||
|
|
8a52c4529f | ||
|
|
d0493f3dd0 | ||
|
|
b845552c37 | ||
| 4e97209494 | |||
| aa0b010bed | |||
| cb79ea64cf | |||
|
|
38409d0d63 | ||
|
|
32dfb4eef3 | ||
|
|
a6de64a089 | ||
|
|
b226814676 | ||
|
|
805de3291b | ||
|
|
b0ab63e30e | ||
|
|
13bfc52b77 | ||
|
|
bbce78cab4 | ||
|
|
1e084d5131 | ||
|
|
af699161da | ||
| 2e1c2cd0b2 | |||
| cd7366b8ff | |||
| ec74f6594c | |||
| 5d2e9affc0 | |||
| 06b7ce4db0 | |||
|
|
4bf695f559 | ||
| 59daa1ff08 | |||
| 04044673e2 | |||
| 3b837dc6b0 | |||
| 2f029e2523 | |||
| 66d477795f | |||
| 78f23db801 | |||
| e62a21c469 | |||
| 9756622148 | |||
| 065cb1b746 | |||
| 54a7ad86dc | |||
| 0e7c86ac2b | |||
| e023964cb2 | |||
| 16f4eb65ef | |||
| 11eb12324e | |||
| 7a0d3fc8a0 | |||
| 3ed2aadc34 | |||
| b2004e3483 | |||
| 55f363a64f | |||
| 7a6ed9f7f4 | |||
| 8fa8c85077 | |||
| 245ffc9d45 | |||
| 6a9a2f0a9e | |||
| 9dbefe5a8a | |||
| cce2a79ee4 | |||
| 8e94ab2d7d | |||
| 6e0337cdeb | |||
| 1c3b8f065e | |||
| c43d5574b4 | |||
| 0e5c654b02 | |||
| 14d6d59581 | |||
| 3796961293 | |||
| c471a97a23 | |||
| fc853bd5f1 | |||
| d0ea0ae4d3 | |||
| 8a6d1281bb | |||
| 8797d51ef4 | |||
| 2c8ac1f155 | |||
| 42706de9df | |||
| 3ce99b8751 | |||
| 44c4f16c5c | |||
| cdccde528e | |||
| f32655c156 | |||
| d7255f004d | |||
| 56a1aaa19c | |||
| 86491bfb2e | |||
| 8dc43ccfce | |||
| 7ff49700fd | |||
| e14c7aafb3 | |||
| 0f727168be | |||
| 0de47b3104 | |||
| 6b3eb97568 | |||
| bd4ba41b0b | |||
| fb2f2d9739 | |||
| a468ae01ff | |||
| 6d534bd1c3 | |||
| 31820317de | |||
| e2efa1f1bd | |||
| 86a0936cd4 | |||
| 645b623a40 | |||
| 96bd56a828 | |||
| 870f1c5194 | |||
| 5d1adf7af8 | |||
| a1170a1347 | |||
| 9b5aab6e6e | |||
| a25776fbbb | |||
| 54b5929aa4 | |||
| 729fc3fd39 | |||
| b4cf05ad44 | |||
| 4da8b32b1a | |||
| c35342df59 | |||
| 7d8d407d07 | |||
| 1ceb109a28 | |||
| a8181ff2b7 | |||
| e42c4c3023 | |||
| ff040eec58 | |||
| 5d663d21b3 | |||
| b73c2d3726 | |||
| ee96acbe4f | |||
| fd22b79d42 | |||
| 99f56b9ef8 | |||
| 9a686c1112 | |||
| ffcf823a7f | |||
| a85c4bf115 | |||
| 29d7167c24 | |||
| 3c6a570394 | |||
| 419fa322a3 | |||
| 71a2e1b6dd | |||
| a352c21198 | |||
| 4accb60a24 | |||
| 88b92a9572 | |||
| e39fa78981 | |||
| 4205113b00 | |||
| ad47efefaf | |||
| 890399dd74 | |||
| 2698ede55e | |||
| c9f0ea2512 | |||
| 228f9c7c6b | |||
| 63e05e924a | |||
| a66cde3934 | |||
| 0b8a2904ec | |||
| 5fffa97ea7 | |||
| 1d14d86d8e | |||
| 217ab89667 | |||
| ac2dd23ad7 | |||
| 7a4f72378d | |||
| 8190839823 | |||
| 4e9d97268f | |||
| 985f7c94da | |||
| 3d28e73369 | |||
| 4076b138b9 | |||
|
|
99d4c709bd | ||
| 93951cfbc8 | |||
| 4ac86c434e | |||
| 171cfa7aab | |||
| a494dbe662 | |||
| c9d02fb11c | |||
| 3ba65385c5 | |||
| 58a2da1996 | |||
| 845d6ba12b | |||
| f0b55b7b2e | |||
| e6dd75c2a8 | |||
| 7b7154085e | |||
| 1c119e80e9 | |||
| 3d86f859c6 | |||
| 0bfad00df7 | |||
| 64efa723b3 | |||
| 0072b28965 | |||
| 21fa5d24af | |||
| 16ec3c515e | |||
| 6868144e25 | |||
| 0e5af96900 | |||
| ded5caf271 | |||
| e8e1e91d1c | |||
| 6a16d545ec | |||
| 976b6fbb78 | |||
| efc5571fb3 | |||
| 6ad4b3a6c4 | |||
| cc0fe080cf | |||
|
|
1de89fba5f | ||
| 0c668c9c62 | |||
| 6a3c3a3566 | |||
| e17f79e0b1 | |||
| f0461fb65f | |||
| aa38927522 | |||
| 881d06deea | |||
| 683869214b | |||
| 3322a6e005 | |||
| 65a48454ba | |||
| 0de8e17593 | |||
| 17beb4152b | |||
| b09097294f | |||
| c6e1b0248d | |||
| 0a956e1fc5 | |||
| e2dee426bf | |||
| 37a738c094 | |||
| bef4c3440b | |||
| b36ad8042f | |||
| 6ec38853ff | |||
| 69144a665f | |||
| 382a1d0ef8 | |||
| 41c751a76d | |||
| 01e46042fb | |||
| f1a28b6efe | |||
| 7fddeeeaae | |||
| bacf35bb4b | |||
| 4a83c7e171 | |||
| 3cbce25394 | |||
|
|
c2b58cc82d | ||
| ac326d2d74 | |||
| 50aaf8b343 | |||
| f667ac6430 | |||
| e2e599ed35 | |||
| 897e422e15 | |||
| 0031be6882 | |||
| 45880ed7a8 | |||
| b7e964174a | |||
| 77b8f60cb1 | |||
| 4cbde00ef3 | |||
| 196472181e | |||
| 2fa7a0c7a5 | |||
| 38e982e70b | |||
| 8a9b57547a | |||
| 6dd3d6255d | |||
| 656de567d7 | |||
| 24e80d7851 | |||
| bc642b48da | |||
| cc07998a8a | |||
| 178309bb1e | |||
| 40c19f47c7 | |||
| 7f82b24b0c | |||
| f31a8da0e1 | |||
| 8ed158ab3c | |||
| 08af379a57 | |||
| 79dbdd3e5a | |||
| f7e8d5bdf7 | |||
| 52eba56e34 | |||
| 4c42276deb | |||
| b4eb772662 | |||
| 825730c3f9 | |||
| b7a2253b01 | |||
| ce1174d41b | |||
| f2da168a03 | |||
| fec4b29261 | |||
| 01b8dadd5f | |||
| 3ee97468f9 | |||
| c6d318bbc3 | |||
| 4ebb07a79a | |||
| 2580d0874f | |||
| 2168e693d8 | |||
| bc2a25346b | |||
| 03e969e08c | |||
| 37e3872782 | |||
| 71442e4160 | |||
| c2b69a769a | |||
| c0bc0e03c0 | |||
| f49bef1112 | |||
| 191b2978ec | |||
| 2771cdd053 | |||
| 2d576645d8 | |||
| be9884b468 | |||
| df280ce7a6 | |||
| c1182b3b90 | |||
| 425a3561ac | |||
| 12be354cc9 | |||
| 7e2917d447 | |||
| cb0de6a2d5 | |||
| 95318d1b36 | |||
| b92456c178 | |||
| bcb25b5d5e | |||
| 6a79204204 | |||
| d3776a8d3f | |||
| 325c6a0448 | |||
| 7c089eed80 | |||
| 8b40a46c1b | |||
| ef4061b96c | |||
|
|
506849b450 | ||
|
|
e73ce8c943 | ||
| bb4543bc65 | |||
|
|
f8d5850a89 | ||
| cce994d35c | |||
| 02cfb53147 | |||
| 0755df03cf | |||
| 51f0ad7497 | |||
| b4a49d20c8 | |||
| 44ccaf10d4 | |||
| 967c14b93a | |||
| 0e42fc657b | |||
| 2d7a91e368 | |||
| 072ab038fe | |||
| 6723c27d5e | |||
| f5fc700886 | |||
| 96a5ba0ceb | |||
| dfe5e72526 | |||
| d1cbebee84 | |||
| eac313feb8 | |||
| a71ed56cf2 | |||
| e3bc72e8d2 | |||
| 0fadfed7f6 | |||
| 031db78590 | |||
| 6fd32fb84b | |||
| 98e6464be6 | |||
| 52ce2afad2 | |||
| 28f89c9469 | |||
| 6f7de8da66 | |||
| 50caf2753d | |||
| 30c0caa04d | |||
| e53d8c14a9 | |||
| eca89358db | |||
| 1e58c71e67 | |||
| 43beaefc07 | |||
| f74faadace | |||
| 77aa622610 | |||
| 95aadd45ee | |||
| 89880016ea | |||
| beb91553ef | |||
| b2037c9575 | |||
| ce4cec5589 | |||
| e8a597ec6b | |||
| a663e2bd56 | |||
| 84bfd3cda2 | |||
| 3dfe96fa89 | |||
| 93cbcef93f | |||
| 5c758536a4 | |||
| bc5a9aa9f1 | |||
| b8ed77f6d5 | |||
| 6c25cd029f | |||
| 68d59eda69 | |||
| 2534d4c159 | |||
| 54252e768c | |||
| 7a2f263061 | |||
| 5ae6e02ce8 | |||
| fb9944ef14 | |||
| 190493179f | |||
| 4b1bc23622 | |||
| 2ad5b04a48 | |||
| afbbbcb4d1 | |||
| 820c3aec9d | |||
| eafbcd8389 | |||
| f0459b3f6e | |||
| ff93265890 | |||
| 320e282dc8 | |||
| d23c911c78 | |||
| 51df812a6c | |||
| a96aba57f7 | |||
| 36c71da4bb | |||
| b53fca9648 | |||
| 9fafb8bd2a | |||
| 41c7ba35a0 | |||
| 4cc5702da7 | |||
| 65f626f15e | |||
| 2abf6f67af | |||
| 72cde997ab | |||
| 8b27cb4690 | |||
| fb0cec0850 | |||
| a9a0bfdad0 | |||
| 00a752173d | |||
| 24caa9a4f4 | |||
| a1075e000a | |||
| c416902280 | |||
| a29d707183 | |||
| 69d08a85ac | |||
| 475ea08517 | |||
| b4c97c20d6 | |||
| f7da2c8185 | |||
| a1a630fc02 | |||
| 22ef117493 | |||
| 04b80d5ff8 | |||
| b764979c3b | |||
| a78c0491ef | |||
| 6082beb964 | |||
| 9d9a5fd9d2 | |||
| 4332f389a1 | |||
| a26b60e726 | |||
| 465d76cd08 | |||
| e0300148cf | |||
| 4442c6c625 | |||
| 289eb86d97 | |||
| 59724777e9 | |||
| 99e0b297b2 | |||
| 7036747042 | |||
| b9cd3e3f9f | |||
| c8e90b6887 | |||
| 56973b62f6 | |||
| 990a13e777 | |||
| cd37623746 | |||
| 193620e4e4 | |||
| 78d85e2440 | |||
| 9fa9689db9 | |||
| 81fc33183c | |||
| 2600011736 | |||
| e5c297eb7c | |||
| a3766aca6c | |||
| 857689dc22 | |||
| 2ed5917e96 | |||
| 84380bf333 | |||
| e7d517f264 | |||
| 40a9cc424e | |||
| b725bc2b5b | |||
| 50306c319e | |||
| c1e86c6897 | |||
| eb02142afc | |||
| 5f0526eef7 | |||
| cbdc55df8f | |||
| e0022b15c5 | |||
| 983edc53d1 | |||
| 2af1dcd24e | |||
| 2814a5f044 | |||
| 802fd87850 | |||
| 50a6a39632 | |||
| 49d0d3b35a | |||
| 8017f017f2 | |||
| 864798be7c | |||
| e20ef40e02 | |||
| 955f9021f7 | |||
| e006090be2 | |||
| 1c6bc56e08 | |||
| aa2da29b4c | |||
| 9e47d28660 | |||
| a5383c87c7 | |||
| 152a637e31 | |||
| 5d947000ca | |||
| bf4f378108 | |||
| 7f03228efa | |||
| 7e6fd1859e | |||
| 979c77c1b9 | |||
| 15c9603902 | |||
| 20dd8bcb3a | |||
| bec4b03a17 | |||
| 2686a162e7 | |||
| 7d9c982cf5 | |||
| d1c09ce74e | |||
| fe5f429039 | |||
| 8937571214 | |||
| f9591951cb | |||
| 956d2a25f2 | |||
| 365bec10a6 | |||
| c69c11b0fe | |||
| 40b8414f8a | |||
| 06cb735b68 | |||
| 985f32e06e | |||
| 36469b20b3 | |||
| 2173e4c6b8 | |||
| d7a35e651e | |||
| 4238c977f3 | |||
| 74e9a3204f | |||
| 7bf94ced84 | |||
| 1b3a40c22a | |||
| 28bef07e37 | |||
| 3b7cb57e7a | |||
| 3f069e6d22 | |||
| 12e7269591 | |||
| 0127cf0a6b | |||
| fd1522a2ca | |||
| c79ec065d3 | |||
| 19fe484049 | |||
| db3d831bc0 | |||
| d27e707044 | |||
| c142440068 | |||
| df6456f5d2 | |||
| 1578a2b8d1 | |||
| 4a43c152d5 | |||
| 0d1ee37272 | |||
| 5c0b97cd1e | |||
| 511e8d6074 | |||
| 826e4a7de2 | |||
| 1ac66b4ece | |||
| 8f58de9f4e | |||
| 611d997df9 | |||
| 3f66e3f1f1 | |||
| 6154fa5dcc | |||
| dd0914cd89 | |||
| b8000d9a64 | |||
| d94bd66c54 | |||
| 2ebd2018db | |||
| 04b41e16e1 | |||
| 7ad73f3c84 | |||
| 359698d54f | |||
| 26ea658f9c | |||
| a13be0530f | |||
| b018b11c57 | |||
| aac023351a | |||
| fd2fbea03f | |||
| f3bbdec77c | |||
| b54ace0272 | |||
| 55bc78d9cb | |||
| fabe027d54 | |||
| b4f4450573 | |||
| 990cc66600 | |||
| aa76147144 | |||
| ee46af08ca | |||
| c33de944ef | |||
| 79e5103b08 | |||
| 4797b213ee | |||
| c232042af5 | |||
| 252448f4a9 | |||
| ec4febe5e9 | |||
| 35ca0d90f7 | |||
| eae3d4689b | |||
| 67c9d4084c | |||
| fb52428219 | |||
| 23e620e168 | |||
| 324054b3db | |||
| d9dcbeef7b | |||
| 9b31d52d7e | |||
| 260d2d28ad | |||
| dd74bdda6a | |||
| 7486390da5 | |||
| eb32c99764 | |||
| 8305f16dae | |||
| a74ace3bbd | |||
| 96c8774e31 | |||
| 45895bddae | |||
| e29e443078 | |||
| f213aeb93a | |||
| b68b0f1583 | |||
| 52fc4c5c34 | |||
| d7f81c2f03 | |||
| 9b6e9aeb1c | |||
| ea3bb13661 | |||
| ec12679426 | |||
| 25a6d60e73 | |||
| 24c3a1ef30 | |||
| fbaef5392f | |||
| f52cd448e2 | |||
| 7f3eada0c1 | |||
| 181cd2fce1 | |||
| a5f600e73b | |||
| c96b3d8349 | |||
| 06c9d48126 | |||
| 0d29fbce45 | |||
| dc7fd81fc5 | |||
| f7503d1f20 | |||
| 56673fceaa | |||
| 3dccc56d16 | |||
| 75566e27fa | |||
| a8627ca89e | |||
| f49b6fef0f | |||
| 8b83b0c212 | |||
| 57292ab8de | |||
| a26b522356 | |||
| e1468e82e4 | |||
| 76102637dd | |||
| a55fac1bfa | |||
| 052a256422 | |||
| 3bfcb0c0d4 | |||
| e18292e352 | |||
| d71a1f5940 | |||
| d63f180030 | |||
| aebdb0b599 | |||
| 3a562005e5 | |||
| 320516fd8d | |||
| 971074777d | |||
| 22c3b5800d | |||
| b0a1197e22 | |||
| 79704e2f2b | |||
| 9f9faf5554 | |||
| a927334e06 | |||
| 74c4c5d2bc | |||
| 2bd8acc00f | |||
| 22ea843f60 | |||
| c7fb636ab5 | |||
| 59761e8eac | |||
| 4f8efa9c90 | |||
| 132ece89c1 | |||
| d0d5cc6841 | |||
| 498fd89239 | |||
| 0f99d3e83b | |||
| d0fa4d649b | |||
| 7d1d74e48a | |||
| 65e81eaae7 | |||
| 85cfdfd24f | |||
| ad5b47f2a5 | |||
| 238189fd9a | |||
| 5691a051a6 | |||
| d11ac9240c | |||
| 7c9f901a7a | |||
| 55fffef5cb | |||
| eb1c475fd9 | |||
| 7cfeea9641 | |||
| 59a7f02893 | |||
| ab4ef89c87 | |||
| 3d8b85dcf4 | |||
| 4b9867bd16 | |||
| b45dd84f0f | |||
| b21468f265 | |||
| 19c5bd5c25 | |||
| 64fd34ff1c | |||
| c0e7adcc42 | |||
| 0f48f21dde | |||
| 5f960cca71 | |||
| 630418cf02 | |||
| dd5ac3a2b4 | |||
| 0385672364 | |||
| c2a0f04cc0 | |||
| 9be9f4329b | |||
| 667972f05e | |||
| 0bbb1f0eba | |||
| a7219861f4 | |||
| 09376e8f98 | |||
| ac45a6b5ef | |||
|
|
ea732301fd | ||
| d21d1b6993 | |||
| 655c003969 | |||
| 23288e15e0 | |||
| cf3c35ffa3 | |||
| 85c37ba748 | |||
| 154dffeee4 | |||
| 2cea90c2c8 | |||
| 44b123307a | |||
| 7ecec70520 | |||
| 7605a826d6 | |||
| 3126991e8f | |||
| 7b58303cde | |||
| 58d302fc38 | |||
| af4b9b95e7 | |||
| dc56edf486 | |||
| 14dd2bb336 | |||
| 7a1499a0b3 | |||
| be8ccbd41a | |||
| 970618f561 | |||
| 8a3d9eec03 | |||
| 46de5cc0c9 | |||
| 0c2ac5d0a5 | |||
| c18b740f46 | |||
| 615abcc8e3 | |||
| 3d13fc6864 | |||
| 8244230268 | |||
| 3f48f11838 | |||
| e16ba2d096 | |||
| 874339348c | |||
| d1833d7b8d | |||
| b658e96425 | |||
| 0ab92d95ea | |||
| a9b480994a | |||
| 3bd5c064c3 | |||
| 6bee6b1ba7 | |||
| e11211fcc7 | |||
| d5a703026a | |||
| 44481fe107 | |||
| 57092e1b26 | |||
| b62a3b9d63 | |||
| 7fd65ede5f | |||
| d3e7938231 | |||
| 2afb59d4a2 | |||
| d54f2159d9 | |||
| e032fb666f | |||
| dca56831af | |||
| 937b70e1fd | |||
| 3b0c7d8388 | |||
| d5093110f7 | |||
| 25ddcef978 | |||
| 5c613df087 | |||
| 0aa44e70de | |||
| 2b69d9985e | |||
| 86f44c8bab | |||
| fa66ee14ee | |||
| 40ed36e0e2 | |||
| b7c8a9ece5 | |||
| 29dfb852c8 | |||
| 9de83c3436 | |||
| 9a0768a2d5 | |||
| 07aa82e7d9 | |||
| bc79951700 | |||
| 155a83cfdd | |||
| 1ffa7cdbcf | |||
| 0f2e1478a8 | |||
| 3efe31c91f | |||
| 22ebefc46a | |||
| 1f8646438d | |||
| 9ac7769dbc | |||
| 5f3cc1a3c6 | |||
| 7b882d6705 | |||
| 33aa01fc78 | |||
| 542bf9c189 | |||
| d39d599570 | |||
| 082e2fe2bd | |||
| 9600d1e7d1 | |||
| 6ab68df4a0 | |||
| 9ae8fa4055 | |||
| 52d40cb8a1 | |||
| 30e51dddd7 | |||
| ffbe78fdc1 | |||
| adae8888f8 | |||
| a3641828fc | |||
| b91d079787 | |||
| 9e2bb48f13 | |||
| 22495294fc | |||
| c83cfff556 | |||
| 35c3108db7 | |||
| 6155a8768f | |||
| 9d16889216 | |||
| 9fd46b9fd9 | |||
| 041dd4b314 | |||
| 13b2c448b5 | |||
| a41e209123 | |||
| c24c1f54be | |||
| 96076af037 | |||
| 1c9bce3b33 | |||
| ef048f77bb | |||
| 361ead1351 | |||
| f031c51ce4 | |||
| ce7ba83fff | |||
| eb692de59a | |||
| fe66dedecf | |||
| 79eea9ea25 | |||
| 83c25f20bb | |||
| 95c5ed6a32 | |||
| 3f0ebca4da | |||
| 03ec852648 | |||
| 6ccc33f762 | |||
| d1bc5c2fe5 | |||
| ee0d5c363c | |||
| 86d5706aac | |||
| 2ca199d98f | |||
| 562d7a603e | |||
| c078189f44 | |||
| d060b1f56c | |||
| d91d51a60a | |||
| 4677400b4f | |||
| f7bac335db | |||
| ad3e6e59f2 | |||
| cc38a45fd0 | |||
| 7320094a87 | |||
| f05691d911 | |||
| 3f9e40dbc8 | |||
| d399427d7f | |||
| bf53784135 | |||
| 988d48996a | |||
| 037873778d | |||
| 835e8be4dc | |||
| e384229d7c | |||
| efcb61781a | |||
| a20551db7a | |||
| 26a3191226 | |||
|
|
f6a390595a | ||
| e0ac364dff | |||
| 1b6c5a57e4 | |||
| 62a3180e53 | |||
| 91a4a2c2a4 | |||
| c40f448aa3 | |||
| 7bc89f4463 | |||
| 30b487e48c | |||
| db7ee08937 | |||
| d810340425 | |||
| 48192240c4 | |||
| 5c6b0085c8 | |||
| c6b088fdf9 | |||
| b1769a3326 | |||
| 61f9efe32d | |||
| f940278f4d | |||
|
|
0665286657 | ||
| eb0064e477 | |||
| 955c06f5aa | |||
| 1ee2a1189d | |||
| c8e49ba49c | |||
| aeb7939c6e | |||
| 910330c7dd | |||
| 1315d6368c | |||
| c9e2350155 | |||
| 35f5b382fa | |||
| a530b98fe2 | |||
| 26b93442b1 | |||
| 1142033d57 | |||
| 808304303a | |||
| 2b298b7c76 | |||
| 3231063bc2 | |||
| 212c5410e1 | |||
| 34a90bf218 | |||
| 56f786cd2b | |||
| cff787f541 | |||
| 55ec88e11f | |||
| b718212702 | |||
| c1dbde2fb7 | |||
| 2eac3f8b0b | |||
| 05cc7767bf | |||
| 7c897a7e12 | |||
| 06655e2378 | |||
| aad927fb96 | |||
| 533fdab150 | |||
| 93eb6da68d | |||
| cc89d45690 | |||
| f993913438 | |||
| 158da60922 | |||
| bb0be1dadf | |||
| c3379f2796 | |||
| 531e1f53fd | |||
| 62f09f738f | |||
| 79d5218b16 | |||
| a068516aa0 | |||
| 9f92bc710c | |||
| 716cc0eb58 | |||
| c3bb2179b1 | |||
| a82e8df06d | |||
| 40dce90aa0 | |||
| 37e59bb8a5 | |||
| 982c124d3b | |||
| 6ebb915c68 | |||
| 4ee8fd2400 | |||
| 19b40d50c1 | |||
| 6b14a4bb24 | |||
| 25790d1c45 | |||
| 4261a35ca7 | |||
| 66e2783d97 | |||
| 55bf78497f | |||
| ac35ce6c47 | |||
| 3b443aafc5 | |||
| a2ab077325 | |||
| 4adb0c953b | |||
| 72a78ba27b | |||
| e4cdd25922 | |||
| 24e2637408 | |||
| b538eb1929 | |||
| 4e40e8b1db | |||
| e6aef415f5 | |||
| 0f8f18943b | |||
| f0accc2e33 | |||
| 14ea157b7a | |||
| 686b0a167d | |||
| bde47e1251 | |||
| 9aaa2e5926 | |||
| ad74d7bdeb | |||
| ab42eeb370 | |||
| a25fc2e7e8 | |||
| ca40db79f7 | |||
| fad2f82b6b | |||
| 1b68387865 | |||
| b2ef15d45c | |||
| 998e69085e | |||
| 77d2786937 | |||
| b7814ac2a4 | |||
| 516410a649 | |||
| 3018048140 | |||
| 8e4044dffb | |||
| 4123845783 | |||
| fd9dcc182f | |||
| a3a6fbcaa4 | |||
| d9e4ccfca8 | |||
| ca288a088a | |||
| a63e73d692 | |||
| 5eda87b1ef | |||
| 2ee1bd3658 | |||
| cd689ea1a8 | |||
| 3ca5db394a | |||
| 5ee6d7ba78 | |||
| d15a44e7d7 | |||
| 4bb557e055 | |||
| f57466b243 | |||
| bdceb6d774 | |||
| fadf6a3d21 | |||
| 3cd03499c9 | |||
| 9406088f1f | |||
| a8f650d530 | |||
| caffbb6c45 | |||
| 4a6a65c5d2 | |||
| b50acd3b74 | |||
| 83f6bce923 | |||
| 7c4f377e2b | |||
| 8851a7149f | |||
| ced5089caf | |||
| ce53c94ea3 | |||
| 5cfcbacb80 | |||
| 5acf5e5297 | |||
| 9337ee292c | |||
| 3de48f002c | |||
| dad736b0d9 | |||
| 7b2e88d089 | |||
| 0db1bd50fd | |||
| 0fcc9b0d7e | |||
| 0349225ede | |||
| 5ee55b298f | |||
| 9877f3883a | |||
| 7cfc24cf39 | |||
| 9025581db8 | |||
| 731e33a986 | |||
| 336e7a7d41 | |||
| 3906ef119b | |||
| 4de4642c23 | |||
| 56eee105d5 | |||
| d7e530b2dd | |||
| 54855e2651 | |||
| 7de134683d | |||
| 1d0030e34f | |||
| ccc30a8b7a | |||
| 9961aba160 | |||
| 5414c0b232 | |||
| 584e146af0 | |||
| e035128c30 | |||
| 03f5c8d90f | |||
| ff75f142b3 | |||
| 094779a4fd | |||
| cfa84faab4 | |||
| c18d7c56c1 | |||
| 127fea1dfd | |||
| ebe1069a7f | |||
| 9f42f92c2e | |||
| c2542472e7 | |||
| 2fe0a8c3f0 | |||
| e97427c7d7 | |||
| d1e586bb39 | |||
| 9e2c7c2565 | |||
| 29deea73d4 | |||
| b1e30a603c | |||
| afc3c94b04 | |||
| ac4d3d8ad0 | |||
| 2b939f417c | |||
| 57236d814d | |||
| 839eaa6db9 | |||
| 78ccd558aa | |||
| a5a9db9669 | |||
| 0b70a52177 | |||
| f9105577ce | |||
| a7e06bef91 | |||
| 0bed3aa096 | |||
| 305c018d39 | |||
| 9404df9a1d | |||
| a2894791e2 | |||
| c671fb9bf6 | |||
| 5f22142a6d | |||
| cc1a389eba | |||
| e6174a4456 | |||
| 145b5559ef | |||
| 09751208a1 | |||
| 22ff92e3a7 | |||
| 8f7f1a35d5 | |||
| 1452d6f340 | |||
| 6080c12a7a | |||
| d16231d338 | |||
| 14e2e2da3e | |||
| 0fba7849b4 | |||
| 6cf5cd1697 | |||
| be39577466 | |||
| fe56acd818 | |||
| e84dfcacd1 | |||
| e639801e37 | |||
| 636ff8c4a9 | |||
| 5da4cca80f | |||
| c5cc91b075 | |||
| e40a2bad16 | |||
| 4367fa4440 | |||
| a2332451b0 | |||
| f6377a6f3a | |||
| ba3846b374 | |||
| 42651ec256 | |||
| d248204bf9 | |||
| 8b84f1c02c | |||
| 3904fe784e | |||
| e5a68fd805 | |||
| 32d39cdc1c | |||
| 9188461b1f | |||
| 44f6ef4a0b | |||
| b0ae6b0a3d | |||
|
|
b6268b3fab | ||
| ba436e5dcc | |||
| dd01a075d8 | |||
| 89ebab5134 | |||
| a135c7e8fd | |||
| 1f8b83fc8c | |||
| c378d683db | |||
| 0d1a0cf130 | |||
| 4ec4b5b10b | |||
| d60383211c | |||
| 900909aa5b | |||
| e33e8ad6f6 | |||
| 7369eb469e | |||
| c3ebec38d7 | |||
| 7f1d0b127d | |||
| cb8dc3cfa5 | |||
| ac68b4856a | |||
| 26348d9e19 | |||
| d8805a791e | |||
| 8fdc7f8600 | |||
| 5681990112 | |||
| ab36df319b | |||
| f48704804a | |||
| 05e9315ef2 | |||
|
|
a885d15341 | ||
| ad69e5c0e2 | |||
| 824ab331b4 | |||
| 1f21e22dce |
23
.env.example
23
.env.example
@@ -38,11 +38,24 @@ MAIL_ENCRYPTION=null
|
|||||||
MAIL_FROM_ADDRESS="hello@example.com"
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_PUBLIC_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_PUBLIC_SECRET_ACCESS_KEY=
|
||||||
AWS_DEFAULT_REGION=us-east-1
|
AWS_PUBLIC_DEFAULT_REGION="us-west-002"
|
||||||
AWS_BUCKET=
|
AWS_PUBLIC_BUCKET=
|
||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_PUBLIC_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
AWS_PUBLIC_ENDPOINT=
|
||||||
|
AWS_PUBLIC_URL=
|
||||||
|
|
||||||
|
AWS_PRIVATE_ACCESS_KEY_ID=
|
||||||
|
AWS_PRIVATE_SECRET_ACCESS_KEY=
|
||||||
|
AWS_PRIVATE_DEFAULT_REGION="us-west-002"
|
||||||
|
AWS_PRIVATE_BUCKET=
|
||||||
|
AWS_PRIVATE_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
AWS_PRIVATE_ENDPOINT=
|
||||||
|
AWS_PRIVATE_URL=
|
||||||
|
|
||||||
|
CLOUDFLARE_ZONE_ID=
|
||||||
|
CLOUDFLARE_API_KEY=
|
||||||
|
|
||||||
PUSHER_APP_ID=
|
PUSHER_APP_ID=
|
||||||
PUSHER_APP_KEY=
|
PUSHER_APP_KEY=
|
||||||
|
|||||||
59
.env.testing
Normal file
59
.env.testing
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=base64:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://127.0.0.1
|
||||||
|
APP_URL_API="${APP_URL}/api/"
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
DB_DATABASE=:memory:
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=array
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_HOST=null
|
||||||
|
MAIL_PORT=null
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_HOST=
|
||||||
|
PUSHER_PORT=443
|
||||||
|
PUSHER_SCHEME=https
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||||
|
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||||
|
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||||
|
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
||||||
|
CONTACT_ADDRESS="hello@stemmechanics.com.au"
|
||||||
|
CONTACT_SUBJECT="Contact from website"
|
||||||
|
|
||||||
|
STORAGE_LOCAL_URL="${APP_URL}/api/media/%ID%/download"
|
||||||
|
STORAGE_PUBLIC_URL="${APP_URL}/uploads/%NAME%"
|
||||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.github/
|
||||||
|
.vscode/
|
||||||
|
vendor/
|
||||||
@@ -4,14 +4,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:vue/vue3-recommended",
|
"plugin:vue/vue3-strongly-recommended",
|
||||||
"prettier",
|
"prettier",
|
||||||
"plugin:jsdoc/recommended",
|
"plugin:jsdoc/recommended",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
"vue/multi-word-component-names": "off",
|
"vue/multi-word-component-names": "off",
|
||||||
indent: ["error", 4],
|
indent: ["off", 4, { ignoredNodes: ["ConditionalExpression"] }],
|
||||||
|
"@typescript-eslint/no-inferrable-types": "off",
|
||||||
},
|
},
|
||||||
plugins: ["jsdoc", "@typescript-eslint"],
|
plugins: ["jsdoc", "@typescript-eslint"],
|
||||||
parser: "vue-eslint-parser",
|
parser: "vue-eslint-parser",
|
||||||
|
|||||||
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "composer"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
42
.github/workflows/laravel.yml
vendored
Normal file
42
.github/workflows/laravel.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: Laravel
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
laravel-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.2"
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Copy .env
|
||||||
|
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: composer install -q --no-interaction --no-progress --prefer-dist
|
||||||
|
- name: Generate key
|
||||||
|
run: php artisan key:generate
|
||||||
|
- name: Directory Permissions
|
||||||
|
run: chmod -R 777 storage bootstrap/cache
|
||||||
|
- name: Create Database
|
||||||
|
run: |
|
||||||
|
mkdir -p database
|
||||||
|
touch database/database.sqlite
|
||||||
|
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||||
|
env:
|
||||||
|
DB_CONNECTION: sqlite
|
||||||
|
DB_DATABASE: database/database.sqlite
|
||||||
|
run: vendor/bin/phpunit
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: "16.x"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Run Vue tests
|
||||||
|
env:
|
||||||
|
LARAVEL_BYPASS_ENV_CHECK: "1"
|
||||||
|
run: npm run test
|
||||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -26,7 +26,7 @@ storage/*.key
|
|||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
Homestead.json
|
Homestead.json
|
||||||
/.vagrant
|
/.vagrant
|
||||||
.phpunit.result.cache
|
/.phpunit.cache
|
||||||
|
|
||||||
### macOS ###
|
### macOS ###
|
||||||
# General
|
# General
|
||||||
@@ -238,3 +238,21 @@ dist/
|
|||||||
/public/uploads
|
/public/uploads
|
||||||
/public/build
|
/public/build
|
||||||
*.key
|
*.key
|
||||||
|
|
||||||
|
### TinyMCE ###
|
||||||
|
/public/tinymce
|
||||||
|
!/public/tinymce/skins/ui/stemmech/
|
||||||
|
|
||||||
|
### Synk ###
|
||||||
|
.dccache
|
||||||
|
|
||||||
|
### TempCodeRunner ###
|
||||||
|
tempCodeRunnerFile.*
|
||||||
|
|
||||||
|
### PHPUnit ###
|
||||||
|
.phpunit.result.cache
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
### Codesniffer ###
|
||||||
|
phpcs.phar
|
||||||
|
phpcbf.phar
|
||||||
|
|||||||
14
.ls-lint.yml
Normal file
14
.ls-lint.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
ls:
|
||||||
|
resources/js/store:
|
||||||
|
.ts: pascalcase
|
||||||
|
"*":
|
||||||
|
.js: camelcase
|
||||||
|
.ts: camelcase
|
||||||
|
.vue: pascalcase
|
||||||
|
.dir: snakecase
|
||||||
|
.type.ts: camelcase
|
||||||
|
ignore:
|
||||||
|
- node_modules
|
||||||
|
- vendor
|
||||||
|
- public/build
|
||||||
|
- public/tinymce
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"bracketSameLine": true,
|
"bracketSameLine": true,
|
||||||
"tabWidth": 4
|
"tabWidth": 4,
|
||||||
|
"htmlWhitespaceSensitivity": "css"
|
||||||
}
|
}
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -2,7 +2,7 @@
|
|||||||
"editor.formatOnType": true,
|
"editor.formatOnType": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
@@ -14,5 +14,6 @@
|
|||||||
"[php]": {
|
"[php]": {
|
||||||
// "editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
|
// "editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
|
||||||
"editor.defaultFormatter": "wongjn.php-sniffer"
|
"editor.defaultFormatter": "wongjn.php-sniffer"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": ["TIMESTAMPDIFF"]
|
||||||
}
|
}
|
||||||
|
|||||||
23355
_ide_helper.php
Normal file
23355
_ide_helper.php
Normal file
File diff suppressed because it is too large
Load Diff
71
app/Conductors/AnalyticsConductor.php
Normal file
71
app/Conductors/AnalyticsConductor.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AnalyticsConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\AnalyticsSession::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default includes to include in a request.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $includes = ['requests.type','requests.path'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/analytics') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/analytics') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/analytics') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
181
app/Conductors/ArticleConductor.php
Normal file
181
app/Conductors/ArticleConductor.php
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use App\Models\Media;
|
||||||
|
use App\Models\User;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\InvalidCastException;
|
||||||
|
use Illuminate\Database\Eloquent\MissingAttributeException;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use LogicException;
|
||||||
|
|
||||||
|
class ArticleConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\Article::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = '-publish_at';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The included fields
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $includes = ['attachments', 'user', 'gallery'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder): void
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/articles') === false) {
|
||||||
|
$builder
|
||||||
|
->where('publish_at', '<=', now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model): bool
|
||||||
|
{
|
||||||
|
if (Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/articles') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/articles') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/articles') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/articles') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the final model data
|
||||||
|
*
|
||||||
|
* @param array $data The model data to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transformFinal(array $data): array
|
||||||
|
{
|
||||||
|
unset($data['user_id']);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include Attachments Field.
|
||||||
|
*
|
||||||
|
* @param Model $model Them model.
|
||||||
|
* @return mixed The model result.
|
||||||
|
*/
|
||||||
|
public function includeAttachments(Model $model)
|
||||||
|
{
|
||||||
|
return $model->getAttachments()->map(function ($attachment) {
|
||||||
|
return MediaConductor::includeModel(request(), 'attachments', $attachment->media);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include Gallery Field.
|
||||||
|
*
|
||||||
|
* @param Model $model Them model.
|
||||||
|
* @return mixed The model result.
|
||||||
|
*/
|
||||||
|
public function includeGallery(Model $model)
|
||||||
|
{
|
||||||
|
return $model->getGallery()->map(function ($item) {
|
||||||
|
return MediaConductor::includeModel(request(), 'gallery', $item->media);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include User Field.
|
||||||
|
*
|
||||||
|
* @param Model $model Them model.
|
||||||
|
* @return mixed The model result.
|
||||||
|
*/
|
||||||
|
public function includeUser(Model $model)
|
||||||
|
{
|
||||||
|
$cacheKey = "user:{$model['user_id']}";
|
||||||
|
$user = Cache::remember($cacheKey, now()->addDays(28), function () use ($model) {
|
||||||
|
return User::find($model['user_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
return UserConductor::includeModel(request(), 'user', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the Hero field.
|
||||||
|
*
|
||||||
|
* @param mixed $value The current value.
|
||||||
|
* @return array|null The new value.
|
||||||
|
*/
|
||||||
|
public function transformHero(mixed $value): array|null
|
||||||
|
{
|
||||||
|
$cacheKey = "media:{$value}";
|
||||||
|
$media = Cache::remember($cacheKey, now()->addDays(28), function () use ($value) {
|
||||||
|
return Media::find($value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return MediaConductor::includeModel(request(), 'hero', $media);
|
||||||
|
}
|
||||||
|
}
|
||||||
1055
app/Conductors/Conductor.php
Normal file
1055
app/Conductors/Conductor.php
Normal file
File diff suppressed because it is too large
Load Diff
140
app/Conductors/EventConductor.php
Normal file
140
app/Conductors/EventConductor.php
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use App\Models\Media;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\InvalidCastException;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class EventConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\Event::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = '-start_at';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The included fields
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $includes = ['attachments'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder): void
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||||
|
$builder
|
||||||
|
->where('status', '!=', 'draft')
|
||||||
|
->where('publish_at', '<=', now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model): bool
|
||||||
|
{
|
||||||
|
if (strtolower($model->status) === 'draft' || Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include Attachments Field.
|
||||||
|
*
|
||||||
|
* @param Model $model Them model.
|
||||||
|
* @return mixed The model result.
|
||||||
|
*/
|
||||||
|
public function includeAttachments(Model $model)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $model->getAttachments()->map(function ($attachment) use ($user) {
|
||||||
|
if ($attachment->private === false || ($user !== null && $user->hasPermission('admin/events') === true)) {
|
||||||
|
return MediaConductor::includeModel(request(), 'attachments', $attachment->media);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the Hero field.
|
||||||
|
*
|
||||||
|
* @param mixed $value The current value.
|
||||||
|
* @return array|null The new value.
|
||||||
|
*/
|
||||||
|
public function transformHero(mixed $value): array|null
|
||||||
|
{
|
||||||
|
$cacheKey = "media:{$value}";
|
||||||
|
$media = Cache::remember($cacheKey, now()->addDays(28), function () use ($value) {
|
||||||
|
return Media::find($value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return MediaConductor::includeModel(request(), 'hero', $media);
|
||||||
|
}
|
||||||
|
}
|
||||||
184
app/Conductors/MediaConductor.php
Normal file
184
app/Conductors/MediaConductor.php
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class MediaConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\Media::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = 'created_at';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The included fields
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $includes = ['user', 'jobs'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default filters to use in a request.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
// protected $defaultFilters = [
|
||||||
|
// 'status' => 'OK'
|
||||||
|
// ];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of model fields visible to the current user.
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return array The array of field names.
|
||||||
|
*/
|
||||||
|
public function fields(Model $model): array
|
||||||
|
{
|
||||||
|
$fields = parent::fields($model);
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/media') === false) {
|
||||||
|
$fields = arrayRemoveItem($fields, ['security_data', 'storage']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder): void
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null) {
|
||||||
|
$builder->where('security_type', '')
|
||||||
|
->orWhere('security_type', 'password');
|
||||||
|
} else {
|
||||||
|
$builder->where(function ($query) use ($user) {
|
||||||
|
$query->where('security_type', '')
|
||||||
|
->orWhere('security_type', 'password')
|
||||||
|
->orWhere(function ($subquery) use ($user) {
|
||||||
|
$subquery->where('security_type', 'permission')
|
||||||
|
->whereIn('security_data', $user->permissions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model): bool
|
||||||
|
{
|
||||||
|
if (strcasecmp('permission', $model->security_type) === 0) {
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission($model->security_data) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif ($model->security_type !== '' && strcasecmp('password', $model->security_type) !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && (strcasecmp($model->user_id, $user->id) === 0 ||
|
||||||
|
$user->hasPermission('admin/media') === true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && ($model->user_id === $user->id || $user->hasPermission('admin/media') === true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the final model data
|
||||||
|
*
|
||||||
|
* @param array $data The model data to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transformFinal(array $data): array
|
||||||
|
{
|
||||||
|
unset($data['user_id']);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include User Field.
|
||||||
|
*
|
||||||
|
* @param Model $model Them model.
|
||||||
|
* @return mixed The model result.
|
||||||
|
*/
|
||||||
|
public function includeUser(Model $model)
|
||||||
|
{
|
||||||
|
$user = Cache::remember("user:{$model['user_id']}", now()->addDays(28), function () use ($model) {
|
||||||
|
return User::find($model['user_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
return UserConductor::includeModel(request(), 'user', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include job models in Media
|
||||||
|
*
|
||||||
|
* @param Model $model The reference model.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function includeJobs(Model $model)
|
||||||
|
{
|
||||||
|
$jobs = $model->jobs()
|
||||||
|
->select(['id','created_at','updated_at','user_id','status','status_text','progress'])
|
||||||
|
->orderBy('created_at', 'desc')->get();
|
||||||
|
return $jobs;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
app/Conductors/MediaJobConductor.php
Normal file
76
app/Conductors/MediaJobConductor.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class MediaJobConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\MediaJob::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = 'created_at';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The included fields
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $includes = ['user'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the final model data
|
||||||
|
*
|
||||||
|
* @param array $data The model data to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transformFinal(array $data): array
|
||||||
|
{
|
||||||
|
unset($data['user_id']);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Conductors/ShortlinkConductor.php
Normal file
59
app/Conductors/ShortlinkConductor.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class ShortlinkConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\Shortlink::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = 'created_at';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable(): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/shortlinks') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/shortlinks') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/shortlinks') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Conductors/SubscriptionConductor.php
Normal file
45
app/Conductors/SubscriptionConductor.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class SubscriptionConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\Subscription::class;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && (
|
||||||
|
(strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) ||
|
||||||
|
$user->hasPermission('admin/subscriptions') === true
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 &&
|
||||||
|
$user->email_verified_at !== null) || $user->hasPermission('admin/subscriptions') === true));
|
||||||
|
}
|
||||||
|
}
|
||||||
89
app/Conductors/UserConductor.php
Normal file
89
app/Conductors/UserConductor.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class UserConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = \App\Models\User::class;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the visible API fields.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return string[] The fields visible.
|
||||||
|
*/
|
||||||
|
public function fields(Model $model): array
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/users') === false) {
|
||||||
|
return ['id', 'display_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::fields($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the passed Model to an array
|
||||||
|
*
|
||||||
|
* @param Model $model The model to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transform(Model $model): array
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$data = $model->toArray();
|
||||||
|
$limit = $this->fields($model);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$user === null || (
|
||||||
|
$user->hasPermission('admin/users') === false && strcasecmp($user->id, $model->id) !== 0
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$limit = ['id', 'display_name'];
|
||||||
|
} else {
|
||||||
|
$data['permissions'] = $user->permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = arrayLimitKeys($data, $limit);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user !== null) {
|
||||||
|
return ($user->hasPermission('admin/users') === true || strcasecmp($user->id, $model->id) === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model): bool
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/users') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Console/Commands/CleanupTempFiles.php
Normal file
46
app/Console/Commands/CleanupTempFiles.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class CleanupTempFiles extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:cleanup-temp-files';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Delete temporary files that are older that 1 day';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$keepTime = (1 * 24 * 60 * 60); // 1 Day
|
||||||
|
$currentTimeStamp = time();
|
||||||
|
$deletedFileCount = 0;
|
||||||
|
|
||||||
|
foreach (glob(storage_path('app/tmp/*')) as $filename) {
|
||||||
|
$fileModifiedTimeStamp = filemtime($filename);
|
||||||
|
if (($currentTimeStamp - $fileModifiedTimeStamp) > $keepTime) {
|
||||||
|
unlink($filename);
|
||||||
|
$deletedFileCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->comment('Deleted ' . $deletedFileCount . ' files');
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Console/Commands/RemoveStaleMediaJobs.php
Normal file
42
app/Console/Commands/RemoveStaleMediaJobs.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class RemoveStaleMediaJobs extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:remove-stale-media-jobs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Remove media_jobs that have not been modified for 48 hours';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$threshold = now()->subHours(48);
|
||||||
|
|
||||||
|
$staleJobs = MediaJob::where('updated_at', '<=', $threshold)->get();
|
||||||
|
|
||||||
|
foreach ($staleJobs as $job) {
|
||||||
|
$job->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info(count($staleJobs) . ' stale media_jobs removed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,11 @@ class Kernel extends ConsoleKernel
|
|||||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule The schedule.
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule The schedule.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
// $schedule->command('inspire')->hourly();
|
// $schedule->command('inspire')->hourly();
|
||||||
|
$schedule->command('app:cleanup-temp-files')->everyThirtyMinutes();
|
||||||
|
$schedule->command('app:remove-stale-media-jobs')->everyThirtyMinutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,7 +25,7 @@ class Kernel extends ConsoleKernel
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function commands()
|
protected function commands(): void
|
||||||
{
|
{
|
||||||
$this->load(__DIR__ . '/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|
||||||
|
|||||||
169
app/Enum/CurlErrorCodes.php
Normal file
169
app/Enum/CurlErrorCodes.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enum;
|
||||||
|
|
||||||
|
class CurlErrorCodes extends Enum
|
||||||
|
{
|
||||||
|
public const CURLE_UNSUPPORTED_PROTOCOL = 1;
|
||||||
|
public const CURLE_FAILED_INIT = 2;
|
||||||
|
public const CURLE_URL_MALFORMAT = 3;
|
||||||
|
public const CURLE_URL_MALFORMAT_USER = 4;
|
||||||
|
public const CURLE_COULDNT_RESOLVE_PROXY = 5;
|
||||||
|
public const CURLE_COULDNT_RESOLVE_HOST = 6;
|
||||||
|
public const CURLE_COULDNT_CONNECT = 7;
|
||||||
|
public const CURLE_FTP_WEIRD_SERVER_REPLY = 8;
|
||||||
|
public const CURLE_REMOTE_ACCESS_DENIED = 9;
|
||||||
|
public const CURLE_FTP_WEIRD_PASS_REPLY = 11;
|
||||||
|
public const CURLE_FTP_WEIRD_PASV_REPLY = 13;
|
||||||
|
public const CURLE_FTP_WEIRD_227_FORMAT = 14;
|
||||||
|
public const CURLE_FTP_CANT_GET_HOST = 15;
|
||||||
|
public const CURLE_FTP_COULDNT_SET_TYPE = 17;
|
||||||
|
public const CURLE_PARTIAL_FILE = 18;
|
||||||
|
public const CURLE_FTP_COULDNT_RETR_FILE = 19;
|
||||||
|
public const CURLE_QUOTE_ERROR = 21;
|
||||||
|
public const CURLE_HTTP_RETURNED_ERROR = 22;
|
||||||
|
public const CURLE_WRITE_ERROR = 23;
|
||||||
|
public const CURLE_UPLOAD_FAILED = 25;
|
||||||
|
public const CURLE_READ_ERROR = 26;
|
||||||
|
public const CURLE_OUT_OF_MEMORY = 27;
|
||||||
|
public const CURLE_OPERATION_TIMEDOUT = 28;
|
||||||
|
public const CURLE_FTP_PORT_FAILED = 30;
|
||||||
|
public const CURLE_FTP_COULDNT_USE_REST = 31;
|
||||||
|
public const CURLE_RANGE_ERROR = 33;
|
||||||
|
public const CURLE_HTTP_POST_ERROR = 34;
|
||||||
|
public const CURLE_SSL_CONNECT_ERROR = 35;
|
||||||
|
public const CURLE_BAD_DOWNLOAD_RESUME = 36;
|
||||||
|
public const CURLE_FILE_COULDNT_READ_FILE = 37;
|
||||||
|
public const CURLE_LDAP_CANNOT_BIND = 38;
|
||||||
|
public const CURLE_LDAP_SEARCH_FAILED = 39;
|
||||||
|
public const CURLE_FUNCTION_NOT_FOUND = 41;
|
||||||
|
public const CURLE_ABORTED_BY_CALLBACK = 42;
|
||||||
|
public const CURLE_BAD_FUNCTION_ARGUMENT = 43;
|
||||||
|
public const CURLE_INTERFACE_FAILED = 45;
|
||||||
|
public const CURLE_TOO_MANY_REDIRECTS = 47;
|
||||||
|
public const CURLE_UNKNOWN_TELNET_OPTION = 48;
|
||||||
|
public const CURLE_TELNET_OPTION_SYNTAX = 49;
|
||||||
|
public const CURLE_PEER_FAILED_VERIFICATION = 51;
|
||||||
|
public const CURLE_GOT_NOTHING = 52;
|
||||||
|
public const CURLE_SSL_ENGINE_NOTFOUND = 53;
|
||||||
|
public const CURLE_SSL_ENGINE_SETFAILED = 54;
|
||||||
|
public const CURLE_SEND_ERROR = 55;
|
||||||
|
public const CURLE_RECV_ERROR = 56;
|
||||||
|
public const CURLE_SSL_CERTPROBLEM = 58;
|
||||||
|
public const CURLE_SSL_CIPHER = 59;
|
||||||
|
public const CURLE_SSL_CACERT = 60;
|
||||||
|
public const CURLE_BAD_CONTENT_ENCODING = 61;
|
||||||
|
public const CURLE_LDAP_INVALID_URL = 62;
|
||||||
|
public const CURLE_FILESIZE_EXCEEDED = 63;
|
||||||
|
public const CURLE_USE_SSL_FAILED = 64;
|
||||||
|
public const CURLE_SEND_FAIL_REWIND = 65;
|
||||||
|
public const CURLE_SSL_ENGINE_INITFAILED = 66;
|
||||||
|
public const CURLE_LOGIN_DENIED = 67;
|
||||||
|
public const CURLE_TFTP_NOTFOUND = 68;
|
||||||
|
public const CURLE_TFTP_PERM = 69;
|
||||||
|
public const CURLE_REMOTE_DISK_FULL = 70;
|
||||||
|
public const CURLE_TFTP_ILLEGAL = 71;
|
||||||
|
public const CURLE_TFTP_UNKNOWNID = 72;
|
||||||
|
public const CURLE_REMOTE_FILE_EXISTS = 73;
|
||||||
|
public const CURLE_TFTP_NOSUCHUSER = 74;
|
||||||
|
public const CURLE_CONV_FAILED = 75;
|
||||||
|
public const CURLE_CONV_REQD = 76;
|
||||||
|
public const CURLE_SSL_CACERT_BADFILE = 77;
|
||||||
|
public const CURLE_REMOTE_FILE_NOT_FOUND = 78;
|
||||||
|
public const CURLE_SSH = 79;
|
||||||
|
public const CURLE_SSL_SHUTDOWN_FAILED = 80;
|
||||||
|
public const CURLE_AGAIN = 81;
|
||||||
|
public const CURLE_SSL_CRL_BADFILE = 82;
|
||||||
|
public const CURLE_SSL_ISSUER_ERROR = 83;
|
||||||
|
public const CURLE_FTP_PRET_FAILED = 84;
|
||||||
|
public const CURLE_RTSP_CSEQ_ERROR = 85;
|
||||||
|
public const CURLE_RTSP_SESSION_ERROR = 86;
|
||||||
|
public const CURLE_FTP_BAD_FILE_LIST = 87;
|
||||||
|
public const CURLE_CHUNK_FAILED = 88;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Curl Error messages
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public static $messages = [
|
||||||
|
1 => 'Unsupported protocol.',
|
||||||
|
2 => 'Failed initalization.',
|
||||||
|
3 => 'Invalid URL format.',
|
||||||
|
4 => 'CURLE_URL_MALFORMAT_USER.',
|
||||||
|
5 => 'Could not resolve proxy.',
|
||||||
|
6 => 'Could not resolve host.',
|
||||||
|
7 => 'Could not connect to host.',
|
||||||
|
8 => 'Invalid reply from FTP server.',
|
||||||
|
9 => 'Access denied on host.',
|
||||||
|
11 => 'Invalid pass reply from FTP server.',
|
||||||
|
13 => 'Invalid pasv reply from FTP server.',
|
||||||
|
14 => 'Invalid 227 format from FTP server.',
|
||||||
|
15 => 'Could not get FTP host.',
|
||||||
|
17 => 'Could not set type for FTP transfer.',
|
||||||
|
18 => 'Invalid partial size.',
|
||||||
|
19 => 'Could not retrieve file from FTP server.',
|
||||||
|
21 => 'Quote error.',
|
||||||
|
22 => 'HTTP server returned error.',
|
||||||
|
23 => 'File write error.',
|
||||||
|
25 => 'Upload file error.',
|
||||||
|
26 => 'File read error.',
|
||||||
|
27 => 'Out of memory.',
|
||||||
|
28 => 'File transfer timed out.',
|
||||||
|
30 => 'Invalid port for FTP server.',
|
||||||
|
31 => 'Could not use rest for FTP server.',
|
||||||
|
33 => 'File range error.',
|
||||||
|
34 => 'Invalid POST for HTTP server.',
|
||||||
|
35 => 'SSL connectio error.',
|
||||||
|
36 => 'Invalid resume download.',
|
||||||
|
37 => 'Could not read file.',
|
||||||
|
38 => 'Could not bind to LDAP.',
|
||||||
|
39 => 'LDAP search failed.',
|
||||||
|
41 => 'Function not found.',
|
||||||
|
42 => 'Aborted by callback.',
|
||||||
|
43 => 'Bad function argument.',
|
||||||
|
45 => 'Interface failed.',
|
||||||
|
47 => 'Too many redirects.',
|
||||||
|
48 => 'Unknown telnet option.',
|
||||||
|
49 => 'Telnet option syntax invalid.',
|
||||||
|
51 => 'Peer failed verification.',
|
||||||
|
52 => 'Did not receive any data.',
|
||||||
|
53 => 'SSL engine was not found.',
|
||||||
|
54 => 'SSL engine failed.',
|
||||||
|
55 => 'Send data error.',
|
||||||
|
56 => 'Receive data error.',
|
||||||
|
58 => 'SSL certificate error.',
|
||||||
|
59 => 'SSL cipher error.',
|
||||||
|
60 => 'SSL CACertificate failed.',
|
||||||
|
61 => 'Invalid content encoding.',
|
||||||
|
62 => 'Invalid LDAP url.',
|
||||||
|
63 => 'Filesize exceeded.',
|
||||||
|
64 => 'SSL Failed.',
|
||||||
|
65 => 'CURLE_SEND_FAIL_REWIND.',
|
||||||
|
66 => 'SSL engine initalization failed.',
|
||||||
|
67 => 'CURLE_LOGIN_DENIED.',
|
||||||
|
68 => 'CURLE_TFTP_NOTFOUND.',
|
||||||
|
69 => 'CURLE_TFTP_PERM.',
|
||||||
|
70 => 'CURLE_REMOTE_DISK_FULL.',
|
||||||
|
71 => 'CURLE_TFTP_ILLEGAL.',
|
||||||
|
72 => 'CURLE_TFTP_UNKNOWNID.',
|
||||||
|
73 => 'Remote file already exists.',
|
||||||
|
74 => 'No such user on FTP server.',
|
||||||
|
75 => 'Conversion failed.',
|
||||||
|
76 => 'Conversion required.',
|
||||||
|
77 => 'SSL CACertificate bad file.',
|
||||||
|
78 => 'Remove file not found.',
|
||||||
|
79 => 'SSH error.',
|
||||||
|
80 => 'SSL Shutdown failed.',
|
||||||
|
81 => 'Again.',
|
||||||
|
82 => 'SSL bad CRL file.',
|
||||||
|
83 => 'SSL issuer error.',
|
||||||
|
84 => 'FTP pret failed.',
|
||||||
|
85 => 'CURLE_RTSP_CSEQ_ERROR.',
|
||||||
|
86 => 'CURLE_RTSP_SESSION_ERROR.',
|
||||||
|
87 => 'CURLE_FTP_BAD_FILE_LIST.',
|
||||||
|
88 => 'CURLE_CHUNK_FAILED.',
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -6,6 +6,13 @@ use ReflectionClass;
|
|||||||
|
|
||||||
class Enum
|
class Enum
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Message list
|
||||||
|
*
|
||||||
|
* @var array<string<static>>
|
||||||
|
*/
|
||||||
|
public static $messages = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caches reflections of enum subclasses.
|
* Caches reflections of enum subclasses.
|
||||||
*
|
*
|
||||||
@@ -47,4 +54,20 @@ class Enum
|
|||||||
{
|
{
|
||||||
return array_values(static::getReflection()->getConstants());
|
return array_values(static::getReflection()->getConstants());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a message from the enum subclass
|
||||||
|
*
|
||||||
|
* @param integer $messageIndex The message index to retrieve.
|
||||||
|
* @param string $defaultMessage Message to use if index does not exist.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getMessage(int $messageIndex, string $defaultMessage = 'Unknown'): string
|
||||||
|
{
|
||||||
|
if (array_key_exists($messageIndex, self::$messages) === true) {
|
||||||
|
return self::$messages[$messageIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $defaultMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,34 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use App\Enum\HttpResponseCodes;
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Symfony\Component\ErrorHandler\Exception\FlattenException;
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* A list of exception types with their corresponding custom log levels.
|
|
||||||
*
|
|
||||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
|
||||||
*/
|
|
||||||
protected $levels = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of the exception types that are not reported.
|
|
||||||
*
|
|
||||||
* @var array<int, class-string<\Throwable>>
|
|
||||||
*/
|
|
||||||
protected $dontReport = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
*
|
*
|
||||||
@@ -47,7 +31,7 @@ class Handler extends ExceptionHandler
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function register()
|
public function register(): void
|
||||||
{
|
{
|
||||||
// $this->renderable(function (HttpException $e, $request) {
|
// $this->renderable(function (HttpException $e, $request) {
|
||||||
// if ($request->is('api/*')) {
|
// if ($request->is('api/*')) {
|
||||||
@@ -79,7 +63,36 @@ class Handler extends ExceptionHandler
|
|||||||
});
|
});
|
||||||
|
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
//
|
if ($this->shouldReport($e) === true) {
|
||||||
|
if (App::runningUnitTests() === false) {
|
||||||
|
$this->sendEmail($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send email
|
||||||
|
*
|
||||||
|
* @param Throwable $exception Throwable object.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function sendEmail(Throwable $exception)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$e = FlattenException::createFromThrowable($exception);
|
||||||
|
$handler = new HtmlErrorRenderer(true);
|
||||||
|
$css = $handler->getStylesheet();
|
||||||
|
$content = $handler->getBody($e);
|
||||||
|
|
||||||
|
Mail::send('emails.exception', compact('css', 'content'), function ($message) {
|
||||||
|
$message
|
||||||
|
->to('webmaster@stemmechanics.com.au')
|
||||||
|
->subject('Exception Generated')
|
||||||
|
;
|
||||||
|
});
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
Log::error($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
class AuditFilter
|
|
||||||
{
|
|
||||||
// public static function filter(Collection $collection): array
|
|
||||||
// {
|
|
||||||
// $collection->transform(function ($item, $key) {
|
|
||||||
// $row = $item->toArray();
|
|
||||||
|
|
||||||
// unset($row['user_type']);
|
|
||||||
// unset($row['auditable_type']);
|
|
||||||
|
|
||||||
// if (array_key_exists('password', $row['old_values'])) {
|
|
||||||
// $row['old_values']['password'] = '###';
|
|
||||||
// }
|
|
||||||
// if (array_key_exists('password', $row['new_values'])) {
|
|
||||||
// $row['new_values']['password'] = '###';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return $row;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return $collection->toArray();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use App\Models\Event;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
|
||||||
|
|
||||||
class EventFilter extends FilterAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Class name of Model
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $class = '\App\Models\Event';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default column sorting (prefix with - for descending)
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $defaultSort = '-start_at';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter columns for q param
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $q = [
|
|
||||||
'_' => ['title','content'],
|
|
||||||
'location' => ['location','address'],
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the user can view the media model
|
|
||||||
*
|
|
||||||
* @param Event $event The event instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function viewable(Event $event, mixed $user)
|
|
||||||
{
|
|
||||||
return (strcasecmp($event->status, 'draft') !== 0 && $event->publish_at <= now())
|
|
||||||
|| $user?->hasPermission('admin/events') === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the prebuild query to limit results
|
|
||||||
*
|
|
||||||
* @param EloquentBuilder $builder The builder instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return EloquentBuilder|null
|
|
||||||
*/
|
|
||||||
protected function prebuild(Builder $builder, mixed $user)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
$user?->hasPermission('admin/events') !== true
|
|
||||||
) {
|
|
||||||
return $builder
|
|
||||||
->where('status', '!=', 'draft')
|
|
||||||
->where('publish_at', '<=', now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,596 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Exception;
|
|
||||||
use Doctrine\DBAL\Schema\SchemaException;
|
|
||||||
use ReflectionClass;
|
|
||||||
use RuntimeException;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Schema;
|
|
||||||
|
|
||||||
abstract class FilterAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The model class to filter
|
|
||||||
*
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
protected $class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The filter request
|
|
||||||
*
|
|
||||||
* @var \Illuminate\Http\Request
|
|
||||||
*/
|
|
||||||
protected $request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The models table
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $table = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of columns that can be filtered by the api
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $filterable = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default column sorting (prefix with - for descending)
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $defaultSort = 'id';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default collection result limit
|
|
||||||
*
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $defaultLimit = 50;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Found records from query
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $foundTotal = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum collection result limit
|
|
||||||
*
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $maxLimit = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only return these attributes in the results
|
|
||||||
* (minus any excludes)
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $only = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exclude these attributes from the results
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $exclude = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter columns for q param
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $q = [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter constructor.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request Request object.
|
|
||||||
*/
|
|
||||||
public function __construct(Request $request)
|
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only include the specified attributes in the results.
|
|
||||||
*
|
|
||||||
* @param string|array $only Only return these attributes.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function only(mixed $only)
|
|
||||||
{
|
|
||||||
if (is_array($only) === true) {
|
|
||||||
$this->only = $only;
|
|
||||||
} else {
|
|
||||||
$this->only = [$only];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exclude the specified attributes in the results.
|
|
||||||
*
|
|
||||||
* @param string|array $exclude Attributes to exclude.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function exclude(mixed $exclude)
|
|
||||||
{
|
|
||||||
if (is_array($exclude) === true) {
|
|
||||||
$this->exclude = $exclude;
|
|
||||||
} else {
|
|
||||||
$this->exclude = [$exclude];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the model is viewable by the user
|
|
||||||
*
|
|
||||||
* @param mixed $model Model instance.
|
|
||||||
* @param mixed $user Current user.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
// protected function viewable(mixed $model, mixed $user)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepend action to the builder to limit the results
|
|
||||||
*
|
|
||||||
* @param Builder $builder Builder instance.
|
|
||||||
* @param mixed $user Current user.
|
|
||||||
* @return Builder|null
|
|
||||||
*/
|
|
||||||
// protected function prebuild(Builder $builder, mixed $user)
|
|
||||||
// {
|
|
||||||
// return $builder;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array of attributes visible in the results
|
|
||||||
*
|
|
||||||
* @param array $attributes Attributes currently visible.
|
|
||||||
* @param User|null $user Current logged in user or null.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function seeAttributes(array $attributes, mixed $user)
|
|
||||||
{
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply all the requested filters if available.
|
|
||||||
*
|
|
||||||
* @param Model $model Model object to filter. If null create query.
|
|
||||||
* @return Builder|Model
|
|
||||||
*/
|
|
||||||
public function filter(Model $model = null)
|
|
||||||
{
|
|
||||||
$this->foundTotal = 0;
|
|
||||||
|
|
||||||
$builder = $this->class::query();
|
|
||||||
|
|
||||||
/* Get the related model */
|
|
||||||
$classModel = $model;
|
|
||||||
if ($model === null) {
|
|
||||||
$classModel = $builder->getModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get table name */
|
|
||||||
if ($this->table === '') {
|
|
||||||
if ($model === null) {
|
|
||||||
$this->table = $classModel->getTable();
|
|
||||||
} else {
|
|
||||||
$this->table = $model->getTable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run query prebuilder or viewable */
|
|
||||||
if ($model === null) {
|
|
||||||
if (method_exists($this, 'prebuild') === true) {
|
|
||||||
$prebuilder = $this->prebuild($builder, $this->request->user());
|
|
||||||
if ($prebuilder instanceof Builder) {
|
|
||||||
$builder = $prebuilder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (method_exists($this, 'viewable') === true) {
|
|
||||||
if ($this->viewable($model, $this->request->user()) === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get attributes from table or use 'only' */
|
|
||||||
$attributes = [];
|
|
||||||
if (is_array($this->only) === true && count($this->only) > 0) {
|
|
||||||
$attributes = $this->only;
|
|
||||||
} else {
|
|
||||||
$attributes = Schema::getColumnListing($this->table);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run attribute modifiers*/
|
|
||||||
$modifiedAttribs = $this->seeAttributes($attributes, $this->request->user());
|
|
||||||
if (is_array($modifiedAttribs) === true) {
|
|
||||||
$attributes = $modifiedAttribs;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($attributes as $key => $column) {
|
|
||||||
$method = 'see' . Str::studly($column) . 'Attribute';
|
|
||||||
if (
|
|
||||||
method_exists($this, $method) === true &&
|
|
||||||
$this->$method($this->request->user()) === false
|
|
||||||
) {
|
|
||||||
unset($attributes[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_array($this->exclude) === true && count($this->exclude) > 0) {
|
|
||||||
$attributes = array_diff($attributes, $this->exclude);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup attributes and appends */
|
|
||||||
// $attributesAppends = array_merge($attributes, $classModel->getAppends());
|
|
||||||
|
|
||||||
/* Apply ?fields= request to attributes */
|
|
||||||
if ($this->request->has('fields') === true) {
|
|
||||||
$attributes = array_intersect($attributes, explode(',', $this->request->fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide remaining attributes in model (if present) and return */
|
|
||||||
if ($model !== null) {
|
|
||||||
// TODO: Also show $this->request->fields that are appends
|
|
||||||
|
|
||||||
$model->makeHidden(array_diff(Schema::getColumnListing($this->table), $attributes));
|
|
||||||
return $model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Are there attributes left? */
|
|
||||||
if (count($attributes) === 0) {
|
|
||||||
$this->foundTotal = 0;
|
|
||||||
return new Collection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* apply select! */
|
|
||||||
$builder->select($attributes);
|
|
||||||
|
|
||||||
/* Setup filterables if not present */
|
|
||||||
if ($this->filterable === null) {
|
|
||||||
$this->filterable = $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter values */
|
|
||||||
$filterRequest = array_filter($this->request->only(array_intersect($attributes, $this->filterable)));
|
|
||||||
$this->builderArrayFilter($builder, $filterRequest);
|
|
||||||
|
|
||||||
if (is_array($this->q) === true && count($this->q) > 0) {
|
|
||||||
$qQueries = [];
|
|
||||||
foreach ($this->q as $key => $value) {
|
|
||||||
if (is_array($value) === true) {
|
|
||||||
$qKey = $key === '_' ? '' : $key;
|
|
||||||
foreach ($value as $subvalue) {
|
|
||||||
$qQueries[$key][$subvalue] = $this->request->get("q" . $qKey);
|
|
||||||
}
|
|
||||||
} elseif ($this->request->has("q") === true) {
|
|
||||||
$qQueries['_'][$value] = $this->request->get("q");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($qQueries as $key => $value) {
|
|
||||||
$builder->where(function ($query) use ($value) {
|
|
||||||
$this->builderArrayFilter($query, $value, 'or');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}//end if
|
|
||||||
|
|
||||||
/* Apply sorting */
|
|
||||||
$sortList = $this->defaultSort;
|
|
||||||
if ($this->request->has('sort') === true) {
|
|
||||||
$sortList = explode(',', $this->request->sort);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transform sort list to array */
|
|
||||||
if (is_array($sortList) === false) {
|
|
||||||
if (strlen($sortList) > 0) {
|
|
||||||
$sortList = [$sortList];
|
|
||||||
} else {
|
|
||||||
$sortList = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove non-viewable attributes from sort list */
|
|
||||||
if (count($sortList) > 0) {
|
|
||||||
$sortList = array_filter($sortList, function ($item) use ($attributes) {
|
|
||||||
$parsedItem = $item;
|
|
||||||
if (substr($parsedItem, 0, 1) === '-') {
|
|
||||||
$parsedItem = substr($parsedItem, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return in_array($parsedItem, $attributes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do we have any sort element left? */
|
|
||||||
if (count($sortList) > 0) {
|
|
||||||
foreach ($sortList as $sortAttribute) {
|
|
||||||
$prefix = substr($sortAttribute, 0, 1);
|
|
||||||
$direction = 'asc';
|
|
||||||
|
|
||||||
if (in_array($prefix, ['-', '+']) === true) {
|
|
||||||
$sortAttribute = substr($sortAttribute, 1);
|
|
||||||
if ($prefix === '-') {
|
|
||||||
$direction = 'desc';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->orderBy($sortAttribute, $direction);
|
|
||||||
}//end foreach
|
|
||||||
}//end if
|
|
||||||
|
|
||||||
/* save found count */
|
|
||||||
$this->foundTotal = $builder->count();
|
|
||||||
|
|
||||||
/* Apply result limit */
|
|
||||||
$limit = $this->defaultLimit;
|
|
||||||
if ($this->request->has('limit') === true) {
|
|
||||||
$limit = intval($this->request->limit);
|
|
||||||
}
|
|
||||||
if ($limit < 1) {
|
|
||||||
$limit = 1;
|
|
||||||
}
|
|
||||||
if ($limit > $this->maxLimit && $this->maxLimit !== 0) {
|
|
||||||
$limit = $this->maxLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->limit($limit);
|
|
||||||
|
|
||||||
/* Apply page offset */
|
|
||||||
if ($this->request->has('page') === true) {
|
|
||||||
$page = intval($this->request->page);
|
|
||||||
if ($page < 1) {
|
|
||||||
$page = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->offset((intval($this->request->page) - 1) * $limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* run spot run */
|
|
||||||
$collection = $builder->get();
|
|
||||||
|
|
||||||
return $collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter content based on the filterRequest
|
|
||||||
* @param mixed $builder Builder object
|
|
||||||
* @param array $filterRequest Filter key/value
|
|
||||||
* @param string $defaultBoolean Default where boolean
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function builderArrayFilter(mixed $builder, array $filterRequest, string $defaultBoolean = 'and')
|
|
||||||
{
|
|
||||||
foreach ($filterRequest as $filterAttribute => $filterValue) {
|
|
||||||
$tags = [];
|
|
||||||
$boolean = $defaultBoolean;
|
|
||||||
|
|
||||||
$matches = preg_split('/(?<!\\\\)"/', $filterValue, -1, PREG_SPLIT_OFFSET_CAPTURE);
|
|
||||||
foreach ($matches as $idx => $match_info) {
|
|
||||||
if (($idx % 2) === true) {
|
|
||||||
if (substr($filterValue, ($match_info[1] - 2), 1) === ',') {
|
|
||||||
$tags[] = ['operator' => '', 'tag' => stripslashes(trim($match_info[0]))];
|
|
||||||
} else {
|
|
||||||
$tags[(count($tags) - 1)]['tag'] .= stripslashes(trim($match_info[0]));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$innerTags = [$match_info[0]];
|
|
||||||
if (strpos($match_info[0], ',') !== false) {
|
|
||||||
$innerTags = preg_split('/(?<!\\\\),/', $match_info[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($innerTags as $tag) {
|
|
||||||
$tag = stripslashes(trim($tag));
|
|
||||||
if (strlen($tag) > 0) {
|
|
||||||
$operator = '=';
|
|
||||||
|
|
||||||
$single = substr($tag, 0, 1);
|
|
||||||
$double = substr($tag . ' ', 0, 2); // add empty space incase len $tag < 2
|
|
||||||
|
|
||||||
// check for operators at start
|
|
||||||
if (in_array($double, ['!=', '<>', '><', '>=', '<=', '=>', '=<']) === true) {
|
|
||||||
if ($double === '<>' || $double === '><') {
|
|
||||||
$double = '!=';
|
|
||||||
} elseif ($double === '=>') {
|
|
||||||
$double = '>=';
|
|
||||||
} elseif ($double === '=<') {
|
|
||||||
$double == '>=';
|
|
||||||
}
|
|
||||||
|
|
||||||
$operator = $double;
|
|
||||||
$tag = substr($tag, 2);
|
|
||||||
} else {
|
|
||||||
if (in_array($single, ['=', '!', '>', '<', '~', '%']) === true) {
|
|
||||||
if ($single === '=') {
|
|
||||||
$single = '=='; // a single '=' is actually a double '=='
|
|
||||||
}
|
|
||||||
|
|
||||||
$operator = $single;
|
|
||||||
$tag = substr($tag, 1);
|
|
||||||
}
|
|
||||||
}//end if
|
|
||||||
|
|
||||||
$tags[] = ['operator' => $operator, 'tag' => $tag];
|
|
||||||
}//end if
|
|
||||||
}//end foreach
|
|
||||||
}//end if
|
|
||||||
}//end foreach
|
|
||||||
|
|
||||||
if (count($tags) > 1) {
|
|
||||||
$boolean = 'or';
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($tags as $tag_data) {
|
|
||||||
$operator = $tag_data['operator'];
|
|
||||||
$value = $tag_data['tag'];
|
|
||||||
$table = $this->table;
|
|
||||||
$column = $filterAttribute;
|
|
||||||
|
|
||||||
if (($dotPos = strpos($filterAttribute, '.')) !== false) {
|
|
||||||
$table = substr($filterAttribute, 0, $dotPos);
|
|
||||||
$column = substr($filterAttribute, ($dotPos + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
$columnType = DB::getSchemaBuilder()->getColumnType($table, $column);
|
|
||||||
|
|
||||||
if (
|
|
||||||
in_array($columnType, ['tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint',
|
|
||||||
'decimal', 'float', 'double', 'real', 'double precision'
|
|
||||||
]) === true
|
|
||||||
) {
|
|
||||||
if (in_array($operator, ['=', '>', '<', '>=', '<=', '%', '!']) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$columnType = 'numeric';
|
|
||||||
} elseif (in_array($columnType, ['date', 'time', 'datetime', 'timestamp', 'year']) === true) {
|
|
||||||
if (in_array($operator, ['=', '>', '<', '>=', '<=', '!']) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$columnType = 'datetime';
|
|
||||||
} elseif (
|
|
||||||
in_array($columnType, ['string', 'char', 'varchar', 'timeblob', 'blob', 'mediumblob',
|
|
||||||
'longblob', 'tinytext', 'text', 'mediumtext', 'longtext', 'enum'
|
|
||||||
]) === true
|
|
||||||
) {
|
|
||||||
if (in_array($operator, ['=', '==', '!', '!=', '~']) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$columnType = 'text';
|
|
||||||
|
|
||||||
if ($value === "''" || $value === '""') {
|
|
||||||
$value = '';
|
|
||||||
} elseif (strcasecmp($value, 'null') !== 0) {
|
|
||||||
if ($operator === '!') {
|
|
||||||
$operator = 'NOT LIKE';
|
|
||||||
$value = '%' . $value . '%';
|
|
||||||
} elseif ($operator === '=') {
|
|
||||||
$operator = 'LIKE';
|
|
||||||
$value = '%' . $value . '%';
|
|
||||||
} elseif ($operator === '~') {
|
|
||||||
$operator = 'SOUNDS LIKE';
|
|
||||||
} elseif ($operator === '==') {
|
|
||||||
$operator = '=';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($columnType === 'boolean') {
|
|
||||||
if (in_array($operator, ['=', '!']) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strtolower($value) === 'true') {
|
|
||||||
$value = 1;
|
|
||||||
} elseif (strtolower($value) === 'false') {
|
|
||||||
$value = 0;
|
|
||||||
}
|
|
||||||
}//end if
|
|
||||||
|
|
||||||
$betweenSeperator = strpos($value, '<>');
|
|
||||||
if (
|
|
||||||
$operator === '=' && $betweenSeperator !== false && in_array($columnType, ['numeric',
|
|
||||||
'datetime'
|
|
||||||
]) === true
|
|
||||||
) {
|
|
||||||
$value = explode('<>', $value);
|
|
||||||
$operator = '<>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($operator !== '') {
|
|
||||||
$this->builderWhere($builder, $table, $column, $operator, $value, $boolean);
|
|
||||||
}
|
|
||||||
}//end foreach
|
|
||||||
}//end foreach
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a where statement into the builder, taking the filter map into consideration
|
|
||||||
*
|
|
||||||
* @param Builder $builder Builder instance.
|
|
||||||
* @param string $table Table name.
|
|
||||||
* @param string $column Column name.
|
|
||||||
* @param string $operator Where operator.
|
|
||||||
* @param mixed $value Value to test.
|
|
||||||
* @param string $boolean Use Or comparison.
|
|
||||||
* @return void
|
|
||||||
* @throws RuntimeException Error applying statement.
|
|
||||||
* @throws InvalidArgumentException Error applying statement.
|
|
||||||
*/
|
|
||||||
protected function builderWhere(
|
|
||||||
Builder &$builder,
|
|
||||||
string $table,
|
|
||||||
string $column,
|
|
||||||
string $operator,
|
|
||||||
mixed $value,
|
|
||||||
string $boolean
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
(is_string($value) === true && $operator !== '<>') || (is_array($value) === true && count($value) === 2 &&
|
|
||||||
$operator === '<>')
|
|
||||||
) {
|
|
||||||
if ($table !== '' && $table !== $this->table) {
|
|
||||||
$builder->whereHas($table, function ($query) use ($column, $operator, $value, $boolean) {
|
|
||||||
if ($operator !== '<>') {
|
|
||||||
if (strcasecmp($value, 'null') === 0) {
|
|
||||||
if ($operator === '!') {
|
|
||||||
$query->whereNotNull($column, $boolean);
|
|
||||||
} else {
|
|
||||||
$query->whereNull($column, $boolean);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$query->where($column, $operator, $value, $boolean);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$query->whereBetween($column, $value, $boolean);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if ($operator !== '<>') {
|
|
||||||
if (strcasecmp($value, 'null') === 0) {
|
|
||||||
if ($operator === '!') {
|
|
||||||
$builder->whereNotNull($column, $boolean);
|
|
||||||
} else {
|
|
||||||
$builder->whereNull($column, $boolean);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$builder->where($column, $operator, $value, $boolean);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$builder->whereBetween($column, $value, $boolean);
|
|
||||||
}
|
|
||||||
}//end if
|
|
||||||
}//end if
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the found total of items
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function foundTotal()
|
|
||||||
{
|
|
||||||
return $this->foundTotal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use App\Models\Media;
|
|
||||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
|
||||||
|
|
||||||
class MediaFilter extends FilterAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Class name of Model
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $class = '\App\Models\Media';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the user can view the media model
|
|
||||||
*
|
|
||||||
* @param Media $media The media instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function viewable(Media $media, mixed $user)
|
|
||||||
{
|
|
||||||
if (empty($media->permission) === false) {
|
|
||||||
return ($user?->hasPermission('admin/media') || $user?->hasPermission($media->permission));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the prebuild query to limit results
|
|
||||||
*
|
|
||||||
* @param EloquentBuilder $builder The builder instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return EloquentBuilder|null
|
|
||||||
*/
|
|
||||||
protected function prebuild(Builder $builder, mixed $user)
|
|
||||||
{
|
|
||||||
if ($user === null) {
|
|
||||||
return $builder->whereNull('permission');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the permission attribute in the results
|
|
||||||
*
|
|
||||||
* @param User|null $user Current logged in user or null.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function seePermissionAttribute(mixed $user)
|
|
||||||
{
|
|
||||||
return ($user?->hasPermission('admin/media'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use App\Models\Post;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
|
||||||
|
|
||||||
class PostFilter extends FilterAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Class name of Model
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $class = '\App\Models\Post';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default column sorting (prefix with - for descending)
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $defaultSort = '-publish_at';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the user can view the media model
|
|
||||||
*
|
|
||||||
* @param Post $post The post instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function viewable(Post $post, mixed $user)
|
|
||||||
{
|
|
||||||
if ($user?->hasPermission('admin/posts') !== true) {
|
|
||||||
return ($post->publish_at <= now());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the prebuild query to limit results
|
|
||||||
*
|
|
||||||
* @param EloquentBuilder $builder The builder instance.
|
|
||||||
* @param mixed $user The current logged in user.
|
|
||||||
* @return EloquentBuilder|null
|
|
||||||
*/
|
|
||||||
protected function prebuild(Builder $builder, mixed $user)
|
|
||||||
{
|
|
||||||
if ($user?->hasPermission('admin/posts') !== true) {
|
|
||||||
return $builder->where('publish_at', '<=', Carbon::now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,7 @@ class SubscriptionFilter extends FilterAbstract
|
|||||||
*
|
*
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
protected $class = '\App\Models\Subscription';
|
protected $class = \App\Models\Subscription::class;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Filters;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
class UserFilter extends FilterAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The model class to filter
|
|
||||||
*
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
protected $class = '\App\Models\User';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array of attributes visible in the results
|
|
||||||
*
|
|
||||||
* @param array $attributes Attributes currently visible.
|
|
||||||
* @param User|null $user Current logged in user or null.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function seeAttributes(array $attributes, mixed $user)
|
|
||||||
{
|
|
||||||
if ($user?->hasPermission('admin/users') !== true) {
|
|
||||||
return ['id', 'username'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
78
app/Helpers/Array.php
Normal file
78
app/Helpers/Array.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* Array Helper Functions */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from an array.
|
||||||
|
*
|
||||||
|
* @param array $arr The array to check.
|
||||||
|
* @param string|array $item The item or items to remove.
|
||||||
|
* @return array The filtered array.
|
||||||
|
*/
|
||||||
|
function arrayRemoveItem(array $arr, string|array $item): array
|
||||||
|
{
|
||||||
|
$filteredArr = $arr;
|
||||||
|
|
||||||
|
if (is_string($item) === true) {
|
||||||
|
$item = [$item];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($item as $str) {
|
||||||
|
$filteredArr = array_filter($arr, function ($item) use ($str) {
|
||||||
|
return $item !== $str;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with specified the keys
|
||||||
|
*
|
||||||
|
* @param array $arr The array to filter.
|
||||||
|
* @param string|array $keys The keys to keep.
|
||||||
|
* @return array The filtered array.
|
||||||
|
*/
|
||||||
|
function arrayLimitKeys(array $arr, array $keys): array
|
||||||
|
{
|
||||||
|
return array_intersect_key($arr, array_flip($keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array value or default value if it does not exist
|
||||||
|
*
|
||||||
|
* @param string $key The key value to return if exists.
|
||||||
|
* @param array $arr The array to check.
|
||||||
|
* @param mixed $value The value to return if key does not exist.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function arrayDefaultValue(string $key, array $arr, mixed $value): mixed
|
||||||
|
{
|
||||||
|
if (array_key_exists($key, $arr) === true) {
|
||||||
|
return $arr[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if an item exists in an array, case insensitive
|
||||||
|
*
|
||||||
|
* @param string $val The value to check.
|
||||||
|
* @param array $arr The array to check.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
function existsInArray(string $val, array $arr): bool
|
||||||
|
{
|
||||||
|
$exists = false;
|
||||||
|
|
||||||
|
foreach ($arr as $el) {
|
||||||
|
if (strcasecmp($val, $el) === 0) {
|
||||||
|
$exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exists;
|
||||||
|
}
|
||||||
92
app/Helpers/Temp.php
Normal file
92
app/Helpers/Temp.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* Temp File Helper Functions */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a temporary file path.
|
||||||
|
*
|
||||||
|
* @param string $extension The file extension to use.
|
||||||
|
* @param string $part The file part number.
|
||||||
|
* @return string The filtered array.
|
||||||
|
*/
|
||||||
|
function generateTempFilePath(string $extension = '', string $part = ''): string
|
||||||
|
{
|
||||||
|
$temporaryDir = storage_path('app/tmp');
|
||||||
|
if (is_dir($temporaryDir) === false) {
|
||||||
|
mkdir($temporaryDir, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $temporaryDir . DIRECTORY_SEPARATOR . uniqid('upload_', true) . ($extension !== '' ? ".{$extension}" : '') .
|
||||||
|
($part !== '' ? ".part-{$part}" : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Temp file information
|
||||||
|
*
|
||||||
|
* @param string $filePath The temp file name.
|
||||||
|
* @return array The temp file name details.
|
||||||
|
*/
|
||||||
|
function tempFileInfo(string $filePath): array
|
||||||
|
{
|
||||||
|
$part = '';
|
||||||
|
|
||||||
|
// Extract the part if it's present
|
||||||
|
if (preg_match('/\.part-(\d+)$/', $filePath, $matches) !== false) {
|
||||||
|
$part = $matches[1];
|
||||||
|
$filePath = substr($filePath, 0, -strlen($matches[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = pathinfo($filePath);
|
||||||
|
|
||||||
|
$directory = $info['dirname'];
|
||||||
|
$name = $info['filename'];
|
||||||
|
$extension = '';
|
||||||
|
|
||||||
|
// If there's an extension, separate it
|
||||||
|
if (isset($info['extension']) === true) {
|
||||||
|
$extension = $info['extension'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dirname' => $directory,
|
||||||
|
'basename' => $name . ($extension !== '' ? ".{$extension}" : ''),
|
||||||
|
'filename' => $name,
|
||||||
|
'extension' => $extension,
|
||||||
|
'part' => $part,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a temporary file exists.
|
||||||
|
*
|
||||||
|
* @param string $dir The file parent directory.
|
||||||
|
* @param string $name The file name.
|
||||||
|
* @param string $extension The file extension to use.
|
||||||
|
* @param string $part The file part number.
|
||||||
|
* @return boolean If the file exists.
|
||||||
|
*/
|
||||||
|
function tempFileExists(string $dir, string $name, string $extension = '', string $part = ''): bool
|
||||||
|
{
|
||||||
|
$filename = constructTempFileName($dir, $name, $extension, $part);
|
||||||
|
$exists = file_exists($filename);
|
||||||
|
|
||||||
|
return $exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the temp file name based on the information
|
||||||
|
*
|
||||||
|
* @param string $dir The file parent directory.
|
||||||
|
* @param string $name The file name.
|
||||||
|
* @param string $extension The file extension to use.
|
||||||
|
* @param string $part The file part number.
|
||||||
|
* @return string The file path.
|
||||||
|
*/
|
||||||
|
function constructTempFileName(string $dir, string $name, string $extension = '', string $part = ''): string
|
||||||
|
{
|
||||||
|
$filename = $dir . DIRECTORY_SEPARATOR . $name . ($extension !== '' ? ".{$extension}" : '') .
|
||||||
|
($part !== "" ? ".part-{$part}" : '');
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
27
app/Helpers/TypeValue.php
Normal file
27
app/Helpers/TypeValue.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* Type Value Helper Functions */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is value true
|
||||||
|
*
|
||||||
|
* @param mixed $value Value to check.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
function isTrue(mixed $value): bool
|
||||||
|
{
|
||||||
|
if (is_bool($value) === true && $value === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($value) === true && intval($value) === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value) === true && in_array(strtolower($value), ['true', '1'], true) === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
121
app/Http/Controllers/Api/AnalyticsController.php
Normal file
121
app/Http/Controllers/Api/AnalyticsController.php
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\AnalyticsConductor;
|
||||||
|
use App\Enum\HttpResponseCodes;
|
||||||
|
use App\Http\Requests\AnalyticsRequest;
|
||||||
|
use App\Models\AnalyticsItemRequest;
|
||||||
|
use App\Models\AnalyticsSession;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class AnalyticsController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* AnalyticsController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth:sanctum')
|
||||||
|
->only([
|
||||||
|
'index',
|
||||||
|
'update',
|
||||||
|
'delete'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->user() !== null && $request->user()?->hasPermission('admin/analytics') === true) {
|
||||||
|
$request->rename([
|
||||||
|
'type' => 'requests.type',
|
||||||
|
'path' => 'requests.path'
|
||||||
|
]);
|
||||||
|
|
||||||
|
list($collection, $total) = AnalyticsConductor::request($request);
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$collection,
|
||||||
|
['resourceName' => 'session',
|
||||||
|
'isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @param \App\Models\AnalyticsSession $session The analytics session.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, AnalyticsSession $session)
|
||||||
|
{
|
||||||
|
if ($request->user() !== null && $request->user()?->hasPermission('admin/analytics') === true) {
|
||||||
|
$session->load(['requests' => function ($query) {
|
||||||
|
$query->select('session_id', 'type', 'path', 'created_at');
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($session->requests as $requestItem) {
|
||||||
|
$requestItem->makeHidden('session_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$session,
|
||||||
|
['resourceName' => 'session']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\AnalyticsRequest $request The user request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(AnalyticsRequest $request)
|
||||||
|
{
|
||||||
|
if (AnalyticsConductor::creatable() === true) {
|
||||||
|
$analytics = null;
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'type' => $request->input('type'),
|
||||||
|
'path' => $request->input('path', ''),
|
||||||
|
'useragent' => $request->userAgent(),
|
||||||
|
'ip' => $request->ip()
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
$user !== null &&
|
||||||
|
$user->hasPermission('admin/analytics') === true &&
|
||||||
|
$request->has('session') === true
|
||||||
|
) {
|
||||||
|
$data['session_id'] = $request->input('session_id');
|
||||||
|
$analytics = AnalyticsItemRequest::create($data);
|
||||||
|
} else {
|
||||||
|
$analytics = AnalyticsItemRequest::create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
AnalyticsConductor::model($request, $analytics),
|
||||||
|
['respondCode' => HttpResponseCodes::HTTP_CREATED]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}//end if
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -23,10 +24,13 @@ class ApiController extends Controller
|
|||||||
* @param array $data Response data.
|
* @param array $data Response data.
|
||||||
* @param integer $respondCode Response status code.
|
* @param integer $respondCode Response status code.
|
||||||
* @param array $headers Response headers.
|
* @param array $headers Response headers.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondJson(array $data, int $respondCode = HttpResponseCodes::HTTP_OK, array $headers = [])
|
public function respondJson(
|
||||||
{
|
array $data,
|
||||||
|
int $respondCode = HttpResponseCodes::HTTP_OK,
|
||||||
|
array $headers = []
|
||||||
|
): JsonResponse {
|
||||||
return response()->json($data, $respondCode, $headers);
|
return response()->json($data, $respondCode, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,10 +38,11 @@ class ApiController extends Controller
|
|||||||
* Return forbidden message
|
* Return forbidden message
|
||||||
*
|
*
|
||||||
* @param string $message Response message.
|
* @param string $message Response message.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondForbidden(string $message = 'You do not have permission to access the resource.')
|
public function respondForbidden(
|
||||||
{
|
string $message = 'You do not have permission to access the resource.'
|
||||||
|
): JsonResponse {
|
||||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_FORBIDDEN);
|
return response()->json(['message' => $message], HttpResponseCodes::HTTP_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +50,9 @@ class ApiController extends Controller
|
|||||||
* Return forbidden message
|
* Return forbidden message
|
||||||
*
|
*
|
||||||
* @param string $message Response message.
|
* @param string $message Response message.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondNotFound(string $message = 'The resource was not found.')
|
public function respondNotFound(string $message = 'The resource was not found.'): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_NOT_FOUND);
|
return response()->json(['message' => $message], HttpResponseCodes::HTTP_NOT_FOUND);
|
||||||
}
|
}
|
||||||
@@ -56,40 +61,74 @@ class ApiController extends Controller
|
|||||||
* Return too large message
|
* Return too large message
|
||||||
*
|
*
|
||||||
* @param string $message Response message.
|
* @param string $message Response message.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondTooLarge(string $message = 'The request entity is too large.')
|
public function respondTooLarge(string $message = 'The request entity is too large.'): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_REQUEST_ENTITY_TOO_LARGE);
|
return response()->json(['message' => $message], HttpResponseCodes::HTTP_REQUEST_ENTITY_TOO_LARGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return no content
|
* Return no content.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
*
|
||||||
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondNoContent()
|
public function respondNoContent(): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json([], HttpResponseCodes::HTTP_NO_CONTENT);
|
return response()->json([], HttpResponseCodes::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return created
|
* Return no content
|
||||||
* @return \Illuminate\Http\JsonResponse
|
*
|
||||||
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondCreated()
|
public function respondNotImplemented(): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([], HttpResponseCodes::HTTP_NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return created.
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function respondCreated(): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json([], HttpResponseCodes::HTTP_CREATED);
|
return response()->json([], HttpResponseCodes::HTTP_CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return accepted.
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function respondAccepted(): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([], HttpResponseCodes::HTTP_ACCEPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return server error.
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function respondServerError(): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([], HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return single error message
|
* Return single error message
|
||||||
*
|
*
|
||||||
* @param string $message Error message.
|
* @param string $message Error message.
|
||||||
* @param integer $responseCode Resource code.
|
* @param integer $responseCode Resource code.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondError(string $message, int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY)
|
public function respondError(
|
||||||
{
|
string $message,
|
||||||
|
int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY
|
||||||
|
): JsonResponse {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => $message
|
'message' => $message
|
||||||
], $responseCode);
|
], $responseCode);
|
||||||
@@ -100,10 +139,12 @@ class ApiController extends Controller
|
|||||||
*
|
*
|
||||||
* @param array $errors Error messages.
|
* @param array $errors Error messages.
|
||||||
* @param integer $responseCode Resource code.
|
* @param integer $responseCode Resource code.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function respondWithErrors(array $errors, int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY)
|
public function respondWithErrors(
|
||||||
{
|
array $errors,
|
||||||
|
int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY
|
||||||
|
): JsonResponse {
|
||||||
$keys = array_keys($errors);
|
$keys = array_keys($errors);
|
||||||
$error = $errors[$keys[0]];
|
$error = $errors[$keys[0]];
|
||||||
|
|
||||||
@@ -122,45 +163,57 @@ class ApiController extends Controller
|
|||||||
* Return resource data
|
* Return resource data
|
||||||
*
|
*
|
||||||
* @param array|Model|Collection $data Resource data.
|
* @param array|Model|Collection $data Resource data.
|
||||||
* @param array|null $appendData Data to append to response.
|
* @param array $options Respond options.
|
||||||
* @param integer $respondCode Resource code.
|
* @param callable|null $validationFn Optional validation function to check the data before responding.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
protected function respondAsResource(
|
protected function respondAsResource(
|
||||||
mixed $data,
|
mixed $data,
|
||||||
mixed $appendData = null,
|
array $options = [],
|
||||||
int $respondCode = HttpResponseCodes::HTTP_OK
|
$validationFn = null
|
||||||
) {
|
): JsonResponse {
|
||||||
|
$isCollection = ($options['isCollection'] ?? false);
|
||||||
|
$appendData = ($options['appendData'] ?? null);
|
||||||
|
$resourceName = ($options['resourceName'] ?? '');
|
||||||
|
$transformResourceName = ($options['transformResourceName'] ?? true);
|
||||||
|
$respondCode = ($options['respondCode'] ?? HttpResponseCodes::HTTP_OK);
|
||||||
|
|
||||||
if ($data === null || ($data instanceof Collection && $data->count() === 0)) {
|
if ($data === null || ($data instanceof Collection && $data->count() === 0)) {
|
||||||
return $this->respondNotFound();
|
$validationData = [];
|
||||||
|
if (array_key_exists('appendData', $options) === true) {
|
||||||
|
$validationData = $options['appendData'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$resourceName = $this->resourceName;
|
if ($validationFn === null || $validationFn($validationData) === true) {
|
||||||
|
return $this->respondNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->resourceName === '') {
|
if (empty($resourceName) === true) {
|
||||||
|
$resourceName = $this->resourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($resourceName) === true) {
|
||||||
$resourceName = get_class($this);
|
$resourceName = get_class($this);
|
||||||
$resourceName = substr($resourceName, (strrpos($resourceName, '\\') + 1));
|
$resourceName = substr($resourceName, (strrpos($resourceName, '\\') + 1));
|
||||||
$resourceName = substr($resourceName, 0, strpos($resourceName, 'Controller'));
|
$resourceName = substr($resourceName, 0, strpos($resourceName, 'Controller'));
|
||||||
$resourceName = strtolower($resourceName);
|
$resourceName = strtolower($resourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
$is_multiple = true;
|
|
||||||
|
|
||||||
$dataArray = [];
|
$dataArray = [];
|
||||||
if ($data instanceof Collection) {
|
if ($data instanceof Collection) {
|
||||||
$dataArray = $data->toArray();
|
$dataArray = $data->toArray();
|
||||||
} elseif (is_array($data) === true) {
|
} elseif (is_array($data) === true) {
|
||||||
$dataArray = $data;
|
$dataArray = $data;
|
||||||
} elseif ($data instanceof Model) {
|
} elseif ($data instanceof Model) {
|
||||||
$is_multiple = false;
|
|
||||||
$dataArray = $data->toArray();
|
$dataArray = $data->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
$resource = [];
|
$resource = [];
|
||||||
if ($is_multiple === true) {
|
if ($isCollection === true) {
|
||||||
$resource = [Str::plural($resourceName) => $dataArray];
|
$resource = [$transformResourceName === true ? Str::plural($resourceName) : $resourceName => $dataArray];
|
||||||
} else {
|
} else {
|
||||||
$resource = [Str::singular($resourceName) => $dataArray];
|
$resource = [$transformResourceName === true ? Str::singular($resourceName) : $resourceName => $dataArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($appendData !== null) {
|
if ($appendData !== null) {
|
||||||
@@ -169,4 +222,22 @@ class ApiController extends Controller
|
|||||||
|
|
||||||
return response()->json($resource, $respondCode);
|
return response()->json($resource, $respondCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Controller Model Class name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getModelClass(): string
|
||||||
|
{
|
||||||
|
$controllerClass = static::class;
|
||||||
|
|
||||||
|
$modelName = 'App\\Models\\' . Str::replaceLast('Controller', '', Str::afterLast($controllerClass, '\\'));
|
||||||
|
|
||||||
|
if (class_exists($modelName) === false) {
|
||||||
|
return $modelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $modelName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
139
app/Http/Controllers/Api/ArticleController.php
Normal file
139
app/Http/Controllers/Api/ArticleController.php
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\MediaConductor;
|
||||||
|
use App\Conductors\ArticleConductor;
|
||||||
|
use App\Enum\HttpResponseCodes;
|
||||||
|
use App\Http\Requests\ArticleRequest;
|
||||||
|
use App\Models\Media;
|
||||||
|
use App\Models\Article;
|
||||||
|
use App\Traits\HasAttachments;
|
||||||
|
use App\Traits\HasGallery;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ArticleController extends ApiController
|
||||||
|
{
|
||||||
|
use HasAttachments;
|
||||||
|
use HasGallery;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApplicationController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth:sanctum')
|
||||||
|
->only([
|
||||||
|
'store',
|
||||||
|
'update',
|
||||||
|
'delete'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
list($collection, $total) = ArticleConductor::request($request);
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$collection,
|
||||||
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @param \App\Models\Article $article The article model.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Article $article)
|
||||||
|
{
|
||||||
|
if (ArticleConductor::viewable($article) === true) {
|
||||||
|
return $this->respondAsResource(ArticleConductor::model($request, $article));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\ArticleRequest $request The user request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(ArticleRequest $request)
|
||||||
|
{
|
||||||
|
if (ArticleConductor::creatable() === true) {
|
||||||
|
$article = Article::create($request->except(['attachments', 'gallery']));
|
||||||
|
|
||||||
|
if ($request->has('attachments') === true) {
|
||||||
|
$article->addAttachments($request->get('attachments'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->has('gallery') === true) {
|
||||||
|
$article->galleryAddMany($request->get('gallery'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
ArticleConductor::model($request, $article),
|
||||||
|
['respondCode' => HttpResponseCodes::HTTP_CREATED]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}//end if
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\ArticleRequest $request The article update request.
|
||||||
|
* @param \App\Models\Article $article The specified article.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function update(ArticleRequest $request, Article $article)
|
||||||
|
{
|
||||||
|
if (ArticleConductor::updatable($article) === true) {
|
||||||
|
if ($request->has('attachments') === true) {
|
||||||
|
$article->deleteAttachments();
|
||||||
|
$article->addAttachments($request->get('attachments'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->has('gallery') === true) {
|
||||||
|
$article->gallery()->delete();
|
||||||
|
$article->galleryAddMany($request->get('gallery'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$article->update($request->except(['attachments', 'gallery']));
|
||||||
|
return $this->respondAsResource(ArticleConductor::model($request, $article));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Article $article The specified article.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function destroy(Article $article)
|
||||||
|
{
|
||||||
|
if (ArticleConductor::destroyable($article) === true) {
|
||||||
|
$article->delete();
|
||||||
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ class AuthController extends ApiController
|
|||||||
* @param Request $request Current request data.
|
* @param Request $request Current request data.
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function me(Request $request)
|
public function me(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$user = $request->user()->makeVisible(['permissions']);
|
$user = $request->user()->makeVisible(['permissions']);
|
||||||
return $this->respondAsResource($user);
|
return $this->respondAsResource($user);
|
||||||
@@ -47,18 +47,22 @@ class AuthController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function login(AuthLoginRequest $request)
|
public function login(AuthLoginRequest $request)
|
||||||
{
|
{
|
||||||
$user = User::where('username', '=', $request->input('username'))->first();
|
$user = User::where('email', '=', $request->input('email'))->first();
|
||||||
|
|
||||||
if ($user !== null && Hash::check($request->input('password'), $user->password) === true) {
|
if (
|
||||||
|
$user !== null &&
|
||||||
|
strlen($user->password) > 0 &&
|
||||||
|
Hash::check($request->input('password'), $user->password) === true
|
||||||
|
) {
|
||||||
if ($user->email_verified_at === null) {
|
if ($user->email_verified_at === null) {
|
||||||
return $this->respondWithErrors([
|
return $this->respondWithErrors([
|
||||||
'username' => 'Email address has not been verified.'
|
'email' => 'Email address has not been verified.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user->disabled === true) {
|
if ($user->disabled === true) {
|
||||||
return $this->respondWithErrors([
|
return $this->respondWithErrors([
|
||||||
'username' => 'Account has been disabled.'
|
'email' => 'Account has been disabled.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +77,13 @@ class AuthController extends ApiController
|
|||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$user->makeVisible(['permissions']),
|
$user->makeVisible(['permissions']),
|
||||||
['token' => $token]
|
['appendData' => ['token' => $token]]
|
||||||
);
|
);
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
return $this->respondWithErrors([
|
return $this->respondWithErrors([
|
||||||
'username' => 'Invalid username or password',
|
'email' => 'Invalid email or password',
|
||||||
'password' => 'Invalid username or password',
|
'password' => 'Invalid email or password',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +93,7 @@ class AuthController extends ApiController
|
|||||||
* @param Request $request Current request data.
|
* @param Request $request Current request data.
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function logout(Request $request)
|
public function logout(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,17 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\EventFilter;
|
|
||||||
use App\Http\Requests\EventRequest;
|
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
|
use App\Conductors\EventConductor;
|
||||||
|
use App\Conductors\MediaConductor;
|
||||||
|
use App\Conductors\UserConductor;
|
||||||
|
use App\Http\Requests\EventRequest;
|
||||||
|
use App\Models\Media;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
class EventController extends ApiController
|
class EventController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -16,62 +23,87 @@ class EventController extends ApiController
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth:sanctum')
|
$this->middleware('auth:sanctum')
|
||||||
->only(['store','update','destroy']);
|
->only(['store','update','destroy', 'userAdd', 'userUpdate', 'userDelete']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param EventFilter $filter The event filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(EventFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource(
|
list($collection, $total) = EventConductor::request($request);
|
||||||
$filter->filter(),
|
|
||||||
['total' => $filter->foundTotal()]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param EventRequest $request The event store request.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(EventRequest $request)
|
|
||||||
{
|
|
||||||
$event = Event::create($request->all());
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
(new EventFilter($request))->filter($event),
|
$collection,
|
||||||
null,
|
['isCollection' => true,
|
||||||
HttpResponseCodes::HTTP_CREATED
|
'appendData' => ['total' => $total]
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param EventFilter $filter The event filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param \App\Models\Event $event The specified event.
|
* @param \App\Models\Event $event The specified event.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(EventFilter $filter, Event $event)
|
public function show(Request $request, Event $event)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($event));
|
if (EventConductor::viewable($event) === true) {
|
||||||
|
return $this->respondAsResource(EventConductor::model($request, $event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\EventRequest $request The request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(EventRequest $request)
|
||||||
|
{
|
||||||
|
if (EventConductor::creatable() === true) {
|
||||||
|
$event = Event::create($request->except(['attachments']));
|
||||||
|
|
||||||
|
if ($request->has('attachments') === true) {
|
||||||
|
$event->addAttachments($request->get('attachments'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
EventConductor::model($request, $event),
|
||||||
|
['respondCode' => HttpResponseCodes::HTTP_CREATED]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param EventRequest $request The event update request.
|
* @param \App\Http\Requests\EventRequest $request The endpoint request.
|
||||||
* @param \App\Models\Event $event The specified event.
|
* @param \App\Models\Event $event The specified event.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(EventRequest $request, Event $event)
|
public function update(EventRequest $request, Event $event)
|
||||||
{
|
{
|
||||||
$event->update($request->all());
|
if (EventConductor::updatable($event) === true) {
|
||||||
return $this->respondAsResource((new EventFilter($request))->filter($event));
|
if ($request->has('attachments') === true) {
|
||||||
|
$event->deleteAttachments();
|
||||||
|
$event->addAttachments($request->get('attachments'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->update($request->except(['attachments']));
|
||||||
|
return $this->respondAsResource(EventConductor::model($request, $event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +114,121 @@ class EventController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function destroy(Event $event)
|
public function destroy(Event $event)
|
||||||
{
|
{
|
||||||
|
if (EventConductor::destroyable($event) === true) {
|
||||||
$event->delete();
|
$event->delete();
|
||||||
return $this->respondNoContent();
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List users of Event
|
||||||
|
* @param Request $request The HTTP request.
|
||||||
|
* @param Event $event Event model.
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function userList(Request $request, Event $event): JsonResponse
|
||||||
|
{
|
||||||
|
$authUser = $request->user();
|
||||||
|
$eventUsers = $event->users;
|
||||||
|
|
||||||
|
if ($authUser !== null) {
|
||||||
|
$isAdmin = $authUser->hasPermission('admin/events');
|
||||||
|
$isEventUser = $eventUsers->contains($authUser->id);
|
||||||
|
|
||||||
|
if ($isAdmin === true || $isEventUser === true) {
|
||||||
|
if ($isAdmin === false) {
|
||||||
|
$eventUsers = $eventUsers->filter(function ($user) use ($authUser) {
|
||||||
|
return $user->id === $authUser->id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
UserConductor::collection($request, $eventUsers),
|
||||||
|
[
|
||||||
|
'isCollection' => true,
|
||||||
|
'resourceName' => 'users'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondNotFound();
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add user to Event
|
||||||
|
* @param Request $request The HTTP request.
|
||||||
|
* @param Event $event Event model.
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function userAdd(Request $request, Event $event): JsonResponse
|
||||||
|
{
|
||||||
|
$authUser = $request->user();
|
||||||
|
if ($authUser !== null && $authUser->hasPermission('admin/events') === true) {
|
||||||
|
if ($request->has("users") === true) {
|
||||||
|
$eventUsers = $event->users()->pluck('user_id')->toArray(); // Get the current users in the event
|
||||||
|
$requestedUsers = $request->input("users"); // Get the requested users
|
||||||
|
|
||||||
|
$usersToAdd = array_diff($requestedUsers, $eventUsers); // Users to add
|
||||||
|
$usersToRemove = array_diff($eventUsers, $requestedUsers); // Users to remove
|
||||||
|
|
||||||
|
// Add missing users
|
||||||
|
foreach ($usersToAdd as $userToAdd) {
|
||||||
|
if (User::find($userToAdd) !== null) {
|
||||||
|
$event->users()->attach($userToAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove extra users
|
||||||
|
foreach ($usersToRemove as $userToRemove) {
|
||||||
|
$event->users()->detach($userToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondNoContent();
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondWithErrors(['users' => 'The user list was not found']);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user
|
||||||
|
* @param Request $request The HTTP request.
|
||||||
|
* @param Event $event Event model.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function userUpdate(Request $request, Event $event): void
|
||||||
|
{
|
||||||
|
// only admin/events permitted
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user from event
|
||||||
|
*
|
||||||
|
* @param Request $request The HTTP request.
|
||||||
|
* @param Event $event Event model.
|
||||||
|
* @param User $user User model.
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function userDelete(Request $request, Event $event, User $user): JsonResponse
|
||||||
|
{
|
||||||
|
$authUser = $request->user();
|
||||||
|
if ($authUser !== null && $authUser->hasPermission('admin/events') === true) {
|
||||||
|
$eventUsers = $event->users;
|
||||||
|
if ($eventUsers->find($user->id) !== null) {
|
||||||
|
$eventUsers->detach($user->id);
|
||||||
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
|
return $this->respondNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
app/Http/Controllers/Api/InfoController.php
Normal file
26
app/Http/Controllers/Api/InfoController.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Enum\HttpResponseCodes;
|
||||||
|
use App\Models\Media;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class InfoController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$info = [
|
||||||
|
"version" => "1.0.0",
|
||||||
|
"max_upload_size" => Media::getMaxUploadSize()
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->respondJson($info);
|
||||||
|
}
|
||||||
|
}
|
||||||
165
app/Http/Controllers/Api/LogController.php
Normal file
165
app/Http/Controllers/Api/LogController.php
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class LogController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ApplicationController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth:sanctum')
|
||||||
|
->only(['show']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param Request $request The log request.
|
||||||
|
* @param string $name The log name.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, string $name)
|
||||||
|
{
|
||||||
|
if ($request->user()?->hasPermission('logs/' . $name) === true) {
|
||||||
|
switch (strtolower($name)) {
|
||||||
|
case 'discord':
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$log = $request->get('log');
|
||||||
|
if ($log === null) {
|
||||||
|
$log = ['output', 'error'];
|
||||||
|
} else {
|
||||||
|
$log = explode(',', strtolower($log));
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = intval($request->get('lines', 50));
|
||||||
|
if ($lines > 100) {
|
||||||
|
$lines = 100;
|
||||||
|
} elseif ($lines < 0) {
|
||||||
|
$lines = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$before = $request->get('before');
|
||||||
|
if ($before !== null) {
|
||||||
|
$before = preg_split(
|
||||||
|
"/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/",
|
||||||
|
$before,
|
||||||
|
-1,
|
||||||
|
(PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)
|
||||||
|
);
|
||||||
|
if (count($before) !== 6) {
|
||||||
|
$before = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$after = $request->get('after');
|
||||||
|
if ($after !== null) {
|
||||||
|
$after = preg_split(
|
||||||
|
"/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/",
|
||||||
|
$after,
|
||||||
|
-1,
|
||||||
|
(PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)
|
||||||
|
);
|
||||||
|
if (count($after) !== 6) {
|
||||||
|
$after = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$logFiles = [
|
||||||
|
[
|
||||||
|
'name' => 'output',
|
||||||
|
'path' => '/home/discordbot/.pm2/logs/stemmech-discordbot-out-0.log'
|
||||||
|
],[
|
||||||
|
'name' => 'error',
|
||||||
|
'path' => '/home/discordbot/.pm2/logs/stemmech-discordbot-error-0.log'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($logFiles as $logFile) {
|
||||||
|
if (in_array($logFile['name'], $log) === true) {
|
||||||
|
$logContent = '';
|
||||||
|
|
||||||
|
if (file_exists($logFile['path']) === true) {
|
||||||
|
$logContent = file_get_contents($logFile['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$logArray = preg_split(
|
||||||
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||||
|
"/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}: (?:(?!\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}: )[\s\S])*)/",
|
||||||
|
$logContent,
|
||||||
|
-1,
|
||||||
|
(PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)
|
||||||
|
);
|
||||||
|
|
||||||
|
$logContent = '';
|
||||||
|
$logLineCount = 0;
|
||||||
|
$logLineSkip = false;
|
||||||
|
foreach (array_reverse($logArray) as $logLine) {
|
||||||
|
$lineDate = preg_split(
|
||||||
|
"/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}): /",
|
||||||
|
$logLine,
|
||||||
|
-1,
|
||||||
|
(PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)
|
||||||
|
);
|
||||||
|
if (count($lineDate) >= 6) {
|
||||||
|
$logLineSkip = false;
|
||||||
|
|
||||||
|
// Is line before
|
||||||
|
if (
|
||||||
|
$before !== null && (
|
||||||
|
$lineDate[0] > $before[0] ||
|
||||||
|
$lineDate[1] > $before[1] ||
|
||||||
|
$lineDate[2] > $before[2] ||
|
||||||
|
$lineDate[3] > $before[3] ||
|
||||||
|
$lineDate[4] > $before[4] ||
|
||||||
|
$lineDate[5] > $before[5]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$logLineSkip = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is line after
|
||||||
|
if (
|
||||||
|
$after !== null && (
|
||||||
|
$after[0] > $lineDate[0] ||
|
||||||
|
$after[1] > $lineDate[1] ||
|
||||||
|
$after[2] > $lineDate[2] ||
|
||||||
|
$after[3] > $lineDate[3] ||
|
||||||
|
$after[4] > $lineDate[4] ||
|
||||||
|
$after[5] > $lineDate[5]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$logLineSkip = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$logLineCount += 1;
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
if ($logLineCount > $lines) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($logLineSkip === false) {
|
||||||
|
$logContent .= $logLine;
|
||||||
|
}
|
||||||
|
}//end foreach
|
||||||
|
|
||||||
|
$data[$logFile['name']] = $logContent;
|
||||||
|
}//end if
|
||||||
|
}//end foreach
|
||||||
|
|
||||||
|
return $this->respondJson([
|
||||||
|
'log' => $data
|
||||||
|
]);
|
||||||
|
}//end switch
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\MediaConductor;
|
||||||
|
use App\Conductors\MediaJobConductor;
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\MediaFilter;
|
use App\Http\Requests\MediaRequest;
|
||||||
use App\Http\Requests\MediaStoreRequest;
|
|
||||||
use App\Http\Requests\MediaUpdateRequest;
|
|
||||||
use App\Models\Media;
|
use App\Models\Media;
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Laravel\Sanctum\PersonalAccessToken;
|
use Laravel\Sanctum\PersonalAccessToken;
|
||||||
|
|
||||||
@@ -26,177 +31,310 @@ class MediaController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\MediaFilter $filter Created filter object.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(MediaFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
list($collection, $total) = MediaConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$filter->filter(),
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
],
|
||||||
|
function ($options) {
|
||||||
|
return $options['total'] === 0;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param MediaFilter $filter The request filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param Media $medium The request media.
|
* @param \App\Models\Media $medium The request media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(MediaFilter $filter, Media $medium)
|
public function show(Request $request, Media $medium)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($medium));
|
if (MediaConductor::viewable($medium) === true) {
|
||||||
|
return $this->respondAsResource(MediaConductor::model($request, $medium));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a new media resource
|
* Store a new media resource
|
||||||
*
|
*
|
||||||
* @param MediaStoreRequest $request The uploaded media.
|
* @param \App\Http\Requests\MediaRequest $request The uploaded media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(MediaStoreRequest $request)
|
public function store(MediaRequest $request)
|
||||||
{
|
{
|
||||||
$file = $request->file('file');
|
// allowed to create a media item
|
||||||
if ($file === null) {
|
if (MediaConductor::creatable() === false) {
|
||||||
return $this->respondError(['file' => 'An error occurred uploading the file to the server.']);
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for file
|
||||||
|
$file = $request->file('file');
|
||||||
|
if ($file === null) {
|
||||||
|
return $this->respondWithErrors(['file' => 'The browser did not upload the file correctly to the server.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->storeOrUpdate($request, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the media resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\MediaRequest $request The update request.
|
||||||
|
* @param \App\Models\Media $medium The specified media.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function update(MediaRequest $request, Media $medium)
|
||||||
|
{
|
||||||
|
// allowed to update a media item
|
||||||
|
if (MediaConductor::updatable($medium) === false) {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->storeOrUpdate($request, $medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a new media resource
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\MediaRequest $request The uploaded media.
|
||||||
|
* @param \App\Models\Media|null $medium The specified media.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function storeOrUpdate(MediaRequest $request, Media|null $medium)
|
||||||
|
{
|
||||||
|
$file = $request->file('file');
|
||||||
|
if ($file !== null) {
|
||||||
|
// validate file object
|
||||||
if ($file->isValid() !== true) {
|
if ($file->isValid() !== true) {
|
||||||
switch ($file->getError()) {
|
switch ($file->getError()) {
|
||||||
case UPLOAD_ERR_INI_SIZE:
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
case UPLOAD_ERR_FORM_SIZE:
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
return $this->respondTooLarge();
|
return $this->respondTooLarge();
|
||||||
case UPLOAD_ERR_PARTIAL:
|
case UPLOAD_ERR_PARTIAL:
|
||||||
return $this->respondError(['file' => 'The file upload was interrupted.']);
|
return $this->respondWithErrors([$file => 'The file upload was interrupted.']);
|
||||||
default:
|
default:
|
||||||
return $this->respondError(['file' => 'An error occurred uploading the file to the server.']);
|
return $this->respondWithErrors(
|
||||||
|
[$file => 'An error occurred uploading the file to the server.']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->getSize() > Media::maxUploadSize()) {
|
if ($file->getSize() > Media::getMaxUploadSize()) {
|
||||||
return $this->respondTooLarge();
|
return $this->respondTooLarge();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$title = $file->getClientOriginalName();
|
// create/get media job
|
||||||
$mime = $file->getMimeType();
|
$mediaJob = null;
|
||||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
$data = [];
|
||||||
if ($fileInfo === null) {
|
|
||||||
return $this->respondError(
|
if ($request->missing('job_id') === true) {
|
||||||
['file' => 'The file could not be stored on the server'],
|
/** @var \App\Models\User */
|
||||||
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$mediaJob = new MediaJob();
|
||||||
|
$mediaJob->user_id = $user->id;
|
||||||
|
if ($medium !== null) {
|
||||||
|
$mediaJob->media_id = $medium->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->has('title') === true || $file !== null) {
|
||||||
|
$data['title'] = $request->get('title', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->has('name') === true || $file !== null) {
|
||||||
|
$data['name'] = (
|
||||||
|
$request->has('chunk') === true ? $request->get('name', '') : $file->getClientOriginalName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->merge([
|
|
||||||
'title' => $title,
|
|
||||||
'mime' => $mime,
|
|
||||||
'name' => $fileInfo['name'],
|
|
||||||
'size' => filesize($fileInfo['path'])
|
|
||||||
]);
|
|
||||||
|
|
||||||
$media = $request->user()->media()->create($request->all());
|
|
||||||
return $this->respondAsResource((new MediaFilter($request))->filter($media));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the media resource in storage.
|
|
||||||
*
|
|
||||||
* @param MediaUpdateRequest $request The update request.
|
|
||||||
* @param \App\Models\Media $medium The specified media.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(MediaUpdateRequest $request, Media $medium)
|
|
||||||
{
|
|
||||||
if ((new MediaFilter($request))->filter($medium) === null) {
|
|
||||||
return $this->respondNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = $request->file('file');
|
|
||||||
if ($file !== null) {
|
if ($file !== null) {
|
||||||
if ($file->getSize() > Media::maxUploadSize()) {
|
$data['size'] = $request->has('chunk') === true ? intval($request->get('size', 0)) : $file->getSize();
|
||||||
return $this->respondTooLarge();
|
$data['mime_type'] = (
|
||||||
}
|
$request->has('chunk') === true ? $request->get('mime_type', '') : $file->getMimeType()
|
||||||
|
|
||||||
$oldPath = $medium->path();
|
|
||||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
|
||||||
if ($fileInfo === null) {
|
|
||||||
return $this->respondError(
|
|
||||||
['file' => 'The file could not be stored on the server'],
|
|
||||||
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($oldPath) === true) {
|
if ($request->has('storage') === true || $file !== null) {
|
||||||
unlink($oldPath);
|
$data['storage'] = $request->get('storage', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->merge([
|
if ($request->has('security_type') === true || $file !== null) {
|
||||||
'title' => $file->getClientOriginalName(),
|
$data['security']['type'] = $request->get('security_type', '');
|
||||||
'mime' => $file->getMimeType(),
|
$data['security']['data'] = $request->get('security_data', '');
|
||||||
'name' => $fileInfo['name'],
|
|
||||||
'size' => filesize($fileInfo['path'])
|
if ($data['security']['type'] === '') {
|
||||||
]);
|
$data['security']['data'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($medium === null || strcasecmp($data['security']['type'], $medium->security_type) !== 0) {
|
||||||
|
if ($request->has('storage') === false) {
|
||||||
|
$mime_type = $request->get('mime_type', $medium === null ? '' : $medium->mime_type);
|
||||||
|
$data['storage'] = Media::recommendedStorage($mime_type, $data['security']['type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
array_key_exists('storage', $data) === true && (
|
||||||
|
array_key_exists('security', $data) === true &&
|
||||||
|
array_key_exists('type', $data['security']) === true
|
||||||
|
) &&
|
||||||
|
array_key_exists('mime_type', $data) === true &&
|
||||||
|
$data['mime_type'] !== ""
|
||||||
|
) {
|
||||||
|
$error = Media::verifyStorage($data['mime_type'], $data['security']['type'], $data['storage']);
|
||||||
|
// Log::error($data['mime_type'] . ' - ' . $data['security']['type'] . ' - ' . $data['storage']);
|
||||||
|
switch ($error) {
|
||||||
|
case Media::STORAGE_VALID:
|
||||||
|
break;
|
||||||
|
case Media::STORAGE_MIME_MISSING:
|
||||||
|
return $this->respondWithErrors(['mime_type' => 'The file type is required.']);
|
||||||
|
case Media::STORAGE_NOT_FOUND:
|
||||||
|
return $this->respondWithErrors(['storage' => 'Storage was not found.']);
|
||||||
|
case Media::STORAGE_INVALID_SECURITY:
|
||||||
|
return $this->respondWithErrors(
|
||||||
|
['storage' => 'Storage invalid for this security requirement.']
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return $this->respondWithErrors(['storage' => 'Storage verification error occurred.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->has('transform') === true) {
|
||||||
|
$transform = [];
|
||||||
|
|
||||||
|
foreach (explode(',', $request->get('transform', '')) as $value) {
|
||||||
|
if (is_string($value) === true) {
|
||||||
|
if (preg_match('/^rotate-(-?\d+)$/', $value, $matches) !== false) {
|
||||||
|
$transform['rotate'] = $matches[1];
|
||||||
|
} elseif (preg_match('/^flip-([vh]|vh|hv)$/', $value, $matches) !== false) {
|
||||||
|
$transform['flip'] = $matches[1];
|
||||||
|
} elseif (preg_match('/^crop-(\d+)-(\d+)$/', $value, $matches) !== false) {
|
||||||
|
$transform['crop'] = ['width' => $matches[1], 'height' => $matches[2]];
|
||||||
|
} elseif (preg_match('/^crop-(\d+)-(\d+)-(\d+)-(\d+)$/', $value, $matches) !== false) {
|
||||||
|
$transform['crop'] = [
|
||||||
|
'width' => $matches[1],
|
||||||
|
'height' => $matches[2],
|
||||||
|
'x' => $matches[3],
|
||||||
|
'y' => $matches[4]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($transform) > 0) {
|
||||||
|
$data['transform'] = $transform;
|
||||||
|
}
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
$medium->update($request->all());
|
$mediaJob->setStatusWaiting();
|
||||||
return $this->respondWithTransformer($file);
|
} else {
|
||||||
|
$mediaJob = MediaJob::find($request->get('job_id'));
|
||||||
|
if ($mediaJob === null || $mediaJob->exists() === false) {
|
||||||
|
$this->respondNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data = json_decode($mediaJob->data, true);
|
||||||
|
if ($data === null) {
|
||||||
|
Log::error(`{$mediaJob->id} contains no data`);
|
||||||
|
return $this->respondServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('name', $data) === false) {
|
||||||
|
Log::error(`{$mediaJob->id} data does not contain the name key`);
|
||||||
|
return $this->respondServerError();
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
if ($mediaJob === null) {
|
||||||
|
Log::error(`media job does not exist`);
|
||||||
|
return $this->respondServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
// save uploaded file
|
||||||
|
if ($file !== null) {
|
||||||
|
if ($data['name'] === '') {
|
||||||
|
Log::error(`filename does not exist`);
|
||||||
|
return $this->respondServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
$temporaryFilePath = generateTempFilePath(
|
||||||
|
pathinfo($data['name'], PATHINFO_EXTENSION),
|
||||||
|
$request->get('chunk', '')
|
||||||
|
);
|
||||||
|
copy($file->path(), $temporaryFilePath);
|
||||||
|
|
||||||
|
if ($request->has('chunk') === true) {
|
||||||
|
$data['chunks'][$request->get('chunk', '1')] = $temporaryFilePath;
|
||||||
|
$data['chunk_count'] = $request->get('chunk_count', 1);
|
||||||
|
} else {
|
||||||
|
$data['file'] = $temporaryFilePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mediaJob->data = json_encode($data, true);
|
||||||
|
$mediaJob->save();
|
||||||
|
$mediaJob->process();
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
MediaJobConductor::model($request, $mediaJob),
|
||||||
|
['resourceName' => 'media_job', 'respondCode' => HttpResponseCodes::HTTP_ACCEPTED]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*
|
*
|
||||||
* @param Request $request Request instance.
|
|
||||||
* @param \App\Models\Media $medium Specified media file.
|
* @param \App\Models\Media $medium Specified media file.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function destroy(Request $request, Media $medium)
|
public function destroy(Media $medium)
|
||||||
{
|
{
|
||||||
if ((new MediaFilter($request))->filter($medium) !== null) {
|
if (MediaConductor::destroyable($medium) === true) {
|
||||||
if (file_exists($medium->path()) === true) {
|
|
||||||
unlink($medium->path());
|
|
||||||
}
|
|
||||||
|
|
||||||
$medium->delete();
|
$medium->delete();
|
||||||
return $this->respondNoContent();
|
return $this->respondNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->respondNotFound();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param Request $request Request instance.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param \App\Models\Media $medium Specified media.
|
* @param \App\Models\Media $media Specified media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function download(Request $request, Media $medium)
|
public function download(Request $request, Media $media): Response
|
||||||
{
|
{
|
||||||
$respondJson = in_array('application/json', explode(',', $request->header('Accept', 'application/json')));
|
|
||||||
|
|
||||||
$headers = [];
|
$headers = [];
|
||||||
$path = $medium->path();
|
|
||||||
|
|
||||||
/* File exists */
|
/* Check file exists */
|
||||||
if (file_exists($path) === false) {
|
if (Storage::disk($media->storage)->exists($media->name) === false) {
|
||||||
if ($respondJson === false) {
|
|
||||||
return redirect('/not-found');
|
|
||||||
} else {
|
|
||||||
return $this->respondNotFound();
|
return $this->respondNotFound();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$updated_at = Carbon::parse(filemtime($path));
|
$updated_at = Carbon::parse(Storage::disk($media->storage)->lastModified($media->name));
|
||||||
|
|
||||||
$headerPragma = 'no-cache';
|
$headerPragma = 'no-cache';
|
||||||
$headerCacheControl = 'max-age=0, must-revalidate';
|
$headerCacheControl = 'max-age=0, must-revalidate';
|
||||||
$headerExpires = $updated_at->toRfc2822String();
|
$headerExpires = $updated_at->toRfc2822String();
|
||||||
|
|
||||||
if (empty($medium->permission) === true) {
|
/* construct user if can */
|
||||||
if ($request->user() === null && $request->has('token') === true) {
|
$user = $request->user();
|
||||||
|
if ($user === null && $request->has('token') === true) {
|
||||||
$accessToken = PersonalAccessToken::findToken(urldecode($request->input('token')));
|
$accessToken = PersonalAccessToken::findToken(urldecode($request->input('token')));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -206,24 +344,38 @@ class MediaController extends ApiController
|
|||||||
$user = $accessToken->tokenable;
|
$user = $accessToken->tokenable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($request->user() === null || $user->hasPermission($medium->permission) === false) {
|
|
||||||
if ($respondJson === false) {
|
if ($media->security_type === '') {
|
||||||
return redirect('/login?redirect=' . $request->path());
|
/* no security */
|
||||||
} else {
|
|
||||||
return $this->respondForbidden();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$headerPragma = 'public';
|
$headerPragma = 'public';
|
||||||
$headerExpires = $updated_at->addMonth()->toRfc2822String();
|
$headerExpires = $updated_at->addMonth()->toRfc2822String();
|
||||||
|
} elseif (strcasecmp('password', $media->security_type) === 0) {
|
||||||
|
/* password */
|
||||||
|
if (
|
||||||
|
($user === null || $user->hasPermission('admin/media') === false) &&
|
||||||
|
($request->has('password') === false || $request->get('password') !== $media->security_data)
|
||||||
|
) {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
} elseif (strcasecmp('permission', $media->security_type) === 0) {
|
||||||
|
/* permission */
|
||||||
|
if (
|
||||||
|
$user === null || (
|
||||||
|
$user->hasPermission('admin/media') === false &&
|
||||||
|
$user->hasPermission($media->security_data) === false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
|
// deepcode ignore InsecureHash: Browsers expect Etag to be a md5 hash
|
||||||
$headerEtag = md5($updated_at->format('U'));
|
$headerEtag = md5($updated_at->format('U'));
|
||||||
$headerLastModified = $updated_at->toRfc2822String();
|
$headerLastModified = $updated_at->toRfc2822String();
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
'Cache-Control' => $headerCacheControl,
|
'Cache-Control' => $headerCacheControl,
|
||||||
'Content-Disposition' => sprintf('inline; filename="%s"', basename($path)),
|
'Content-Disposition' => sprintf('inline; filename="%s"', basename($media->name)),
|
||||||
'Etag' => $headerEtag,
|
'Etag' => $headerEtag,
|
||||||
'Expires' => $headerExpires,
|
'Expires' => $headerExpires,
|
||||||
'Last-Modified' => $headerLastModified,
|
'Last-Modified' => $headerLastModified,
|
||||||
@@ -242,6 +394,50 @@ class MediaController extends ApiController
|
|||||||
return response()->make('', 304, $headers);
|
return response()->make('', 304, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->file($path, $headers);
|
$headers['Content-Type'] = Storage::disk($media->storage)->mimeType($media->name);
|
||||||
|
$headers['Content-Length'] = Storage::disk($media->storage)->size($media->name);
|
||||||
|
$headers['Content-Disposition'] = 'attachment; filename="' . basename($media->name) . '"';
|
||||||
|
|
||||||
|
$stream = Storage::disk($media->storage)->readStream($media->name);
|
||||||
|
return response()->stream(
|
||||||
|
function () use ($stream) {
|
||||||
|
while (ob_get_level() > 0) {
|
||||||
|
ob_end_flush();
|
||||||
|
}
|
||||||
|
fpassthru($stream);
|
||||||
|
},
|
||||||
|
200,
|
||||||
|
$headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a File item in a request is valid
|
||||||
|
*
|
||||||
|
* @param UploadedFile $file The file to validate.
|
||||||
|
* @param string $errorKey The error key to use.
|
||||||
|
* @return JsonResponse|null
|
||||||
|
*/
|
||||||
|
private function validateFileObject(UploadedFile $file, string $errorKey = 'file'): JsonResponse|null
|
||||||
|
{
|
||||||
|
if ($file->isValid() !== true) {
|
||||||
|
switch ($file->getError()) {
|
||||||
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
|
return $this->respondTooLarge();
|
||||||
|
case UPLOAD_ERR_PARTIAL:
|
||||||
|
return $this->respondWithErrors([$errorKey => 'The file upload was interrupted.']);
|
||||||
|
default:
|
||||||
|
return $this->respondWithErrors(
|
||||||
|
[$errorKey => 'An error occurred uploading the file to the server.']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file->getSize() > Media::getMaxUploadSize()) {
|
||||||
|
return $this->respondTooLarge();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
app/Http/Controllers/Api/MediaJobController.php
Normal file
52
app/Http/Controllers/Api/MediaJobController.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\MediaJobConductor;
|
||||||
|
use App\Http\Controllers\Api\ApiController;
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class MediaJobController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
list($collection, $total) = MediaJobConductor::request($request);
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$collection,
|
||||||
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total],
|
||||||
|
'resourceName' => 'media_job'
|
||||||
|
],
|
||||||
|
function ($options) {
|
||||||
|
return $options['total'] === 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @param \App\Models\MediaJob $mediaJob The request media job.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, MediaJob $mediaJob)
|
||||||
|
{
|
||||||
|
if (MediaJobConductor::viewable($mediaJob) === true) {
|
||||||
|
return $this->respondAsResource(
|
||||||
|
MediaJobConductor::model($request, $mediaJob),
|
||||||
|
['resourceName' => 'media_job']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
234
app/Http/Controllers/Api/OCRController.php
Normal file
234
app/Http/Controllers/Api/OCRController.php
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use thiagoalessio\TesseractOCR\TesseractOCR;
|
||||||
|
use FFMpeg;
|
||||||
|
use App\Enum\CurlErrorCodes;
|
||||||
|
|
||||||
|
class OCRController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ApplicationController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// $this->middleware('auth:sanctum')
|
||||||
|
// ->only(['show']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param Request $request The log request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request)
|
||||||
|
{
|
||||||
|
// if ($request->user()?->hasPermission('logs/' . $name) === true) {
|
||||||
|
$url = $request->get('url');
|
||||||
|
if ($url !== null) {
|
||||||
|
$data = ['ocr' => []];
|
||||||
|
|
||||||
|
$filters = $request->get('filters', ['tesseract']);
|
||||||
|
if (is_array($filters) === false) {
|
||||||
|
$filters = explode(',', $filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tesseractOEM = $request->get('tesseract.oem');
|
||||||
|
$tesseractDigits = $request->get('tesseract.digits');
|
||||||
|
$tesseractAllowlist = $request->get('tesseract.allowlist');
|
||||||
|
|
||||||
|
// Download URL
|
||||||
|
$urlDownloadFilePath = tempnam(sys_get_temp_dir(), 'download');
|
||||||
|
$maxDownloadSize = (1024 * 1024); // 1MB
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
// We need progress updates to break the connection mid-way
|
||||||
|
curl_setopt($ch, CURLOPT_BUFFERSIZE, 128); // more progress info
|
||||||
|
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
|
||||||
|
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function (
|
||||||
|
$downloadSize,
|
||||||
|
$downloaded,
|
||||||
|
$uploadSize,
|
||||||
|
$uploaded
|
||||||
|
) use ($maxDownloadSize) {
|
||||||
|
return ($downloaded > $maxDownloadSize) ? 1 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
$curlResult = curl_exec($ch);
|
||||||
|
$curlError = curl_errno($ch);
|
||||||
|
$curlSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
||||||
|
curl_close($ch);
|
||||||
|
if ($curlError !== 0) {
|
||||||
|
$error = 'File size is larger then allowed';
|
||||||
|
if ($curlError !== CurlErrorCodes::CURLE_ABORTED_BY_CALLBACK) {
|
||||||
|
$error = CurlErrorCodes::getMessage($curlError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondWithErrors(['url' => $error]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save url file
|
||||||
|
file_put_contents($urlDownloadFilePath, $curlResult);
|
||||||
|
$urlDownloadFilePathBase = preg_replace('/\\.[^.\\s]{3,4}$/', '', $urlDownloadFilePath);
|
||||||
|
|
||||||
|
// tesseract (overall)
|
||||||
|
$ocr = null;
|
||||||
|
foreach ($filters as $filterItem) {
|
||||||
|
if (str_starts_with($filterItem, 'tesseract') === true) {
|
||||||
|
$ocr = new TesseractOCR();
|
||||||
|
$ocr->image($urlDownloadFilePath);
|
||||||
|
if ($tesseractOEM !== null) {
|
||||||
|
$ocr->oem($tesseractOEM);
|
||||||
|
}
|
||||||
|
if ($tesseractDigits !== null) {
|
||||||
|
$ocr->digits();
|
||||||
|
}
|
||||||
|
if ($tesseractAllowlist !== null) {
|
||||||
|
$ocr->allowlist($tesseractAllowlist);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image Filter Function
|
||||||
|
$tesseractImageFilterFunc = function ($filter, $options = null) use ($curlResult, $curlSize, $ocr) {
|
||||||
|
$result = '';
|
||||||
|
$img = imagecreatefromstring($curlResult);
|
||||||
|
if (
|
||||||
|
$img !== false && (($options !== null && imagefilter($img, $filter, $options) === true) ||
|
||||||
|
($options === null && imagefilter($img, $filter) === true))
|
||||||
|
) {
|
||||||
|
ob_start();
|
||||||
|
imagepng($img);
|
||||||
|
$imgData = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
$imgDataSize = strlen($imgData);
|
||||||
|
|
||||||
|
$ocr->imageData($imgData, $imgDataSize);
|
||||||
|
imagedestroy($img);
|
||||||
|
|
||||||
|
$result = $ocr->run(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Image Scale Function
|
||||||
|
$tesseractImageScaleFunc = function ($scaleFunc) use ($curlResult, $ocr) {
|
||||||
|
$result = '';
|
||||||
|
$srcImage = imagecreatefromstring($curlResult);
|
||||||
|
$srcWidth = imagesx($srcImage);
|
||||||
|
$srcHeight = imagesy($srcImage);
|
||||||
|
|
||||||
|
$dstWidth = $scaleFunc($srcWidth);
|
||||||
|
$dstHeight = $scaleFunc($srcHeight);
|
||||||
|
$dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
|
||||||
|
|
||||||
|
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
imagepng($dstImage);
|
||||||
|
$imgData = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
$imgDataSize = strlen($imgData);
|
||||||
|
|
||||||
|
imagedestroy($srcImage);
|
||||||
|
imagedestroy($dstImage);
|
||||||
|
|
||||||
|
$ocr->imageData($imgData, $imgDataSize);
|
||||||
|
$result = $ocr->run(500);
|
||||||
|
return $result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// filter: tesseract
|
||||||
|
if (in_array('tesseract', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract'] = $ocr->run(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.grayscale
|
||||||
|
if (in_array('tesseract.grayscale', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.grayscale'] = $tesseractImageFilterFunc(IMG_FILTER_GRAYSCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.double_scale
|
||||||
|
if (in_array('tesseract.double_scale', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.double_scale'] = $tesseractImageScaleFunc(function ($size) {
|
||||||
|
return $size * 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.half_scale
|
||||||
|
if (in_array('tesseract.half_scale', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.half_scale'] = $tesseractImageScaleFunc(function ($size) {
|
||||||
|
return $size / 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.edgedetect
|
||||||
|
if (in_array('tesseract.edgedetect', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.edgedetect'] = $tesseractImageFilterFunc(IMG_FILTER_EDGEDETECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.mean_removal
|
||||||
|
if (in_array('tesseract.mean_removal', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.mean_removal'] = $tesseractImageFilterFunc(IMG_FILTER_MEAN_REMOVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.negate
|
||||||
|
if (in_array('tesseract.negate', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.negate'] = $tesseractImageFilterFunc(IMG_FILTER_NEGATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: tesseract.pixelate
|
||||||
|
if (in_array('tesseract.pixelate', $filters) === true) {
|
||||||
|
$data['ocr']['tesseract.pixelate'] = $tesseractImageFilterFunc(IMG_FILTER_PIXELATE, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter: keras
|
||||||
|
if (in_array('keras', $filters) === true) {
|
||||||
|
$cmd = '/usr/bin/python3 ' . base_path() . '/scripts/keras_oc.py ' . urlencode($url);
|
||||||
|
$command = escapeshellcmd($cmd);
|
||||||
|
$output = shell_exec($cmd);
|
||||||
|
if ($output !== null && strlen($output) > 0) {
|
||||||
|
$output = substr($output, (strpos($output, '----------START----------') + 25));
|
||||||
|
} else {
|
||||||
|
$output = '';
|
||||||
|
}
|
||||||
|
$data['ocr']['keras'] = $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink($urlDownloadFilePath);
|
||||||
|
return $this->respondJson($data);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondWithErrors(['url' => 'url is missing']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// $ffmpeg = FFMpeg\FFMpeg::create();
|
||||||
|
|
||||||
|
// // Load the input video
|
||||||
|
// $inputFile = $ffmpeg->open('input.mp4');
|
||||||
|
|
||||||
|
// // Split the video into individual frames
|
||||||
|
// $videoFrames = $inputFile->frames();
|
||||||
|
// foreach ($videoFrames as $frame) {
|
||||||
|
// // Save the frame as a PNG
|
||||||
|
// $frame->save(new FFMpeg\Format\Video\PNG(), 'frame-' . $frame->getMetadata('pts') . '.png');
|
||||||
|
|
||||||
|
// // Pass the PNG to Tesseract for processing
|
||||||
|
// exec("tesseract frame-" . $frame->getMetadata('pts') . ".png output");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Read the output from Tesseract
|
||||||
|
// $text = file_get_contents("output.txt");
|
||||||
|
|
||||||
|
// // Do something with the text from Tesseract
|
||||||
|
// echo $text;
|
||||||
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use App\Enum\HttpResponseCodes;
|
|
||||||
use App\Filters\PostFilter;
|
|
||||||
use App\Http\Requests\PostStoreRequest;
|
|
||||||
use App\Http\Requests\PostUpdateRequest;
|
|
||||||
use App\Models\Post;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class PostController extends ApiController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* ApplicationController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth:sanctum')
|
|
||||||
->only([
|
|
||||||
'store',
|
|
||||||
'update',
|
|
||||||
'delete'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* @param \App\Filters\PostFilter $filter Post filter request.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index(PostFilter $filter)
|
|
||||||
{
|
|
||||||
return $this->respondAsResource(
|
|
||||||
$filter->filter(),
|
|
||||||
['total' => $filter->foundTotal()]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*
|
|
||||||
* @param PostFilter $filter The filter request.
|
|
||||||
* @param \App\Models\Post $post The post model.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function show(PostFilter $filter, Post $post)
|
|
||||||
{
|
|
||||||
return $this->respondAsResource($filter->filter($post));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param PostStoreRequest $request The post store request.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(PostStoreRequest $request)
|
|
||||||
{
|
|
||||||
$post = Post::create($request->all());
|
|
||||||
return $this->respondAsResource(
|
|
||||||
(new PostFilter($request))->filter($post),
|
|
||||||
null,
|
|
||||||
HttpResponseCodes::HTTP_CREATED
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*
|
|
||||||
* @param PostUpdateRequest $request The post update request.
|
|
||||||
* @param \App\Models\Post $post The specified post.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(PostUpdateRequest $request, Post $post)
|
|
||||||
{
|
|
||||||
$post->update($request->all());
|
|
||||||
return $this->respondAsResource((new PostFilter($request))->filter($post));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*
|
|
||||||
* @param \App\Models\Post $post The specified post.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(Post $post)
|
|
||||||
{
|
|
||||||
$post->delete();
|
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
111
app/Http/Controllers/Api/ShortlinkController.php
Normal file
111
app/Http/Controllers/Api/ShortlinkController.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\ShortlinkConductor;
|
||||||
|
use App\Enum\HttpResponseCodes;
|
||||||
|
use App\Http\Requests\ShortlinkRequest;
|
||||||
|
use App\Models\Shortlink;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ShortlinkController extends ApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* ApplicationController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth:sanctum')
|
||||||
|
->only(['store','update','destroy']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
list($collection, $total) = ShortlinkConductor::request($request);
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$collection,
|
||||||
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
],
|
||||||
|
function ($options) {
|
||||||
|
return $options['total'] === 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @param \App\Models\Shortlink $shortlink The request shortlink.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Shortlink $shortlink)
|
||||||
|
{
|
||||||
|
if (ShortlinkConductor::viewable($shortlink) === true) {
|
||||||
|
return $this->respondAsResource(ShortlinkConductor::model($request, $shortlink));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a new media resource
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\ShortlinkRequest $request The shortlink.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(ShortlinkRequest $request)
|
||||||
|
{
|
||||||
|
if (ShortlinkConductor::creatable() === true) {
|
||||||
|
$shortlink = Shortlink::create($request->all());
|
||||||
|
|
||||||
|
return $this->respondAsResource(
|
||||||
|
ShortlinkConductor::model($request, $shortlink),
|
||||||
|
['respondCode' => HttpResponseCodes::HTTP_ACCEPTED]
|
||||||
|
);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the media resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\ShortlinkRequest $request The update request.
|
||||||
|
* @param \App\Models\Shortlink $shortlink The specified shortlink.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function update(ShortlinkRequest $request, Shortlink $shortlink)
|
||||||
|
{
|
||||||
|
if (ShortlinkConductor::updatable($shortlink) === true) {
|
||||||
|
$shortlink->update($request->all());
|
||||||
|
return $this->respondAsResource(ShortlinkConductor::model($request, $shortlink));
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Shortlink $shortlink Specified shortlink.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function destroy(Shortlink $shortlink)
|
||||||
|
{
|
||||||
|
if (ShortlinkConductor::destroyable($shortlink) === true) {
|
||||||
|
$shortlink->delete();
|
||||||
|
return $this->respondNoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use App\Models\Subscription;
|
|
||||||
use App\Filters\SubscriptionFilter;
|
|
||||||
use App\Http\Requests\SubscriptionRequest;
|
|
||||||
use App\Jobs\SendEmailJob;
|
|
||||||
use App\Mail\SubscriptionConfirm;
|
|
||||||
use App\Mail\SubscriptionUnsubscribed;
|
|
||||||
|
|
||||||
class SubscriptionController extends ApiController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* ApplicationController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth:sanctum')
|
|
||||||
->except(['store', 'destroyByEmail']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of subscribers.
|
|
||||||
*
|
|
||||||
* @param \App\Filters\SubscriptionFilter $filter Filter object.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index(SubscriptionFilter $filter)
|
|
||||||
{
|
|
||||||
$collection = $filter->filter();
|
|
||||||
return $this->respondAsResource(
|
|
||||||
$collection,
|
|
||||||
['total' => $filter->foundTotal()]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a subscriber email in the database.
|
|
||||||
*
|
|
||||||
* @param SubscriptionRequest $request The subscriber update request.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(SubscriptionRequest $request)
|
|
||||||
{
|
|
||||||
if (Subscription::where('email', $request->email)->first() !== null) {
|
|
||||||
return $this->respondWithErrors(['email' => 'This email address has already subscribed']);
|
|
||||||
}
|
|
||||||
|
|
||||||
Subscription::create($request->all());
|
|
||||||
dispatch((new SendEmailJob($request->email, new SubscriptionConfirm($request->email))))->onQueue('mail');
|
|
||||||
|
|
||||||
return $this->respondCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified user.
|
|
||||||
*
|
|
||||||
* @param SubscriptionFilter $filter The subscription filter.
|
|
||||||
* @param Subscription $subscription The subscription model.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function show(SubscriptionFilter $filter, Subscription $subscription)
|
|
||||||
{
|
|
||||||
return $this->respondAsResource($filter->filter($subscription));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*
|
|
||||||
* @param SubscriptionRequest $request The subscription update request.
|
|
||||||
* @param Subscription $subscription The specified subscription.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(SubscriptionRequest $request, Subscription $subscription)
|
|
||||||
{
|
|
||||||
// $input = [];
|
|
||||||
// $updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
|
||||||
|
|
||||||
// if ($request->user()->hasPermission('admin/user') === true) {
|
|
||||||
// $updatable = array_merge($updatable, ['email_verified_at']);
|
|
||||||
// } elseif ($request->user()->is($user) !== true) {
|
|
||||||
// return $this->respondForbidden();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $input = $request->only($updatable);
|
|
||||||
// if (array_key_exists('password', $input) === true) {
|
|
||||||
// $input['password'] = Hash::make($request->input('password'));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $user->update($input);
|
|
||||||
|
|
||||||
// return $this->respondAsResource((new UserFilter($request))->filter($user));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the user from the database.
|
|
||||||
*
|
|
||||||
* @param Subscription $subscription The specified subscription.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(Subscription $subscription)
|
|
||||||
{
|
|
||||||
// if ($user->hasPermission('admin/user') === false) {
|
|
||||||
// return $this->respondForbidden();
|
|
||||||
// }
|
|
||||||
|
|
||||||
$email = $subscription->email;
|
|
||||||
|
|
||||||
$subscription->delete();
|
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the user from the database.
|
|
||||||
*
|
|
||||||
* @param SubscriptionRequest $request The specified subscription.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroyByEmail(SubscriptionRequest $request)
|
|
||||||
{
|
|
||||||
$subscription = Subscription::where('email', $request->email)->first();
|
|
||||||
if ($subscription !== null) {
|
|
||||||
$subscription->delete();
|
|
||||||
dispatch((new SendEmailJob($request->email, new SubscriptionUnsubscribed($request->email))))->onQueue('mail');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\EventConductor;
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\UserFilter;
|
use App\Http\Requests\UserRequest;
|
||||||
use App\Http\Requests\UserUpdateRequest;
|
|
||||||
use App\Http\Requests\UserStoreRequest;
|
|
||||||
use App\Http\Requests\UserForgotPasswordRequest;
|
use App\Http\Requests\UserForgotPasswordRequest;
|
||||||
use App\Http\Requests\UserForgotUsernameRequest;
|
|
||||||
use App\Http\Requests\UserRegisterRequest;
|
use App\Http\Requests\UserRegisterRequest;
|
||||||
use App\Http\Requests\UserResendVerifyEmailRequest;
|
use App\Http\Requests\UserResendVerifyEmailRequest;
|
||||||
use App\Http\Requests\UserResetPasswordRequest;
|
use App\Http\Requests\UserResetPasswordRequest;
|
||||||
@@ -16,13 +14,15 @@ use App\Jobs\SendEmailJob;
|
|||||||
use App\Mail\ChangedEmail;
|
use App\Mail\ChangedEmail;
|
||||||
use App\Mail\ChangedPassword;
|
use App\Mail\ChangedPassword;
|
||||||
use App\Mail\ChangeEmailVerify;
|
use App\Mail\ChangeEmailVerify;
|
||||||
use App\Mail\ForgotUsername;
|
|
||||||
use App\Mail\ForgotPassword;
|
use App\Mail\ForgotPassword;
|
||||||
use App\Mail\EmailVerify;
|
use App\Mail\EmailVerify;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\UserCode;
|
use App\Models\UserCode;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use App\Conductors\UserConductor;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
|
||||||
class UserController extends ApiController
|
class UserController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -38,73 +38,81 @@ class UserController extends ApiController
|
|||||||
'register',
|
'register',
|
||||||
'exists',
|
'exists',
|
||||||
'forgotPassword',
|
'forgotPassword',
|
||||||
'forgotUsername',
|
|
||||||
'resetPassword',
|
'resetPassword',
|
||||||
'verifyEmail',
|
'verifyEmail',
|
||||||
'resendVerifyEmailCode'
|
'resendVerifyEmailCode',
|
||||||
|
'eventList',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\UserFilter $filter Filter object.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(UserFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$collection = $filter->filter();
|
list($collection, $total) = UserConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$collection,
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created user in the database.
|
* Store a newly created user in the database.
|
||||||
*
|
*
|
||||||
* @param UserStoreRequest $request The user update request.
|
* @param \App\Http\Requests\UserRequest $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(UserStoreRequest $request)
|
public function store(UserRequest $request)
|
||||||
{
|
{
|
||||||
if ($request->user()->hasPermission('admin/user') !== true) {
|
if (UserConductor::creatable() === true) {
|
||||||
|
$user = User::create($request->all());
|
||||||
|
return $this->respondAsResource(
|
||||||
|
UserConductor::model($request, $user),
|
||||||
|
['respondCode' => HttpResponseCodes::HTTP_CREATED]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
return $this->respondForbidden();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = User::create($request->all());
|
|
||||||
return $this->respondAsResource((new UserFilter($request))->filter($user), [], HttpResponseCodes::HTTP_CREATED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified user.
|
* Display the specified user.
|
||||||
*
|
*
|
||||||
* @param UserFilter $filter The user filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param User $user The user model.
|
* @param \App\Models\User $user The user model.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(UserFilter $filter, User $user)
|
public function show(Request $request, User $user)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($user));
|
if (UserConductor::viewable($user) === true) {
|
||||||
|
return $this->respondAsResource(UserConductor::model($request, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param UserUpdateRequest $request The user update request.
|
* @param \App\Http\Requests\UserRequest $request The user update request.
|
||||||
* @param User $user The specified user.
|
* @param \App\Models\User $user The specified user.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(UserUpdateRequest $request, User $user)
|
public function update(UserRequest $request, User $user)
|
||||||
{
|
{
|
||||||
|
if (UserConductor::updatable($user) === true) {
|
||||||
$input = [];
|
$input = [];
|
||||||
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
$updatable = ['first_name', 'last_name', 'email', 'phone', 'password', 'display_name'];
|
||||||
|
|
||||||
if ($request->user()->hasPermission('admin/user') === true) {
|
if ($request->user()->hasPermission('admin/user') === true) {
|
||||||
$updatable = array_merge($updatable, ['email_verified_at']);
|
$updatable = array_merge($updatable, ['email_verified_at']);
|
||||||
} elseif ($request->user()->is($user) !== true) {
|
|
||||||
return $this->respondForbidden();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$input = $request->only($updatable);
|
$input = $request->only($updatable);
|
||||||
@@ -114,44 +122,59 @@ class UserController extends ApiController
|
|||||||
|
|
||||||
$user->update($input);
|
$user->update($input);
|
||||||
|
|
||||||
return $this->respondAsResource((new UserFilter($request))->filter($user));
|
return $this->respondAsResource(UserConductor::model($request, $user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the user from the database.
|
* Remove the user from the database.
|
||||||
*
|
*
|
||||||
* @param User $user The specified user.
|
* @param \App\Models\User $user The specified user.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function destroy(User $user)
|
public function destroy(User $user)
|
||||||
{
|
{
|
||||||
if ($user->hasPermission('admin/user') === false) {
|
if (UserConductor::destroyable($user) === true) {
|
||||||
return $this->respondForbidden();
|
|
||||||
}
|
|
||||||
|
|
||||||
$user->delete();
|
$user->delete();
|
||||||
return $this->respondNoContent();
|
return $this->respondNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new user
|
* Register a new user
|
||||||
*
|
*
|
||||||
* @param UserRegisterRequest $request The register user request.
|
* @param \App\Http\Requests\UserRegisterRequest $request The register user request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function register(UserRegisterRequest $request)
|
public function register(UserRegisterRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$user = User::create([
|
$userData = $request->only([
|
||||||
'first_name' => $request->input('first_name'),
|
'first_name',
|
||||||
'last_name' => $request->input('last_name'),
|
'last_name',
|
||||||
'username' => $request->input('username'),
|
'email',
|
||||||
'email' => $request->input('email'),
|
'phone',
|
||||||
'phone' => $request->input('phone'),
|
'password',
|
||||||
'password' => Hash::make($request->input('password'))
|
'display_name',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$userData['password'] = Hash::make($userData['password']);
|
||||||
|
|
||||||
|
$user = User::where('email', $request->input('email'))
|
||||||
|
->whereNull('password')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
|
$user = User::create($userData);
|
||||||
|
} else {
|
||||||
|
unset($userData['email']);
|
||||||
|
$user->update($userData);
|
||||||
|
}//end if
|
||||||
|
|
||||||
$code = $user->codes()->create([
|
$code = $user->codes()->create([
|
||||||
'action' => 'verify-email',
|
'action' => 'verify-email',
|
||||||
]);
|
]);
|
||||||
@@ -168,35 +191,15 @@ class UserController extends ApiController
|
|||||||
}//end try
|
}//end try
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an email with all the usernames registered at that address
|
|
||||||
*
|
|
||||||
* @param UserForgotUsernameRequest $request The forgot username request.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function forgotUsername(UserForgotUsernameRequest $request)
|
|
||||||
{
|
|
||||||
$users = User::where('email', $request->input('email'))->whereNotNull('email_verified_at')->get();
|
|
||||||
if ($users->count() > 0) {
|
|
||||||
dispatch((new SendEmailJob(
|
|
||||||
$users->first()->email,
|
|
||||||
new ForgotUsername($users->pluck('username')->toArray())
|
|
||||||
)))->onQueue('mail');
|
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->respondJson(['message' => 'Username send to the email address if registered']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new reset password code
|
* Generates a new reset password code
|
||||||
*
|
*
|
||||||
* @param UserForgotPasswordRequest $request The reset password request.
|
* @param \App\Http\Requests\UserForgotPasswordRequest $request The reset password request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function forgotPassword(UserForgotPasswordRequest $request)
|
public function forgotPassword(UserForgotPasswordRequest $request)
|
||||||
{
|
{
|
||||||
$user = User::where('username', $request->input('username'))->first();
|
$user = User::where('email', $request->input('email'))->first();
|
||||||
if ($user !== null) {
|
if ($user !== null) {
|
||||||
$user->codes()->where('action', 'reset-password')->delete();
|
$user->codes()->where('action', 'reset-password')->delete();
|
||||||
$code = $user->codes()->create([
|
$code = $user->codes()->create([
|
||||||
@@ -213,7 +216,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Resets a user password
|
* Resets a user password
|
||||||
*
|
*
|
||||||
* @param UserResetPasswordRequest $request The reset password request.
|
* @param \App\Http\Requests\UserResetPasswordRequest $request The reset password request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function resetPassword(UserResetPasswordRequest $request)
|
public function resetPassword(UserResetPasswordRequest $request)
|
||||||
@@ -240,14 +243,14 @@ class UserController extends ApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $this->respondError([
|
return $this->respondError([
|
||||||
'code' => 'The code was not found or has expired'
|
'code' => 'The code was not found or has expired.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify an email code
|
* Verify an email code
|
||||||
*
|
*
|
||||||
* @param UserVerifyEmailRequest $request The verify email request.
|
* @param \App\Http\Requests\UserVerifyEmailRequest $request The verify email request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function verifyEmail(UserVerifyEmailRequest $request)
|
public function verifyEmail(UserVerifyEmailRequest $request)
|
||||||
@@ -278,21 +281,21 @@ class UserController extends ApiController
|
|||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
return $this->respondWithErrors([
|
return $this->respondWithErrors([
|
||||||
'code' => 'The code was not found or has expired'
|
'code' => 'The code was not found or has expired.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resend a new verify email
|
* Resend a new verify email
|
||||||
*
|
*
|
||||||
* @param UserResendVerifyEmailRequest $request The resend verify email request.
|
* @param \App\Http\Requests\UserResendVerifyEmailRequest $request The resend verify email request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function resendVerifyEmail(UserResendVerifyEmailRequest $request)
|
public function resendVerifyEmail(UserResendVerifyEmailRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
UserCode::clearExpired();
|
UserCode::clearExpired();
|
||||||
|
|
||||||
$user = User::where('username', $request->input('username'))->first();
|
$user = User::where('email', $request->input('email'))->first();
|
||||||
if ($user !== null) {
|
if ($user !== null) {
|
||||||
$code = $user->codes()->where('action', 'verify-email')->first();
|
$code = $user->codes()->where('action', 'verify-email')->first();
|
||||||
$code->regenerate();
|
$code->regenerate();
|
||||||
@@ -312,12 +315,12 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Resend verification email
|
* Resend verification email
|
||||||
*
|
*
|
||||||
* @param UserResendVerifyEmailRequest $request The resend user request.
|
* @param \App\Http\Requests\UserResendVerifyEmailRequest $request The resend user request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
||||||
{
|
{
|
||||||
$user = User::where('username', $request->input('username'))->first();
|
$user = User::where('email', $request->input('email'))->first();
|
||||||
if ($user !== null) {
|
if ($user !== null) {
|
||||||
$user->codes()->where('action', 'verify-email')->delete();
|
$user->codes()->where('action', 'verify-email')->delete();
|
||||||
|
|
||||||
@@ -334,4 +337,33 @@ class UserController extends ApiController
|
|||||||
|
|
||||||
return $this->respondNotFound();
|
return $this->respondNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a JSON event list of a user.
|
||||||
|
*
|
||||||
|
* @param Request $request The http request.
|
||||||
|
* @param User $user The specified user.
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function eventList(Request $request, User $user): JsonResponse
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
$request->user() !== null && (
|
||||||
|
$request->user() === $user || $request->user()->hasPermission('admin/events') === true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$collection = $user->events;
|
||||||
|
$total = $collection->count();
|
||||||
|
|
||||||
|
$collection = EventConductor::collection($request, $collection);
|
||||||
|
return $this->respondAsResource(
|
||||||
|
$collection,
|
||||||
|
['isCollection' => true,
|
||||||
|
'appendData' => ['total' => $total]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
use AuthorizesRequests;
|
||||||
use DispatchesJobs;
|
|
||||||
use ValidatesRequests;
|
use ValidatesRequests;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,28 +40,30 @@ class Kernel extends HttpKernel
|
|||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||||
'throttle:api',
|
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
// \App\Http\Middleware\ForceJsonResponse::class,
|
// \App\Http\Middleware\ForceJsonResponse::class,
|
||||||
|
\App\Http\Middleware\UnmangleRequest::class,
|
||||||
'useSanctumGuard',
|
'useSanctumGuard',
|
||||||
\App\Http\Middleware\LogRequest::class,
|
\App\Http\Middleware\LogRequest::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application's route middleware.
|
* The application's middleware aliases.
|
||||||
*
|
*
|
||||||
* These middleware may be assigned to groups or used individually.
|
* Aliases may be used to conveniently assign middleware to routes and groups.
|
||||||
*
|
*
|
||||||
* @var array<string, class-string|string>
|
* @var array<string, class-string|string>
|
||||||
*/
|
*/
|
||||||
protected $routeMiddleware = [
|
protected $middlewareAliases = [
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'unmangle' => \App\Http\Middleware\UnmangleRequest::class,
|
||||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ class Authenticate extends Middleware
|
|||||||
* Get the path the user should be redirected to when they are not authenticated.
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
*
|
*
|
||||||
* @param mixed $request Request.
|
* @param mixed $request Request.
|
||||||
* @return string|null
|
* @return ?string
|
||||||
*/
|
*/
|
||||||
protected function redirectTo(mixed $request)
|
protected function redirectTo(mixed $request): ?string
|
||||||
{
|
{
|
||||||
if ($request->expectsJson() === false) {
|
if ($request->expectsJson() === false) {
|
||||||
return route('login');
|
return route('login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@@ -10,11 +11,9 @@ class ForceJsonResponse
|
|||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
$request->headers->set('Accept', 'application/json');
|
$request->headers->set('Accept', 'application/json');
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|||||||
@@ -2,30 +2,29 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\AnalyticsItemRequest;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Models\Analytics;
|
|
||||||
|
|
||||||
class LogRequest
|
class LogRequest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param Illuminate\Http\Request $request HTTP Request.
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
* @param \Closure $next Closure.
|
||||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
* @return Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
// Make it an after middleware
|
// Make it an after middleware
|
||||||
$response = $next($request);
|
$response = $next($request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Analytics::create([
|
AnalyticsItemRequest::create([
|
||||||
'type' => 'pageview',
|
'type' => 'apirequest',
|
||||||
'attribute' => $request->path(),
|
'path' => $request->path(),
|
||||||
'useragent' => $request->userAgent(),
|
|
||||||
'ip' => $request->ip(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use App\Providers\RouteServiceProvider;
|
use App\Providers\RouteServiceProvider;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -13,11 +14,11 @@ class RedirectIfAuthenticated
|
|||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param Request $request Request.
|
* @param Request $request Request.
|
||||||
* @param Closure(Request): (Response|RedirectResponse) $next Next.
|
* @param \Closure $next Closure.
|
||||||
* @param string|null ...$guards Guards.
|
* @param string|null ...$guards Guards.
|
||||||
* @return Response|RedirectResponse
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next, ...$guards)
|
public function handle(Request $request, Closure $next, string ...$guards): Response
|
||||||
{
|
{
|
||||||
$guards = empty($guards) === true ? [null] : $guards;
|
$guards = empty($guards) === true ? [null] : $guards;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class TrustHosts extends Middleware
|
|||||||
*
|
*
|
||||||
* @return array<int, string|null>
|
* @return array<int, string|null>
|
||||||
*/
|
*/
|
||||||
public function hosts()
|
public function hosts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
$this->allSubdomainsOfApplicationUrl(),
|
$this->allSubdomainsOfApplicationUrl(),
|
||||||
|
|||||||
47
app/Http/Middleware/UnmangleRequest.php
Normal file
47
app/Http/Middleware/UnmangleRequest.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class UnmangleRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param Request $request Request.
|
||||||
|
* @param \Closure $next Next.
|
||||||
|
* @param string|null ...$guards Guards.
|
||||||
|
* @return Response response.
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next, string ...$guards): Response
|
||||||
|
{
|
||||||
|
if (isset($_SERVER['QUERY_STRING']) === true) {
|
||||||
|
$params = $request->all();
|
||||||
|
|
||||||
|
$string = $_SERVER['QUERY_STRING'];
|
||||||
|
$parts = explode('&', $string);
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
$key = $part;
|
||||||
|
$splitPos = strpos($key, '=');
|
||||||
|
if ($splitPos !== false) {
|
||||||
|
$key = urldecode(substr($key, 0, $splitPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
$replace_key = str_replace('.', '_', $key);
|
||||||
|
if (strpos($key, '.') !== false && array_key_exists($replace_key, $params) === true) {
|
||||||
|
$params[$key] = $params[$replace_key];
|
||||||
|
unset($params[$replace_key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->replace($params);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@@ -11,11 +12,11 @@ class UseSanctumGuard
|
|||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param Request $request Request object.
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
* @param \Closure $next Closure object.
|
||||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
Auth::shouldUse('sanctum');
|
Auth::shouldUse('sanctum');
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|||||||
35
app/Http/Requests/AnalyticsRequest.php
Normal file
35
app/Http/Requests/AnalyticsRequest.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class AnalyticsRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to POST requests.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => 'string',
|
||||||
|
'useragent' => 'string',
|
||||||
|
'ip' => 'ipv4|ipv6',
|
||||||
|
'session' => 'number',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Http/Requests/ArticleRequest.php
Normal file
46
app/Http/Requests/ArticleRequest.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class ArticleRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to POST requests.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => 'required|string|min:6|unique:articles',
|
||||||
|
'title' => 'required|string|min:6|max:255',
|
||||||
|
'publish_at' => 'required|date',
|
||||||
|
'user_id' => 'required|uuid|exists:users,id',
|
||||||
|
'content' => 'required|string|min:6',
|
||||||
|
'hero' => 'required|uuid|exists:media,id',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => [
|
||||||
|
'string',
|
||||||
|
'min:6',
|
||||||
|
Rule::unique('articles')->ignoreModel($this->article),
|
||||||
|
],
|
||||||
|
'title' => 'string|min:6|max:255',
|
||||||
|
'publish_at' => 'date',
|
||||||
|
'user_id' => 'uuid|exists:users,id',
|
||||||
|
'content' => 'string|min:6',
|
||||||
|
'hero' => 'uuid|exists:media,id',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,10 +11,10 @@ class AuthLoginRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'username' => 'required|string|min:6|max:255',
|
'email' => 'required|string|min:6|max:255',
|
||||||
'password' => 'required|string|min:6',
|
'password' => 'required|string|min:6',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,18 @@ class BaseRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function authorize()
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
if (method_exists($this, 'postAuthorize') === true && request()->isMethod('post') === true) {
|
if (request()->isMethod('post') === true && method_exists($this, 'postAuthorize') === true) {
|
||||||
return $this->postAuthorize();
|
return $this->postAuthorize();
|
||||||
} elseif (method_exists($this, 'putAuthorize') === true && request()->isMethod('put') === true) {
|
} elseif (
|
||||||
|
(
|
||||||
|
request()->isMethod('put') === true || request()->isMethod('patch') === true
|
||||||
|
) && method_exists($this, 'putAuthorize') === true
|
||||||
|
) {
|
||||||
return $this->putAuthorize();
|
return $this->putAuthorize();
|
||||||
|
} elseif (request()->isMethod('delete') === true && method_exists($this, 'destroyAuthorize') === true) {
|
||||||
|
return $this->deleteAuthorize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -28,7 +34,7 @@ class BaseRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
@@ -38,8 +44,12 @@ class BaseRequest extends FormRequest
|
|||||||
|
|
||||||
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
||||||
$rules = $this->mergeRules($rules, $this->postRules());
|
$rules = $this->mergeRules($rules, $this->postRules());
|
||||||
} elseif (method_exists($this, 'putRules') === true && request()->isMethod('put') === true) {
|
} elseif (
|
||||||
$rules = $this->mergeRules($rules, $this->postRules());
|
method_exists($this, 'putRules') === true && (
|
||||||
|
request()->isMethod('put') === true || request()->isMethod('patch') === true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$rules = $this->mergeRules($rules, $this->putRules());
|
||||||
} elseif (method_exists($this, 'destroyRules') === true && request()->isMethod('delete') === true) {
|
} elseif (method_exists($this, 'destroyRules') === true && request()->isMethod('delete') === true) {
|
||||||
$rules = $this->mergeRules($rules, $this->destroyRules());
|
$rules = $this->mergeRules($rules, $this->destroyRules());
|
||||||
}
|
}
|
||||||
@@ -54,7 +64,7 @@ class BaseRequest extends FormRequest
|
|||||||
* @param array $collection2 The second collection of rules to merge.
|
* @param array $collection2 The second collection of rules to merge.
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function mergeRules(array $collection1, array $collection2)
|
private function mergeRules(array $collection1, array $collection2): array
|
||||||
{
|
{
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
@@ -73,8 +83,8 @@ class BaseRequest extends FormRequest
|
|||||||
|
|
||||||
if (is_array($collection2[$key]) === true) {
|
if (is_array($collection2[$key]) === true) {
|
||||||
$key_ruleset = array_merge($key_ruleset, $collection2[$key]);
|
$key_ruleset = array_merge($key_ruleset, $collection2[$key]);
|
||||||
} elseif (is_string($collection1[$key]) === true) {
|
} elseif (is_string($collection2[$key]) === true) {
|
||||||
$key_ruleset = array_merge($key_ruleset, explode('|', $collection1[$key]));
|
$key_ruleset = array_merge($key_ruleset, explode('|', $collection2[$key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($key_ruleset) > 0) {
|
if (count($key_ruleset) > 0) {
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ class ContactSendRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => 'required|max:255',
|
'name' => 'required|max:255',
|
||||||
'email' => 'required|email|max:255',
|
'email' => 'required|email|max:255',
|
||||||
'content' => 'required|max:2000',
|
'content' => 'required|max:2000',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class EventRequest extends BaseRequest
|
class EventRequest extends BaseRequest
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function postAuthorize()
|
|
||||||
{
|
|
||||||
return $this->user()?->hasPermission('admin/events');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function putAuthorize()
|
|
||||||
{
|
|
||||||
return $this->user()?->hasPermission('admin/events');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the base rules to this request
|
* Apply the base rules to this request
|
||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function baseRules()
|
public function baseRules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'title' => 'min:6',
|
'title' => 'min:6',
|
||||||
@@ -44,16 +23,18 @@ class EventRequest extends BaseRequest
|
|||||||
'end_at' => 'date|after:start_date',
|
'end_at' => 'date|after:start_date',
|
||||||
'publish_at' => 'date|nullable',
|
'publish_at' => 'date|nullable',
|
||||||
'status' => [
|
'status' => [
|
||||||
Rule::in(['draft', 'open', 'closed', 'cancelled']),
|
Rule::in(['draft', 'soon', 'open', 'closed', 'cancelled', 'scheduled', 'full']),
|
||||||
],
|
],
|
||||||
'registration_type' => [
|
'registration_type' => [
|
||||||
Rule::in(['none', 'email', 'link']),
|
Rule::in(['none', 'email', 'link', 'message']),
|
||||||
],
|
],
|
||||||
'registration_data' => [
|
'registration_data' => [
|
||||||
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
||||||
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url')
|
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url'),
|
||||||
|
Rule::when(strcasecmp('message', $this->attributes->get('registration_type')) == 0, 'required|message'),
|
||||||
],
|
],
|
||||||
'hero' => 'uuid|exists:media,id',
|
'hero' => 'uuid|exists:media,id',
|
||||||
|
'location_url' => 'sometimes|string|max:255',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +43,7 @@ class EventRequest extends BaseRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
protected function postRules()
|
protected function postRules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'title' => 'required',
|
'title' => 'required',
|
||||||
|
|||||||
33
app/Http/Requests/MediaRequest.php
Normal file
33
app/Http/Requests/MediaRequest.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class MediaRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* POST request rules
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function postRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'job_id' => [
|
||||||
|
Rule::requiredIf(function () {
|
||||||
|
return request()->has('chunk') && request('chunk') != 1;
|
||||||
|
}),
|
||||||
|
'string',
|
||||||
|
],
|
||||||
|
'name' => [
|
||||||
|
Rule::requiredIf(function () {
|
||||||
|
return request()->has('chunk') && request('chunk') == 1;
|
||||||
|
}),
|
||||||
|
'string',
|
||||||
|
],
|
||||||
|
'chunk' => 'required_with:chunk_count|integer|min:1|max:999|lte:chunk_count',
|
||||||
|
'chunk_count' => 'required_with:chunk|integer|min:1',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class MediaStoreRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class MediaUpdateRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class PostStoreRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'slug' => 'string|min:6|unique:posts',
|
|
||||||
'title' => 'string|min:6|max:255',
|
|
||||||
'publish_at' => 'date',
|
|
||||||
'user_id' => 'uuid|exists:users,id',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class PostUpdateRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'slug' => [
|
|
||||||
'string',
|
|
||||||
'min:6',
|
|
||||||
Rule::unique('posts')->ignoreModel($this->post),
|
|
||||||
],
|
|
||||||
'title' => 'string|min:6|max:255',
|
|
||||||
'publish_at' => 'date',
|
|
||||||
'user_id' => 'uuid|exists:users,id',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
app/Http/Requests/ShortlinkRequest.php
Normal file
36
app/Http/Requests/ShortlinkRequest.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class ShortlinkRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Apply the additional POST base rules to this request
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'code' => 'required|string|max:255|min:2|unique:shortlinks',
|
||||||
|
'url' => 'required|string|max:255|min:2',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules(): array
|
||||||
|
{
|
||||||
|
$shortlink = $this->route('shortlink');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'code' => ['required', 'string', 'max:255', 'min:2', Rule::unique('shortlinks')->ignore($shortlink->id)],
|
||||||
|
'url' => 'required|string|max:255|min:2',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,11 @@ class SubscriptionRequest extends BaseRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function postRules()
|
public function postRules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email' => 'required|email',
|
'email' => 'required|email|unique:subscriptions',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,11 +24,23 @@ class SubscriptionRequest extends BaseRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function destroyRules()
|
public function destroyRules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email' => 'required|email',
|
'email' => 'required|email',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the custom error messages.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email.unique' => 'This email address has already subscribed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ class UserForgotPasswordRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'username' => 'required|exists:users,username',
|
'email' => 'required|exists:users,email',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use App\Rules\Recaptcha;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class UserForgotUsernameRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'email' => 'required|email|max:255',
|
|
||||||
'captcha_token' => [new Recaptcha()],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Rules\Uniqueish;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
class UserRegisterRequest extends FormRequest
|
class UserRegisterRequest extends FormRequest
|
||||||
@@ -11,13 +12,11 @@ class UserRegisterRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'first_name' => 'required|string|max:255',
|
'display_name' => ['required','string','max:255', new Uniqueish('users')],
|
||||||
'last_name' => 'required|string|max:255',
|
'email' => 'required|string|email|max:255|unique:users',
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'username' => 'required|string|min:4|max:255|unique:users',
|
|
||||||
'password' => 'required|string|min:8',
|
'password' => 'required|string|min:8',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
111
app/Http/Requests/UserRequest.php
Normal file
111
app/Http/Requests/UserRequest.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Rules\RequiredIfAny;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\Rules\RequiredIf;
|
||||||
|
use App\Rules\Uniqueish;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class UserRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Apply the additional POST base rules to this request
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules(): array
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
$isAdminUser = $user->hasPermission('admin/users');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'first_name' => (
|
||||||
|
$isAdminUser === true ? 'required_with:last_name,display_name,phone' : 'required'
|
||||||
|
) . '|string|max:255|min:2',
|
||||||
|
'last_name' => (
|
||||||
|
$isAdminUser === true ? 'required_with:first_name,display_name,phone' : 'required'
|
||||||
|
) . '|string|max:255|min:2',
|
||||||
|
'display_name' => [
|
||||||
|
$isAdminUser === true ? 'required_with:first_name,last_name,phone' : 'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
new Uniqueish('users')
|
||||||
|
],
|
||||||
|
'email' => 'required|string|email|max:255|unique:users',
|
||||||
|
'phone' => ['string', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
||||||
|
'email_verified_at' => 'date'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules(): array
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
$ruleUser = $this->route('user');
|
||||||
|
$isAdminUser = $user->hasPermission('admin/users');
|
||||||
|
|
||||||
|
$requiredIfFieldsPresent = function (array $fields) use ($ruleUser): RequiredIf {
|
||||||
|
return new RequiredIf(function () use ($fields, $ruleUser) {
|
||||||
|
$input = $this->all();
|
||||||
|
$values = Arr::only($input, $fields);
|
||||||
|
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
if ($value !== null && $value !== '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = array_diff($fields, array_keys($values));
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
if ($ruleUser->$field !== '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
'first_name' => [
|
||||||
|
'sometimes',
|
||||||
|
$isAdminUser === true ? $requiredIfFieldsPresent(['last_name', 'display_name', 'phone']) : 'required',
|
||||||
|
'string',
|
||||||
|
'between:2,255',
|
||||||
|
],
|
||||||
|
'last_name' => [
|
||||||
|
'sometimes',
|
||||||
|
$isAdminUser === true ? $requiredIfFieldsPresent(['first_name', 'last_name', 'phone']) : 'required',
|
||||||
|
'string',
|
||||||
|
'between:2,255',
|
||||||
|
],
|
||||||
|
'display_name' => [
|
||||||
|
'sometimes',
|
||||||
|
$isAdminUser === true ? $requiredIfFieldsPresent(['first_name', 'display_name', 'phone']) : 'required',
|
||||||
|
'string',
|
||||||
|
'between:2,255',
|
||||||
|
(new Uniqueish('users', 'display_name'))->ignore($ruleUser->id)
|
||||||
|
],
|
||||||
|
'email' => [
|
||||||
|
'string',
|
||||||
|
'email',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('users')->ignore($ruleUser->id)->when(
|
||||||
|
$this->email !== $ruleUser->email,
|
||||||
|
function ($query) {
|
||||||
|
return $query->where('email', $this->email);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'phone' => ['nullable', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
||||||
|
'password' => "nullable|string|min:8"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,11 +12,11 @@ class UserResendVerifyEmailRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'username' => 'required|exists:users,username',
|
'email' => 'required|exists:users,email',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ class UserResetPasswordRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'code' => 'required|digits:6',
|
'code' => 'required|digits:6',
|
||||||
'password' => 'required|string|min:8',
|
'password' => 'required|string|min:8',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class UserStoreRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'username' => 'required|string|max:255|min:4|unique:users',
|
|
||||||
'first_name' => 'required|string|max:255|min:2',
|
|
||||||
'last_name' => 'required|string|max:255|min:2',
|
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'phone' => ['string', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
|
||||||
'email_verified_at' => 'date'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class UserUpdateRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'username' => 'string|max:255|min:6|unique:users',
|
|
||||||
'first_name' => 'string|max:255|min:2',
|
|
||||||
'last_name' => 'string|max:255|min:2',
|
|
||||||
'email' => 'string|email|max:255',
|
|
||||||
'phone' => ['nullable','regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
|
||||||
'password' => 'string|min:8'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,11 +12,11 @@ class UserVerifyEmailRequest extends FormRequest
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'code' => 'required|digits:6',
|
'code' => 'required|digits:6',
|
||||||
'captcha_token' => [new Recaptcha()],
|
// 'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
393
app/Jobs/MediaWorkerJob.php
Normal file
393
app/Jobs/MediaWorkerJob.php
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Media;
|
||||||
|
use App\Models\MediaJob;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use FFMpeg;
|
||||||
|
use FFMpeg\Coordinate\Dimension;
|
||||||
|
use FFMpeg\FFProbe;
|
||||||
|
use FFMpeg\Format\VideoInterface;
|
||||||
|
use Intervention\Image\Facades\Image;
|
||||||
|
|
||||||
|
/** @property on $format */
|
||||||
|
class MediaWorkerJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithQueue;
|
||||||
|
use Queueable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MediaJob item
|
||||||
|
*
|
||||||
|
* @var MediaJob
|
||||||
|
*/
|
||||||
|
protected $mediaJob;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param MediaJob $mediaJob The mediaJob model.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(MediaJob $mediaJob)
|
||||||
|
{
|
||||||
|
$this->mediaJob = $mediaJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$media = $this->mediaJob->media()->first();
|
||||||
|
$newMedia = false;
|
||||||
|
$data = json_decode($this->mediaJob->data, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// FILE
|
||||||
|
if (array_key_exists('file', $data) === true) {
|
||||||
|
if (file_exists($data['file']) === false) {
|
||||||
|
$this->throwMediaJobFailure('temporary upload file no longer exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert HEIC files to JPG
|
||||||
|
$fileExtension = File::extension($data['file']);
|
||||||
|
if ($fileExtension === 'heic') {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'converting image');
|
||||||
|
|
||||||
|
// Get the path without the file name
|
||||||
|
$uploadedFileDirectory = dirname($data['file']);
|
||||||
|
|
||||||
|
// Convert the HEIC file to JPG
|
||||||
|
$jpgFileName = pathinfo($data['file'], PATHINFO_FILENAME) . '.jpg';
|
||||||
|
$jpgFilePath = $uploadedFileDirectory . '/' . $jpgFileName;
|
||||||
|
if (file_exists($jpgFilePath) === true) {
|
||||||
|
$this->throwMediaJobFailure('file already exists on server');
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::make($data['file'])->save($jpgFilePath);
|
||||||
|
|
||||||
|
// Update the uploaded file path and file name
|
||||||
|
unlink($data['file']);
|
||||||
|
$data['file'] = $jpgFileName;
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
// get security
|
||||||
|
$security = [];
|
||||||
|
if ($media === null) {
|
||||||
|
if (array_key_exists('security', $data) === true) {
|
||||||
|
$security = $data['security'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$security['type'] = $media->security_type;
|
||||||
|
$security['data'] = $media->security_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get storage
|
||||||
|
$storage = '';
|
||||||
|
if ($media === null) {
|
||||||
|
if (array_key_exists('storage', $data) === true) {
|
||||||
|
$storage = $data['storage'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$storage = $media->storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($storage === '') {
|
||||||
|
if (count($security) === 0 || $security['type'] === '') {
|
||||||
|
if (strpos($data['mime_type'], 'image/') === 0) {
|
||||||
|
$storage = 'local';
|
||||||
|
} else {
|
||||||
|
$storage = 'cdn';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$storage = 'private';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file already exists
|
||||||
|
$exists = Storage::disk($storage)->exists($data['name']);
|
||||||
|
if ($exists === true) {
|
||||||
|
if (array_key_exists('noreplace', $data) === true && isTrue($data['noreplace']) === true) {
|
||||||
|
$this->throwMediaJobFailure('file already exists on server');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exists === true) {
|
||||||
|
$pathInfo = pathinfo($data['name']);
|
||||||
|
$basename = $pathInfo['filename'];
|
||||||
|
$extension = $pathInfo['extension'];
|
||||||
|
$index = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$index++;
|
||||||
|
$data['name'] = $basename . '-' . $index . '.' . $extension;
|
||||||
|
} while (Storage::disk($storage)->exists($data['name']) === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($media === null) {
|
||||||
|
$newMedia = true;
|
||||||
|
$media = new Media([
|
||||||
|
'user_id' => $this->mediaJob->user_id,
|
||||||
|
'title' => $data['title'],
|
||||||
|
'name' => $data['name'],
|
||||||
|
'mime_type' => $data['mime_type'],
|
||||||
|
'size' => $data['size'],
|
||||||
|
'security_type' => $data['security']['type'],
|
||||||
|
'security_data' => $data['security']['data'],
|
||||||
|
'storage' => $storage,
|
||||||
|
]);
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
$media->setStagingFile($data['file']);
|
||||||
|
} else {
|
||||||
|
if ($media === null) {
|
||||||
|
$this->throwMediaJobFailure('The media item no longer exists');
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
if (array_key_exists('transform', $data) === true) {
|
||||||
|
$media->createStagingFile();
|
||||||
|
|
||||||
|
// Modifications
|
||||||
|
if (strpos($media->mime_type, 'image/') === 0) {
|
||||||
|
$modified = false;
|
||||||
|
$image = Image::make($media->getStagingFilePath());
|
||||||
|
|
||||||
|
// ROTATE
|
||||||
|
if (array_key_exists("rotate", $data['transform']) === true) {
|
||||||
|
$rotate = intval($data['transform']['rotate']);
|
||||||
|
if ($rotate !== 0) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'rotating image');
|
||||||
|
$image = $image->rotate($rotate);
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLIP-H/V
|
||||||
|
if (array_key_exists('flip', $data['transform']) === true) {
|
||||||
|
if (stripos($data['transform']['flip'], 'h') !== false) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'flipping image');
|
||||||
|
$image = $image->flip('h');
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripos($data['transform']['flip'], 'v') !== false) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'flipping image');
|
||||||
|
$image = $image->flip('v');
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CROP
|
||||||
|
if (array_key_exists("crop", $data['transform']) === true) {
|
||||||
|
$cropData = $data['transform']['crop'];
|
||||||
|
$width = intval(arrayDefaultValue("width", $cropData, $image->getWidth()));
|
||||||
|
$height = intval(arrayDefaultValue("height", $cropData, $image->getHeight()));
|
||||||
|
$x = intval(arrayDefaultValue("x", $cropData, 0));
|
||||||
|
$y = intval(arrayDefaultValue("y", $cropData, 0));
|
||||||
|
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'cropping image');
|
||||||
|
$image = $image->crop($width, $height, $x, $y);
|
||||||
|
$modified = true;
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
if ($modified === true) {
|
||||||
|
$image->save();
|
||||||
|
}
|
||||||
|
} elseif (strpos($data['mime_type'], 'video/') === 0) {
|
||||||
|
$stagingFilePath = $media->getStagingFilePath();
|
||||||
|
$ffmpeg = FFMpeg\FFMpeg::create();
|
||||||
|
$video = $ffmpeg->open($stagingFilePath);
|
||||||
|
$format = $this->detectVideoFormat($stagingFilePath);
|
||||||
|
$modified = false;
|
||||||
|
|
||||||
|
if ($format === null) {
|
||||||
|
$this->mediaJob->setStatusFailed('Unsupported video format');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var FFMpeg\Media\Video::filters */
|
||||||
|
$filters = $video->filters();
|
||||||
|
|
||||||
|
// ROTATE
|
||||||
|
if (array_key_exists("rotate", $data['transform']) === true) {
|
||||||
|
$rotate = intval($data['transform']['rotate']);
|
||||||
|
$rotate = (($rotate % 360 + 360) % 360); // remove excess rotations
|
||||||
|
$rotate = intval(round($rotate / 90) * 90); // round to nearest 90%
|
||||||
|
|
||||||
|
if ($rotate > 0) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'rotating video');
|
||||||
|
|
||||||
|
if ($rotate === 90) {
|
||||||
|
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_270);
|
||||||
|
$modified = true;
|
||||||
|
} elseif ($rotate === 180) {
|
||||||
|
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_180);
|
||||||
|
$modified = true;
|
||||||
|
} elseif ($rotate === 270) {
|
||||||
|
$filters->rotate(FFMpeg\Filters\Video\RotateFilter::ROTATE_90);
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLIP-H/V
|
||||||
|
if (array_key_exists('flip', $data['transform']) === true) {
|
||||||
|
if (stripos($data['transform']['flip'], 'h') !== false) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'flipping video');
|
||||||
|
$filters->hflip()->synchronize();
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripos($data['transform']['flip'], 'v') !== false) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'flipping video');
|
||||||
|
$filters->vflip()->synchronize();
|
||||||
|
$modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CROP
|
||||||
|
if (array_key_exists("crop", $data['transform']) === true) {
|
||||||
|
$cropData = $data['transform']['crop'];
|
||||||
|
$videoStream = $video->getStreams()->videos()->first();
|
||||||
|
|
||||||
|
$width = intval(arrayDefaultValue("width", $cropData, $videoStream->get('width')));
|
||||||
|
$height = intval(arrayDefaultValue("height", $cropData, $videoStream->get('height')));
|
||||||
|
$x = intval(arrayDefaultValue("x", $cropData, 0));
|
||||||
|
$y = intval(arrayDefaultValue("y", $cropData, 0));
|
||||||
|
|
||||||
|
$cropDimension = new Dimension($width, $height);
|
||||||
|
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'cropping video');
|
||||||
|
$filters->crop($cropDimension, $x, $y)->synchronize();
|
||||||
|
$modified = true;
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
$tempFilePath = generateTempFilePath(pathinfo($stagingFilePath, PATHINFO_EXTENSION));
|
||||||
|
if (method_exists($format, 'on') === true) {
|
||||||
|
$mediaJob = $this->mediaJob;
|
||||||
|
$format->on('progress', function ($video, $format, $percentage) use ($mediaJob) {
|
||||||
|
$mediaJob->setStatusProcessing($percentage, 100, 'transcoded');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modified === true) {
|
||||||
|
$video->save($format, $tempFilePath);
|
||||||
|
$media->changeStagingFile($tempFilePath);
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
// Update attributes
|
||||||
|
if (array_key_exists('title', $data) === true) {
|
||||||
|
$media->title = $data['title'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relocate file (if requested)
|
||||||
|
if (array_key_exists('security', $data) === true && array_key_exists('type', $data['security']) === true) {
|
||||||
|
$media->security_type = $data['security']['type'];
|
||||||
|
$media->security_data = $data['security']['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('storage', $data) === true) {
|
||||||
|
if ($media->storage !== $data['storage']) {
|
||||||
|
$media->createStagingFile();
|
||||||
|
Storage::disk($media->storage)->delete($media->name);
|
||||||
|
$media->storage = $data['storage'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish media object
|
||||||
|
if ($media->hasStagingFile() === true) {
|
||||||
|
$this->mediaJob->setStatusProcessing(0, 0, 'transferring to cdn');
|
||||||
|
$media->deleteFile();
|
||||||
|
$media->saveStagingFile(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$media->save();
|
||||||
|
$this->mediaJob->media_id = $media->id;
|
||||||
|
$this->mediaJob->setStatusComplete();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($this->mediaJob->status !== 'failed') {
|
||||||
|
$this->mediaJob->setStatusFailed('Unexpected server error occurred');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($media !== null) {
|
||||||
|
$media->deleteStagingFile();
|
||||||
|
if ($newMedia === true) {
|
||||||
|
$media->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::error($e->getMessage() . "\n" . $e->getFile() . " - " . $e->getLine() . "\n" . $e->getTraceAsString());
|
||||||
|
$this->fail($e);
|
||||||
|
}//end try
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the format of a video using FFProbe
|
||||||
|
*
|
||||||
|
* @param string $videoPath The video file path.
|
||||||
|
* @return VideoInterface | null
|
||||||
|
*/
|
||||||
|
public function detectVideoFormat(string $videoPath): VideoInterface | null
|
||||||
|
{
|
||||||
|
$ffprobe = FFProbe::create();
|
||||||
|
|
||||||
|
$videoStream = $ffprobe
|
||||||
|
->streams($videoPath) // Provide the path to the video file
|
||||||
|
->videos() // Filter video streams
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$codecName = $videoStream->get('codec_name');
|
||||||
|
|
||||||
|
$codecToFormatClass = [
|
||||||
|
'h264' => 'FFMpeg\Format\Video\X264',
|
||||||
|
'wmv2' => 'FFMpeg\Format\Video\WMV',
|
||||||
|
'vp9' => 'FFMpeg\Format\Video\WebM',
|
||||||
|
'theora' => 'FFMpeg\Format\Video\Ogg',
|
||||||
|
'mpeg4' => 'FFMpeg\Format\Video\Mpeg4',
|
||||||
|
// Add more mappings as needed
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($codecToFormatClass[$codecName]) === false) {
|
||||||
|
Log::info("Unsupported codec: $codecName");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$formatClassName = $codecToFormatClass[$codecName];
|
||||||
|
|
||||||
|
if (class_exists($formatClassName) === false) {
|
||||||
|
Log::info("Format class does not exist: $formatClassName");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new $formatClassName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set failure status of MediaJob and throw exception.
|
||||||
|
*
|
||||||
|
* @param string $error The failure message.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function throwMediaJobFailure(string $error): void
|
||||||
|
{
|
||||||
|
$this->mediaJob->setStatusFailed($error);
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,7 @@ class SendEmailJob implements ShouldQueue
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): void
|
||||||
{
|
{
|
||||||
Mail::to($this->to)->send($this->mailable);
|
Mail::to($this->to)->send($this->mailable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class ChangeEmailVerify extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '👋🏻 Lets change your email!',
|
subject: '👋🏻 Lets change your email!',
|
||||||
@@ -67,9 +67,9 @@ class ChangeEmailVerify extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.change_email_verify',
|
view: 'emails.user.change_email_verify',
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class ChangedEmail extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '👍 Your email has been changed!',
|
subject: '👍 Your email has been changed!',
|
||||||
@@ -67,9 +67,9 @@ class ChangedEmail extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.changed_email',
|
view: 'emails.user.changed_email',
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ class ChangedPassword extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '👍 Your password has been changed!',
|
subject: '👍 Your password has been changed!',
|
||||||
@@ -49,9 +49,9 @@ class ChangedPassword extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.changed_password',
|
view: 'emails.user.changed_password',
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ class Contact extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: config('contact.contact_subject'),
|
subject: config('contact.contact_subject'),
|
||||||
@@ -66,9 +66,9 @@ class Contact extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.contact',
|
view: 'emails.user.contact',
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ class EmailVerify extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '👋🏻 Welcome to STEMMechanics!',
|
subject: '👋🏻 Welcome to STEMMechanics!',
|
||||||
@@ -58,9 +58,9 @@ class EmailVerify extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.email_verify',
|
view: 'emails.user.email_verify',
|
||||||
|
|||||||
@@ -9,52 +9,51 @@ use Illuminate\Mail\Mailables\Content;
|
|||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ForgotUsername extends Mailable
|
class ExceptionMail extends Mailable
|
||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of usernames
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
public $usernames;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
|
||||||
* @param array $usernames The usernames.
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $usernames)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->usernames = $usernames;
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '🤦 Forgot your username?',
|
subject: 'Exception Mail',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.forgot_username',
|
view: 'view.name',
|
||||||
text: 'emails.user.forgot_username_plain',
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attachments for the message.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||||
|
*/
|
||||||
|
public function attachments(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -46,9 +46,9 @@ class ForgotPassword extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '🤦 Forgot your password?',
|
subject: '🤦 Forgot your password?',
|
||||||
@@ -58,9 +58,9 @@ class ForgotPassword extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.forgot_password',
|
view: 'emails.user.forgot_password',
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ class SubscriptionConfirm extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: '🗞️ You\'re on the mailing list!',
|
subject: '🗞️ You\'re on the mailing list!',
|
||||||
@@ -49,9 +49,9 @@ class SubscriptionConfirm extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.subscription_confirm',
|
view: 'emails.user.subscription_confirm',
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ class SubscriptionUnsubscribed extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message envelope.
|
* Get the message envelope.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
* @return Illuminate\Mail\Mailables\Envelope
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: 'You have been unsubscribed',
|
subject: 'You have been unsubscribed',
|
||||||
@@ -49,9 +49,9 @@ class SubscriptionUnsubscribed extends Mailable
|
|||||||
/**
|
/**
|
||||||
* Get the message content definition.
|
* Get the message content definition.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
* @return Illuminate\Mail\Mailables\Content
|
||||||
*/
|
*/
|
||||||
public function content()
|
public function content(): Content
|
||||||
{
|
{
|
||||||
return new Content(
|
return new Content(
|
||||||
view: 'emails.user.subscription_unsubscribed',
|
view: 'emails.user.subscription_unsubscribed',
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class Analytics extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that aren't mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $guarded = [];
|
|
||||||
}
|
|
||||||
73
app/Models/AnalyticsItemRequest.php
Normal file
73
app/Models/AnalyticsItemRequest.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class AnalyticsItemRequest extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table name
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $table = 'analytics_requests';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'type',
|
||||||
|
'path'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model Boot.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected static function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
static::creating(function (AnalyticsItemRequest $analytics) {
|
||||||
|
if (isset($analytics->session_id) !== true) {
|
||||||
|
$request = request();
|
||||||
|
if ($request !== null) {
|
||||||
|
$session = AnalyticsSession::where('ip', $request->ip())
|
||||||
|
->where('useragent', $request->userAgent())
|
||||||
|
->where('ended_at', '>=', now()->subMinutes(30))
|
||||||
|
->first();
|
||||||
|
if ($session === null) {
|
||||||
|
$session = AnalyticsSession::create([
|
||||||
|
'ip' => $request->ip(),
|
||||||
|
'useragent' => $request->userAgent(),
|
||||||
|
'ended_at' => now()
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$session->update(['ended_at' => now()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$analytics->session_id = $session->id;
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Analytics Session model.
|
||||||
|
*
|
||||||
|
* @return BelongsTo
|
||||||
|
*/
|
||||||
|
public function session(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(AnalyticsSession::class, 'id', 'session_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Models/AnalyticsSession.php
Normal file
44
app/Models/AnalyticsSession.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
class AnalyticsSession extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'ip',
|
||||||
|
'useragent',
|
||||||
|
'ended_at'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the "useragent" attribute.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseragentAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['useragent'] = $value !== null ? $value : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the related requests for this session.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
|
public function requests(): HasMany {
|
||||||
|
return $this->hasMany(AnalyticsItemRequest::class, 'session_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasAttachments;
|
||||||
|
use App\Traits\HasGallery;
|
||||||
use App\Traits\Uuids;
|
use App\Traits\Uuids;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class Post extends Model
|
class Article extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Uuids;
|
use Uuids;
|
||||||
|
use HasGallery;
|
||||||
|
use HasAttachments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
@@ -27,11 +32,11 @@ class Post extends Model
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the file user
|
* Get the article user
|
||||||
*
|
*
|
||||||
* @return BelongsTo
|
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
*/
|
*/
|
||||||
public function user()
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
86
app/Models/Attachment.php
Normal file
86
app/Models/Attachment.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class Attachment extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'media_id',
|
||||||
|
'private',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default attributes.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $attributes = [
|
||||||
|
'private' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the media for this attachment.
|
||||||
|
*
|
||||||
|
* @return BelongsTo
|
||||||
|
*/
|
||||||
|
public function media(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Media::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get associated Media object.
|
||||||
|
*
|
||||||
|
* @return null|Media
|
||||||
|
*/
|
||||||
|
public function getMediaAttribute(): ?Media
|
||||||
|
{
|
||||||
|
$mediaId = '0';
|
||||||
|
$media = null;
|
||||||
|
|
||||||
|
if (Cache::has("attachment:{$this->id}:media") === true) {
|
||||||
|
$mediaId = Cache::get("attachment:{$this->id}:media");
|
||||||
|
} else {
|
||||||
|
$media = $this->media()->first();
|
||||||
|
if ($media === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mediaId = $media->id;
|
||||||
|
Cache::put("attachment:{$this->id}:media", $mediaId, now()->addDays(28));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Cache::remember("media:{$mediaId}", now()->addDays(28), function () use ($media) {
|
||||||
|
if ($media !== null) {
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->media()->first();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the media for this item.
|
||||||
|
*
|
||||||
|
* @param Media $media The media model.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setMediaAttribute(Media $media): void
|
||||||
|
{
|
||||||
|
$this->media()->associate($media)->save();
|
||||||
|
Cache::put("attachment:{$this->id}:media", $media->id, now()->addDays(28));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasAttachments;
|
||||||
use App\Traits\Uuids;
|
use App\Traits\Uuids;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
|
|
||||||
class Event extends Model
|
class Event extends Model
|
||||||
{
|
{
|
||||||
|
use HasAttachments;
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Uuids;
|
use Uuids;
|
||||||
|
|
||||||
@@ -19,6 +23,7 @@ class Event extends Model
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'title',
|
'title',
|
||||||
'location',
|
'location',
|
||||||
|
'location_url',
|
||||||
'address',
|
'address',
|
||||||
'start_at',
|
'start_at',
|
||||||
'end_at',
|
'end_at',
|
||||||
@@ -27,6 +32,20 @@ class Event extends Model
|
|||||||
'registration_type',
|
'registration_type',
|
||||||
'registration_data',
|
'registration_data',
|
||||||
'hero',
|
'hero',
|
||||||
'content'
|
'content',
|
||||||
|
'price',
|
||||||
|
'ages',
|
||||||
|
'open_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the associated users.
|
||||||
|
*
|
||||||
|
* @return BelongsToMany
|
||||||
|
*/
|
||||||
|
public function users(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(User::class, 'event_user', 'event_id', 'user_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
app/Models/EventUsers.php
Normal file
45
app/Models/EventUsers.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\Uuids;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class EventUser extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
use Uuids;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'event_id',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the event for this attachment.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function event(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Event::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user for this attachment.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
app/Models/Gallery.php
Normal file
107
app/Models/Gallery.php
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\Uuids;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class Gallery extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
use Uuids;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'media_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot the model.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected static function boot(): void
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
$clearCache = function ($gallery) {
|
||||||
|
Cache::forget("gallery:{$gallery->id}:media");
|
||||||
|
};
|
||||||
|
|
||||||
|
static::saving($clearCache);
|
||||||
|
static::deleting($clearCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get gallery addendum model.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Eloquent\Relations\MorphTo Addenum model.
|
||||||
|
*/
|
||||||
|
public function addendum(): MorphTo
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the media for this item.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Eloquent\Relations\BelongsTo The media model.
|
||||||
|
*/
|
||||||
|
public function media(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Media::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the media for this item.
|
||||||
|
*
|
||||||
|
* @return null|Media The media model.
|
||||||
|
*/
|
||||||
|
public function getMediaAttribute(): ?Media
|
||||||
|
{
|
||||||
|
$mediaId = '0';
|
||||||
|
$media = null;
|
||||||
|
|
||||||
|
if (Cache::has("gallery:{$this->id}:media") === true) {
|
||||||
|
$mediaId = Cache::get("gallery:{$this->id}:media");
|
||||||
|
} else {
|
||||||
|
$media = $this->media()->first();
|
||||||
|
if ($media === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mediaId = $media->id;
|
||||||
|
Cache::put("gallery:{$this->id}:media", $mediaId, now()->addDays(28));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Cache::remember("media:{$mediaId}", now()->addDays(28), function () use ($media) {
|
||||||
|
if ($media !== null) {
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->media()->first();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the media for this item.
|
||||||
|
*
|
||||||
|
* @param Media $media The media model.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setMediaAttribute(Media $media): void
|
||||||
|
{
|
||||||
|
$this->media()->associate($media)->save();
|
||||||
|
Cache::put("gallery:{$this->id}:media", $media->id, now()->addDays(28));
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user