Compare commits
3015 Commits
v5.0.1
...
6db5c924c7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db5c924c7 | ||
|
|
51aecacee6 | ||
|
|
b3b8febd12 | ||
|
|
7c770d9a9d | ||
|
|
30b89fe56f | ||
|
|
06870f2577 | ||
|
|
e84170b8ea | ||
|
|
e8d26b2cb2 | ||
|
|
a8f87ea572 | ||
|
|
1fb78a457f | ||
|
|
82cb7a8c02 | ||
|
|
cae61ab701 | ||
|
|
975c1955f2 | ||
|
|
fc2a8cb9dd | ||
|
|
e2d9aaaa0f | ||
|
|
aca70dd6c7 | ||
|
|
5a1b3a7017 | ||
|
|
6733299290 | ||
|
|
e11e021750 | ||
|
|
0ad5bd2335 | ||
|
|
255764a4ac | ||
|
|
7330d5b2a7 | ||
|
|
5d3e507672 | ||
|
|
15ee2c9bcb | ||
|
|
e1163b7ab1 | ||
|
|
96bea78424 | ||
|
|
8ba67a63ba | ||
|
|
8120db84f4 | ||
|
|
4e6a6afaab | ||
|
|
417a6bf226 | ||
|
|
5e5d91bd93 | ||
|
|
5c536aafc2 | ||
|
|
15efe75aa8 | ||
|
|
a10575bfc3 | ||
|
|
57e26d6156 | ||
|
|
1682a123b4 | ||
|
|
13064cdb26 | ||
|
|
f74eda274a | ||
|
|
7dee55ce30 | ||
|
|
8113c82da8 | ||
|
|
2d684d23d7 | ||
|
|
33da5809e3 | ||
|
|
68a6e6862e | ||
|
|
07fb15d5be | ||
|
|
0f791253ba | ||
|
|
828d070194 | ||
|
|
0d5bb3f3b3 | ||
|
|
32de1ca511 | ||
|
|
f5f00c51c1 | ||
|
|
1305125390 | ||
|
|
c539f41ebf | ||
|
|
075e754830 | ||
|
|
87fd9dd000 | ||
|
|
e34321f727 | ||
|
|
a48dff4a85 | ||
|
|
576d3d0b56 | ||
|
|
640c2cee00 | ||
|
|
1958787185 | ||
|
|
073e1ac13a | ||
|
|
c0eacd0d6b | ||
|
|
7938eaf123 | ||
|
|
b4f8896713 | ||
|
|
c418ddf240 | ||
|
|
6c6b36f3da | ||
|
|
c0c71251ab | ||
|
|
2685c18798 | ||
|
|
7f914271ed | ||
|
|
7b37cc3c82 | ||
|
|
ec2afd5f03 | ||
|
|
e62268af2e | ||
|
|
01ad99b925 | ||
|
|
a33e9d25cc | ||
|
|
a0e45cbea0 | ||
|
|
171314947c | ||
|
|
6b883b3f94 | ||
|
|
c99348650f | ||
|
|
011375a081 | ||
|
|
38f43c9988 | ||
|
|
f459d0503a | ||
|
|
c2912a291e | ||
|
|
53a88e0c9f | ||
|
|
9cf670bcad | ||
|
|
156e7bd3d4 | ||
|
|
009829c8f9 | ||
|
|
d7c0b0aaaf | ||
|
|
06071fb7f9 | ||
|
|
a51f87d743 | ||
|
|
12fa6ff4f0 | ||
|
|
23d14c62a5 | ||
|
|
ad993c6180 | ||
|
|
e99ce3ac7b | ||
|
|
270b447fbd | ||
|
|
1c55a74ff1 | ||
|
|
47f42747cb | ||
|
|
86a3f67871 | ||
|
|
29b87f809f | ||
|
|
8bd63fdc61 | ||
|
|
cf88347c3d | ||
|
|
171f9c84a0 | ||
|
|
a6069e572d | ||
|
|
16a13a6c01 | ||
|
|
ac31cd3f41 | ||
|
|
321fe2954e | ||
|
|
e2f174e0b5 | ||
|
|
50c085fe65 | ||
|
|
4e63c9ce9d | ||
|
|
fb6e8bb233 | ||
|
|
44103c1311 | ||
|
|
6ef6e6aac8 | ||
|
|
9499012825 | ||
|
|
6af73873d7 | ||
|
|
0a04035b2f | ||
|
|
1e3c176ddf | ||
|
|
f5bb9a934c | ||
|
|
476cf7c080 | ||
|
|
1279c30fbb | ||
|
|
012b7ba6ed | ||
|
|
e08c033e76 | ||
|
|
dafbae7237 | ||
|
|
31b8080a3d | ||
|
|
28c7617227 | ||
|
|
7f7c53dabe | ||
|
|
950a9bf2fa | ||
|
|
f0d4a416be | ||
|
|
708d79ffaf | ||
|
|
583bd3b511 | ||
|
|
5f8798c224 | ||
|
|
74c259cd79 | ||
|
|
c96787f1b9 | ||
|
|
aaac471956 | ||
|
|
db41bfe638 | ||
|
|
bfc9acb170 | ||
|
|
50540b50aa | ||
|
|
9f9d3460b1 | ||
|
|
e2f02bcd4b | ||
|
|
538bc093e0 | ||
|
|
3d34ab83c6 | ||
|
|
5fb413505c | ||
|
|
efcbdee869 | ||
|
|
2ea6f9e447 | ||
|
|
2acd5799d9 | ||
|
|
78bfc91469 | ||
|
|
c9590247eb | ||
|
|
df4209ed77 | ||
|
|
4daaaa8dbf | ||
|
|
eb10f2a6a0 | ||
|
|
05993ab462 | ||
|
|
f276a892fd | ||
|
|
7a7508c4ca | ||
|
|
d2cf817aac | ||
|
|
b9497cbb56 | ||
|
|
b2aa17410e | ||
|
|
545096a753 | ||
|
|
d652757614 | ||
|
|
bff00832fc | ||
|
|
7a09a48b64 | ||
|
|
c5fbb5b61b | ||
|
|
f6be499e47 | ||
|
|
49ef0d7464 | ||
|
|
528cbde7e5 | ||
|
|
cf7e082dbc | ||
|
|
b786faa6a1 | ||
|
|
ebbf39c360 | ||
|
|
92dc46a81e | ||
|
|
63494bc7ee | ||
|
|
e6ee13784f | ||
|
|
11284f0285 | ||
|
|
0b2ade4b01 | ||
|
|
f687c09adc | ||
|
|
20fdd211be | ||
|
|
21fcd653b8 | ||
|
|
62db107d90 | ||
|
|
f6c1d65c89 | ||
|
|
8f3c5f5768 | ||
|
|
eb8cfa28ed | ||
|
|
b4789dff3e | ||
|
|
ee25c46ee1 | ||
|
|
dc9d4a1938 | ||
|
|
e58ee4e5b1 | ||
|
|
3c5d839e9d | ||
|
|
590901bf6e | ||
|
|
76938503b6 | ||
|
|
9ffd4f39e9 | ||
|
|
a707ae7da9 | ||
|
|
0e717c8f57 | ||
|
|
78853173db | ||
|
|
20005797e5 | ||
|
|
206806d01a | ||
|
|
2def6ad854 | ||
|
|
4339833aa3 | ||
|
|
afbe6c7054 | ||
|
|
2914749253 | ||
|
|
54902051ce | ||
|
|
01ee9650ff | ||
|
|
2f6b9a2fc7 | ||
|
|
20e270c040 | ||
|
|
afb2613f67 | ||
|
|
b917a7bbf6 | ||
|
|
7d01aa449e | ||
|
|
fe16594885 | ||
|
|
29f74131d1 | ||
|
|
b0e861e985 | ||
|
|
ed8df61143 | ||
|
|
6374314d3c | ||
|
|
aedfa91ce6 | ||
|
|
087d5ef394 | ||
|
|
637e285441 | ||
|
|
ab4bc7e678 | ||
|
|
e68fe3a9c3 | ||
|
|
adfd870319 | ||
|
|
edd89bf133 | ||
|
|
d774557522 | ||
|
|
f9c1906fe2 | ||
|
|
d5ad29be34 | ||
|
|
d62f8e966c | ||
|
|
670f3854fa | ||
|
|
ed4ab703c0 | ||
|
|
1d3c1c158f | ||
|
|
431fa05763 | ||
|
|
cd24573599 | ||
|
|
c43af46d38 | ||
|
|
7e69b5193f | ||
|
|
a06b1becc5 | ||
|
|
b2dfde58d5 | ||
|
|
e548c21c94 | ||
|
|
f4a1bf659f | ||
|
|
cf3a86dc4a | ||
|
|
bbd441b0b7 | ||
|
|
ac9b7a60fd | ||
|
|
9abe4a1c42 | ||
|
|
273097d96d | ||
|
|
57aeac2277 | ||
|
|
39ad5a0638 | ||
|
|
b4ef5faa28 | ||
|
|
a4c2989062 | ||
|
|
2ef9963587 | ||
|
|
19b003ae49 | ||
|
|
5d66c904cd | ||
|
|
ca521f3a5e | ||
|
|
9a622dd88f | ||
|
|
ff6c71e587 | ||
|
|
7c3d59915e | ||
|
|
970f6b400f | ||
|
|
abc4905a14 | ||
|
|
be3ecdf7f6 | ||
|
|
5875c1caa7 | ||
|
|
ebfcd59c44 | ||
|
|
73dc4c6e5f | ||
|
|
0c514743b7 | ||
|
|
1e11b0f3a3 | ||
|
|
e7ae1b26d1 | ||
|
|
46fa9abf43 | ||
|
|
d837cd8af5 | ||
|
|
cca6aff735 | ||
|
|
2be11b52c3 | ||
|
|
7b026c5b14 | ||
|
|
9135894053 | ||
|
|
8ebe34b038 | ||
|
|
7ef4376363 | ||
|
|
9a4bfd7009 | ||
|
|
356e350588 | ||
|
|
b976983a36 | ||
|
|
7ce7020b51 | ||
|
|
d22949522f | ||
|
|
8bac8677c5 | ||
|
|
4a4edfa857 | ||
|
|
2648887f7d | ||
|
|
eac7ad90e3 | ||
|
|
75c7b55b20 | ||
|
|
23ae819b70 | ||
|
|
f908637313 | ||
|
|
68aeee7c45 | ||
|
|
e4248ed569 | ||
|
|
7cf325f4f6 | ||
|
|
a80e449e9c | ||
|
|
83b56966f4 | ||
|
|
cc2e34fc0b | ||
|
|
bc617db649 | ||
|
|
4a57abc99d | ||
|
|
db85d1fbc3 | ||
|
|
20f2eeefbf | ||
|
|
02c4da5539 | ||
|
|
8e10a8e042 | ||
|
|
0c3aed5fa9 | ||
|
|
95ec163f2c | ||
|
|
bf2d0e35d3 | ||
|
|
002cf28e12 | ||
|
|
c0a75ba665 | ||
|
|
4b05406d40 | ||
|
|
832f94070f | ||
|
|
cf761f56d4 | ||
|
|
27ba8dfeab | ||
|
|
044bd54201 | ||
|
|
e48ba633be | ||
|
|
08757b42df | ||
|
|
a59f2e7ca6 | ||
|
|
e9a672ebd0 | ||
|
|
72e475bca5 | ||
|
|
c49637444a | ||
|
|
b1cc0ffc13 | ||
|
|
4e49092434 | ||
|
|
7713f18ddf | ||
|
|
c1880c54ce | ||
|
|
6a43bcf458 | ||
|
|
ecec06b616 | ||
|
|
368c9e60ea | ||
|
|
f5b4e52526 | ||
|
|
a939a286ae | ||
|
|
88acb2a665 | ||
|
|
e7c2ad5965 | ||
|
|
071cceb7f8 | ||
|
|
377465e361 | ||
|
|
0f738113af | ||
|
|
534a6147a8 | ||
|
|
87313c8082 | ||
|
|
b315f09640 | ||
|
|
3025f11ea8 | ||
|
|
8fb391717f | ||
|
|
583ccf9811 | ||
|
|
cd6ec49cc8 | ||
|
|
2b9c5b1728 | ||
|
|
0e772974a6 | ||
|
|
62e181cfc3 | ||
|
|
68233951cb | ||
|
|
d8531899b6 | ||
|
|
bc2e7915cc | ||
|
|
b18d47afa3 | ||
|
|
297f91da00 | ||
|
|
2bdc7e1bc3 | ||
|
|
dc0a5c8bb0 | ||
|
|
2a302a187a | ||
|
|
407a3a19b6 | ||
|
|
d462fc7afd | ||
|
|
0b425e3bd9 | ||
|
|
3efc12fabc | ||
|
|
852385a192 | ||
|
|
8077025fe8 | ||
|
|
6f0da0c002 | ||
|
|
5dbea610c1 | ||
|
|
026d716ece | ||
|
|
92496f4369 | ||
|
|
ec00b1162f | ||
|
|
e638aee1ac | ||
|
|
5420f625b4 | ||
|
|
77fa7f4a79 | ||
|
|
2db1fe0890 | ||
|
|
63bb70785a | ||
|
|
8d23d9aba3 | ||
|
|
bebe70f46b | ||
|
|
34f2db5985 | ||
|
|
8e75c09e3f | ||
|
|
b5d4eaa36e | ||
|
|
116d163b9d | ||
|
|
2cb568773c | ||
|
|
916019f015 | ||
|
|
e83d7e9d57 | ||
|
|
151af30259 | ||
|
|
7fed6bb93a | ||
|
|
382a8eb8f3 | ||
|
|
9508ff68db | ||
|
|
9684e6e1a8 | ||
|
|
52745b1946 | ||
|
|
3db2d03a37 | ||
|
|
a85ae69ed1 | ||
|
|
9052d6abb6 | ||
|
|
3c528f0b93 | ||
|
|
3322297eaa | ||
|
|
3c1167d359 | ||
|
|
9e35a520cc | ||
|
|
beb4919d97 | ||
|
|
6895d16a20 | ||
|
|
05b37080c1 | ||
|
|
51894de708 | ||
|
|
442ec291a1 | ||
|
|
6ef106be31 | ||
|
|
70551f9d27 | ||
|
|
fe422ed5aa | ||
|
|
1995a96a98 | ||
|
|
4abcc6e58f | ||
|
|
a6f4921055 | ||
|
|
33f525dbda | ||
|
|
23e83a5e30 | ||
|
|
6abd2cf7fc | ||
|
|
085f137942 | ||
|
|
62d99d33bd | ||
|
|
57375eaab9 | ||
|
|
30b7e71cd8 | ||
|
|
e26bb66405 | ||
|
|
6263bd3a60 | ||
|
|
8bee8d2f3f | ||
|
|
4ffe8fac3a | ||
|
|
09a7457c01 | ||
|
|
15a8f0a4ac | ||
|
|
503134d38c | ||
|
|
e19b8ffed9 | ||
|
|
c38dc69d3b | ||
|
|
ff16fd8b9c | ||
|
|
880a6e43d1 | ||
|
|
188d3b42d8 | ||
|
|
c526e01534 | ||
|
|
dae906d52f | ||
|
|
e620bba0da | ||
|
|
fae22595aa | ||
|
|
a528e5eab2 | ||
|
|
0991925090 | ||
|
|
f9741a82bd | ||
|
|
f0067d86a6 | ||
|
|
d7aa999f25 | ||
|
|
ea87497e6d | ||
|
|
2dc8cabc80 | ||
|
|
4e53dcd8d5 | ||
|
|
dd447e802e | ||
|
|
c7a86aa49c | ||
|
|
c6e7638e8b | ||
|
|
166969bc35 | ||
|
|
61d231801a | ||
|
|
cc51f5bb0f | ||
|
|
dcd99695e7 | ||
|
|
1c78683f4c | ||
|
|
f2124c5ae0 | ||
|
|
868aca9fdb | ||
|
|
d12f7b79d2 | ||
|
|
600bbdfd0d | ||
|
|
04bc68de55 | ||
|
|
82b4f7b611 | ||
|
|
4adba1ab5f | ||
|
|
6d2ac670af | ||
|
|
64a03b6e91 | ||
|
|
c88958ae7e | ||
|
|
4278b9992b | ||
|
|
ebac6d51b0 | ||
|
|
fba4f23f71 | ||
|
|
7231d2f49e | ||
|
|
d871bffdd5 | ||
|
|
5630b4842c | ||
|
|
b19141b361 | ||
|
|
732e279605 | ||
|
|
fa173d492c | ||
|
|
3259494d45 | ||
|
|
0a03eb620a | ||
|
|
701d8c9a57 | ||
|
|
fdca8a2890 | ||
|
|
22e2a4da1e | ||
|
|
409523912b | ||
|
|
d5c68444c3 | ||
|
|
ffa93e0ee7 | ||
|
|
3f4f1a8278 | ||
|
|
8e70949880 | ||
|
|
be8436d237 | ||
|
|
876f13be5e | ||
|
|
dfca6640da | ||
|
|
a2e57bc54c | ||
|
|
dcc2e59e46 | ||
|
|
90e721b172 | ||
|
|
94391875d5 | ||
|
|
43d06c042d | ||
|
|
3e12910fbd | ||
|
|
ba70ebe23c | ||
|
|
b739841495 | ||
|
|
acabc75aa6 | ||
|
|
27041f464f | ||
|
|
9f923ae968 | ||
|
|
9c7d832357 | ||
|
|
e913c10d5b | ||
|
|
c698188901 | ||
|
|
8fd67621ac | ||
|
|
0c60085e09 | ||
|
|
1826316c80 | ||
|
|
07341aeebe | ||
|
|
9f6945dda2 | ||
|
|
b39b568b4c | ||
|
|
e59d5fd339 | ||
|
|
b7bc527d6c | ||
|
|
1ea76d06d1 | ||
|
|
b049be9d83 | ||
|
|
966fc55594 | ||
|
|
ca9ddbd90f | ||
|
|
0d04926d9f | ||
|
|
2b500d41ca | ||
|
|
5c67eeea58 | ||
|
|
09daf3f6cc | ||
|
|
9a06a3311e | ||
|
|
304694fbf9 | ||
|
|
96ba42df96 | ||
|
|
e7bc11d026 | ||
|
|
1272305355 | ||
|
|
30c6da13c2 | ||
|
|
5aacb2b877 | ||
|
|
b5fdf42c37 | ||
|
|
4ba7e034b7 | ||
|
|
c81d677c5c | ||
|
|
6daf675e52 | ||
|
|
3f7a7f3340 | ||
|
|
1ebf3c4077 | ||
|
|
1f1173ae03 | ||
|
|
efa466e1d6 | ||
|
|
cefe349b4e | ||
|
|
a9bc356f37 | ||
|
|
6fc791020c | ||
|
|
713ec1b373 | ||
|
|
e3fa781122 | ||
|
|
e4b6d0ff29 | ||
|
|
cd2a328560 | ||
|
|
d2d88d4b5e | ||
|
|
0067cc4266 | ||
|
|
da3afefa8d | ||
|
|
ab534d07f3 | ||
|
|
49c513ac9b | ||
|
|
6f7a18674e | ||
|
|
0f559ba42d | ||
|
|
2af02fae95 | ||
|
|
006423e32e | ||
|
|
23f29ca55d | ||
|
|
68a7571741 | ||
|
|
10e60e352a | ||
|
|
3b16ae8cc0 | ||
|
|
66c4737021 | ||
|
|
8684e03af1 | ||
|
|
edad9e6b3c | ||
|
|
66b89752d3 | ||
|
|
9a6195edf1 | ||
|
|
2bd07b54b6 | ||
|
|
7cf9d9ad65 | ||
|
|
4dff30ec8c | ||
|
|
581f14e661 | ||
|
|
8ccdc37b64 | ||
|
|
9e85b35498 | ||
|
|
fff408a5bf | ||
|
|
4d5168c998 | ||
|
|
bf2c978f1d | ||
|
|
ec06c1cdf1 | ||
|
|
f451cfce09 | ||
|
|
91e55aeb9b | ||
|
|
919fb5012f | ||
|
|
74a5fb656e | ||
|
|
2bb6226e78 | ||
|
|
6a0c47f7b1 | ||
|
|
31b688cbf6 | ||
|
|
7f1fed2fb1 | ||
|
|
aa6c876b12 | ||
|
|
4e33aeef89 | ||
|
|
e2601dcf05 | ||
|
|
247baa375d | ||
|
|
a4adba846e | ||
|
|
52799c7cb0 | ||
|
|
a8635dc555 | ||
|
|
cca0f2219e | ||
|
|
d2f8c3c2bb | ||
|
|
0f38df053f | ||
|
|
5c926a10a7 | ||
|
|
036bbb418e | ||
|
|
bc5ce74925 | ||
|
|
93d224fa37 | ||
|
|
5b45e3e417 | ||
|
|
338c652635 | ||
|
|
c2f2dfd837 | ||
|
|
2f2baf12fb | ||
|
|
052c339d0d | ||
|
|
96192e2e06 | ||
|
|
ea9fa30358 | ||
|
|
78f8e2f484 | ||
|
|
0fe2a3fb80 | ||
|
|
a340f52973 | ||
|
|
bd94b715ba | ||
|
|
b9a97ffa4c | ||
|
|
5a37ab1b89 | ||
|
|
67a6ac2240 | ||
|
|
7b42845ecc | ||
|
|
3ef39896d1 | ||
|
|
b01f3b505d | ||
|
|
84c5e4c30b | ||
|
|
abc0f3943e | ||
|
|
c7b71db015 | ||
|
|
f5a8a953bb | ||
|
|
8e965912aa | ||
|
|
6c3cfb0c7a | ||
|
|
85d162aa9d | ||
|
|
67c460dfa5 | ||
|
|
83d35dbc65 | ||
|
|
86735a5afd | ||
|
|
6ecbb89469 | ||
|
|
2ca0508030 | ||
|
|
8fbd50dcef | ||
|
|
2143660345 | ||
|
|
8c903fbfdd | ||
|
|
33be372348 | ||
|
|
447ec3f5e6 | ||
|
|
a4aed69887 | ||
|
|
4834761f64 | ||
|
|
bbbd6e9e3e | ||
|
|
06712faee9 | ||
|
|
48a90072ee | ||
|
|
0344f4d60b | ||
|
|
6a4affd5a6 | ||
|
|
d73e2288bb | ||
|
|
7d7500ba05 | ||
|
|
247fc5248b | ||
|
|
85fcd1ed33 | ||
|
|
4ab8f8cc25 | ||
|
|
ccdfe9bc26 | ||
|
|
dc47961cc2 | ||
|
|
87394cd330 | ||
|
|
b5a9c32c3e | ||
|
|
d16521f037 | ||
|
|
b553b16049 | ||
|
|
784548be57 | ||
|
|
cf96a80ead | ||
|
|
ede6babeaf | ||
|
|
9a57cae4bd | ||
|
|
1a296bf58c | ||
|
|
e900d2f35a | ||
|
|
69d2d3d942 | ||
|
|
b7ff49bdb2 | ||
|
|
d0ef5d0fe3 | ||
|
|
3284e0f60a | ||
|
|
8cec847188 | ||
|
|
2d44644a3d | ||
|
|
e32f55e433 | ||
|
|
362c4ae272 | ||
|
|
eb8ad04557 | ||
|
|
d1455596c6 | ||
|
|
6142bfc5db | ||
|
|
dbda0be53b | ||
|
|
bf932719b2 | ||
|
|
60e6e33805 | ||
|
|
64b8b5d3c8 | ||
|
|
8bce40c2b8 | ||
|
|
b3f6194fda | ||
|
|
fdbf2ab0a7 | ||
|
|
d7eb0dc509 | ||
|
|
1a34bf4460 | ||
|
|
4cf1b5c0e7 | ||
|
|
764b883579 | ||
|
|
3bd6767138 | ||
|
|
bef9025b6c | ||
|
|
a37f07d20b | ||
|
|
638946b1f5 | ||
|
|
30c869ff2a | ||
|
|
2c3fda9cb5 | ||
|
|
b11a7a678c | ||
|
|
02011f9ce5 | ||
|
|
39ae6a76cd | ||
|
|
7d9b102ec4 | ||
|
|
31684bf7ca | ||
|
|
7b36f8d122 | ||
|
|
f2a0be4f57 | ||
|
|
2cefab1c64 | ||
|
|
5b4b96f065 | ||
|
|
77949331e2 | ||
|
|
4f8c4f47e2 | ||
|
|
334137454e | ||
|
|
af7ea3efa8 | ||
|
|
6119417331 | ||
|
|
580397a82d | ||
|
|
23c3c47db4 | ||
|
|
df3073fb12 | ||
|
|
aa9664e187 | ||
|
|
44f4aee55d | ||
|
|
02861b8e01 | ||
|
|
9607110381 | ||
|
|
9ae12ff678 | ||
|
|
2bcb8636ca | ||
|
|
4c2960eeae | ||
|
|
7e2c76e872 | ||
|
|
30fcde7157 | ||
|
|
4971d3317d | ||
|
|
85ae7b01b8 | ||
|
|
5b2dff254f | ||
|
|
9f566624fe | ||
|
|
50fa95dff9 | ||
|
|
752083e9eb | ||
|
|
582c7f83f7 | ||
|
|
d95104cb92 | ||
|
|
6c58ab4554 | ||
|
|
085187cfac | ||
|
|
3d0f0a5adc | ||
|
|
eae8b431ee | ||
|
|
e3a34446c0 | ||
|
|
bfe57c3ac7 | ||
|
|
d4001be716 | ||
|
|
662a1817f2 | ||
|
|
2c99ef412d | ||
|
|
f53ed5b13b | ||
|
|
b5d51838c6 | ||
|
|
92fd70198a | ||
|
|
7f1990f851 | ||
|
|
797d7afc3e | ||
|
|
c5a23cdfa0 | ||
|
|
906358f1f8 | ||
|
|
638f2a59c5 | ||
|
|
cf9b4b869c | ||
|
|
671c52fbbb | ||
|
|
6c0e2a62e7 | ||
|
|
986c9d9f72 | ||
|
|
1b78c9ad81 | ||
|
|
7a4b98aec9 | ||
|
|
9ef6c15014 | ||
|
|
f4cea3fe03 | ||
|
|
5dd9b1ec91 | ||
|
|
658059806b | ||
|
|
4f8a18451c | ||
|
|
b1770ebb76 | ||
|
|
6923065d86 | ||
|
|
9f097521f6 | ||
|
|
235e5c1d3a | ||
|
|
e179976fe8 | ||
|
|
082726b405 | ||
|
|
91c5309855 | ||
|
|
92be1e7a5c | ||
|
|
cceda1db1e | ||
|
|
a59191cea7 | ||
|
|
b0dee4a60c | ||
|
|
3f33f2b9df | ||
|
|
97116b4e0c | ||
|
|
a5f51ff9a1 | ||
|
|
962488fd34 | ||
|
|
4db37059cd | ||
|
|
190d973b77 | ||
|
|
397e0b3f71 | ||
|
|
83ba9ca73e | ||
|
|
3d08138686 | ||
|
|
262fa6b99b | ||
|
|
372db9dcfa | ||
|
|
378c68be4b | ||
|
|
e9dc52919c | ||
|
|
a981dd0e97 | ||
|
|
7c2775119b | ||
|
|
0be7f1bdb5 | ||
|
|
8446b9e8d5 | ||
|
|
ce404668d3 | ||
|
|
948fab50ee | ||
|
|
9690f1df48 | ||
|
|
5a24f87293 | ||
|
|
d2ff49fe73 | ||
|
|
e9035df9d2 | ||
|
|
1ddf21f4fc | ||
|
|
63d2ded038 | ||
|
|
7b8e0e48c0 | ||
|
|
bb52402a17 | ||
|
|
13d9cb461b | ||
|
|
0a994afd67 | ||
|
|
ebca580a0b | ||
|
|
57a1257750 | ||
|
|
b0c1d36bab | ||
|
|
0621751968 | ||
|
|
461330773a | ||
|
|
818a97cc2c | ||
|
|
17045073c8 | ||
|
|
7a818ee698 | ||
|
|
668e0cb4eb | ||
|
|
741b16ca4e | ||
|
|
85a376b17d | ||
|
|
df86cd909c | ||
|
|
ac236607f5 | ||
|
|
19813b7eb6 | ||
|
|
cb5e4e076f | ||
|
|
48fca77f59 | ||
|
|
76372451aa | ||
|
|
34cd197122 | ||
|
|
6b567364f9 | ||
|
|
711de49571 | ||
|
|
9a0f7ad83f | ||
|
|
0d3d693799 | ||
|
|
b63590d6c7 | ||
|
|
5f3a3d4d54 | ||
|
|
413df647d3 | ||
|
|
b1a8c28283 | ||
|
|
1412737036 | ||
|
|
ffb3f4fa50 | ||
|
|
ff450ca43a | ||
|
|
d4f0805108 | ||
|
|
a1011ed709 | ||
|
|
64ce69d1c7 | ||
|
|
ca3cb48091 | ||
|
|
85085bf4c7 | ||
|
|
71be6d4ded | ||
|
|
873af6b598 | ||
|
|
c423895f31 | ||
|
|
4418e27c29 | ||
|
|
f776977af8 | ||
|
|
f586401a14 | ||
|
|
c13ce3d0f1 | ||
|
|
2c4c669ea2 | ||
|
|
29fe3dfd0b | ||
|
|
985e50d415 | ||
|
|
11150b6a10 | ||
|
|
c499acdc4a | ||
|
|
b24e3252d9 | ||
|
|
e15787b1e4 | ||
|
|
d5f19d97e2 | ||
|
|
5543a4aeed | ||
|
|
9c333232e2 | ||
|
|
fa384cb6f3 | ||
|
|
d52b95ea23 | ||
|
|
ef4fbcbb8a | ||
|
|
aa454b411f | ||
|
|
543e9339c7 | ||
|
|
7fff5c0d18 | ||
|
|
05db1bcbfb | ||
|
|
fa79f3f6fa | ||
|
|
c098839881 | ||
|
|
8d0d88c1b9 | ||
|
|
2b6ba0f410 | ||
|
|
11235009c0 | ||
|
|
4b05f7fdad | ||
|
|
bdd6d9781c | ||
|
|
338b0ae509 | ||
|
|
a437082952 | ||
|
|
ca9aba7b3b | ||
|
|
fe9f189734 | ||
|
|
018ac612f4 | ||
|
|
5bde40ec2b | ||
|
|
4d5780c192 | ||
|
|
ff6a810ad5 | ||
|
|
feec01ba00 | ||
|
|
1f05d12ef5 | ||
|
|
31aba14507 | ||
|
|
bbd6f13f36 | ||
|
|
68edbbbdb9 | ||
|
|
eb5a0dc1c9 | ||
|
|
d23a5ad91b | ||
|
|
7cea4f1792 | ||
|
|
c57c6abb1b | ||
|
|
a25b706c7b | ||
|
|
5d077e843d | ||
|
|
65bf3e9899 | ||
|
|
51ba3a01f5 | ||
|
|
f9ca611b8b | ||
|
|
a49b8728fd | ||
|
|
018737c42a | ||
|
|
a50e179744 | ||
|
|
9234b91089 | ||
|
|
f3fcef52dd | ||
|
|
7f8b741981 | ||
|
|
f1791a709c | ||
|
|
30307fb05e | ||
|
|
57d443be8d | ||
|
|
84844c5043 | ||
|
|
9000f05961 | ||
|
|
6bef9b5c6e | ||
|
|
ffef1f4820 | ||
|
|
10c7bdbcaa | ||
|
|
e8f9888a41 | ||
|
|
8bac702be6 | ||
|
|
128bcecfe3 | ||
|
|
1390b3c489 | ||
|
|
a0f41341ac | ||
|
|
8ffa7ef7ff | ||
|
|
deb4607081 | ||
|
|
49c62f80e8 | ||
|
|
139793f3c0 | ||
|
|
f237cb9655 | ||
|
|
9f18c460d8 | ||
|
|
306a41b442 | ||
|
|
b53f54295d | ||
|
|
7e4f066694 | ||
|
|
90d72489d9 | ||
|
|
045c455324 | ||
|
|
60da903360 | ||
|
|
6d2a71f37e | ||
|
|
c8f60a12a4 | ||
|
|
11b2d3aa43 | ||
|
|
3d9c81d850 | ||
|
|
8ccb1a24f8 | ||
|
|
48c6796128 | ||
|
|
fc403f920b | ||
|
|
ca19496b5c | ||
|
|
0ae38f8a40 | ||
|
|
ad868ba841 | ||
|
|
f2aa39aa85 | ||
|
|
e76e0fc351 | ||
|
|
a348913888 | ||
|
|
5507006c53 | ||
|
|
994429f098 | ||
|
|
8efdcb9c49 | ||
|
|
db9a40db2b | ||
|
|
25667499e6 | ||
|
|
a728cd2d91 | ||
|
|
3f5f3ef10b | ||
|
|
3811b8f0c0 | ||
|
|
0d708124c2 | ||
|
|
d31f73df14 | ||
|
|
3fed45438b | ||
|
|
8aa967fa1b | ||
|
|
6eaa3e342c | ||
|
|
6fc9e60f62 | ||
|
|
d81514e9be | ||
|
|
874d9f32a9 | ||
|
|
14b0d7abf0 | ||
|
|
c39ffcf51c | ||
|
|
6f60a91f4c | ||
|
|
8031df6f28 | ||
|
|
da1e859fda | ||
|
|
8d4d25f1d1 | ||
|
|
6aff27778d | ||
|
|
ca2dcbfec0 | ||
|
|
24b666a382 | ||
|
|
9e34295529 | ||
|
|
10c55d056b | ||
|
|
753ab3bdd7 | ||
|
|
feee8def6f | ||
|
|
c208f12f8c | ||
|
|
dc926bf838 | ||
|
|
55a76b5204 | ||
|
|
53b837e763 | ||
|
|
3a551cdf25 | ||
|
|
c0c7f87dc9 | ||
|
|
ac77fd138b | ||
|
|
30d6e9d67c | ||
|
|
47db4334a1 | ||
|
|
fce97179e1 | ||
|
|
4f16cd2d01 | ||
|
|
fa587691b1 | ||
|
|
e0044658f9 | ||
|
|
53de1ddb36 | ||
|
|
da7b046092 | ||
|
|
d888d83a98 | ||
|
|
430f83e8e9 | ||
|
|
e7acd14faa | ||
|
|
1b00fa74bc | ||
|
|
4d572d8173 | ||
|
|
dbda85d8d9 | ||
|
|
95cb5dd66c | ||
|
|
f64e1c3a6a | ||
|
|
26a686c412 | ||
|
|
2505383f53 | ||
|
|
1a1e9ac6be | ||
|
|
7f20a3179e | ||
|
|
46431f0187 | ||
|
|
1ff8ec78c9 | ||
|
|
7840230c62 | ||
|
|
523db0a005 | ||
|
|
cc906d49ba | ||
|
|
fc23af89d3 | ||
|
|
5d8829ba63 | ||
|
|
31ccd80894 | ||
|
|
bac2234616 | ||
|
|
bd61db76a3 | ||
|
|
bc99e3b992 | ||
|
|
b7314b0813 | ||
|
|
4759bd569f | ||
|
|
b88c28f864 | ||
|
|
774ccb05f8 | ||
|
|
0ac48cba34 | ||
|
|
e36880fe3a | ||
|
|
713cf5de2c | ||
|
|
0fa336411f | ||
|
|
8ebdb09d68 | ||
|
|
40bc53001e | ||
|
|
b1656d1eea | ||
|
|
7aa54bf979 | ||
|
|
231f9bca84 | ||
|
|
e4f8596c19 | ||
|
|
020b7233d0 | ||
|
|
85fc0b3e2f | ||
|
|
5dcc7c14f3 | ||
|
|
7993d27b11 | ||
|
|
1f8c54ce74 | ||
|
|
73a414a34b | ||
|
|
8fa19c4a51 | ||
|
|
0667ae3e15 | ||
|
|
db1d00cd07 | ||
|
|
b27f092bef | ||
|
|
4eaea8e586 | ||
|
|
89cd7d3bbb | ||
|
|
2fff1d8d21 | ||
|
|
850631f00e | ||
|
|
1cea8846cf | ||
|
|
af48a48559 | ||
|
|
655c1762aa | ||
|
|
f706ccfd87 | ||
|
|
71e4c7f117 | ||
|
|
ad6182f4bd | ||
|
|
86bf0f65b0 | ||
|
|
7742f7747d | ||
|
|
eb998c41f2 | ||
|
|
657bd7c97c | ||
|
|
c8286148c1 | ||
|
|
e6ba2cce62 | ||
|
|
6105ff44b4 | ||
|
|
72da77be01 | ||
|
|
4c29b31f1b | ||
|
|
6e640108ed | ||
|
|
157322441d | ||
|
|
61d967e6af | ||
|
|
99f2158e55 | ||
|
|
1cba78cc4e | ||
|
|
1770c1ee11 | ||
|
|
a57fbea0cc | ||
|
|
f0c27c83f1 | ||
|
|
7873ca564c | ||
|
|
5ebc1fec24 | ||
|
|
f2559b7d4d | ||
|
|
1ee92a248e | ||
|
|
8376f98f21 | ||
|
|
810a3e0171 | ||
|
|
2eac9c3795 | ||
|
|
75f2425668 | ||
|
|
2dd1d7e926 | ||
|
|
5bb05a0a51 | ||
|
|
bc2c5b00c6 | ||
|
|
09f5e158dd | ||
|
|
4656471a0a | ||
|
|
69d58a4273 | ||
|
|
53a27677d4 | ||
|
|
f243ad0348 | ||
|
|
b4ce6bbb42 | ||
|
|
fa32937045 | ||
|
|
812e5f3c8e | ||
|
|
e2981e802c | ||
|
|
4ae4705c73 | ||
|
|
fbf4b12713 | ||
|
|
e4ece3e0dc | ||
|
|
9f231421be | ||
|
|
b4fdbb5e48 | ||
|
|
fbf62ca30d | ||
|
|
05d2096fb8 | ||
|
|
7683af81bc | ||
|
|
bad10b3812 | ||
|
|
9f9522c2ed | ||
|
|
62879c3e52 | ||
|
|
45610f8dd7 | ||
|
|
18102cbd78 | ||
|
|
262d6a1529 | ||
|
|
1124ddaf90 | ||
|
|
981add3872 | ||
|
|
8d4b30140e | ||
|
|
b9c59137a8 | ||
|
|
0b1c7e06ca | ||
|
|
fcaf80cba6 | ||
|
|
6358b9eabb | ||
|
|
70a3fab1ff | ||
|
|
bdf86ace86 | ||
|
|
d57132d1e4 | ||
|
|
a6e87abf99 | ||
|
|
f1771610fe | ||
|
|
a88ea9780f | ||
|
|
bca0866d72 | ||
|
|
cebed93abf | ||
|
|
ee2b2e3569 | ||
|
|
cb8e9ee244 | ||
|
|
486184b16c | ||
|
|
9f9bd1988f | ||
|
|
ba1bfd1bc0 | ||
|
|
e12926e971 | ||
|
|
5b4db0de3b | ||
|
|
70ff55faa6 | ||
|
|
f2bd47d8bc | ||
|
|
e2b9c9e98e | ||
|
|
b0791a594f | ||
|
|
ea5eaa6ed2 | ||
|
|
ec3fd1d585 | ||
|
|
d76de22977 | ||
|
|
c0e3483cc7 | ||
|
|
0994cdf3b6 | ||
|
|
a76fd82262 | ||
|
|
2f919c7d69 | ||
|
|
f12592731b | ||
|
|
4a20e1a25d | ||
|
|
cc7111c3ff | ||
|
|
81972aed62 | ||
|
|
5e2092c6d4 | ||
|
|
d136f8ac91 | ||
|
|
5b23917940 | ||
|
|
2cda0a3798 | ||
|
|
f315ad1ce9 | ||
|
|
49f1c273c2 | ||
|
|
8518476c87 | ||
|
|
a34ed756db | ||
|
|
a48232c4e3 | ||
|
|
ac65e38390 | ||
|
|
eab3a753f5 | ||
|
|
9e5922e121 | ||
|
|
bf57b23776 | ||
|
|
8f4a20fd46 | ||
|
|
b3716da5ac | ||
|
|
2c129fd800 | ||
|
|
6fb18e7a25 | ||
|
|
38d28d6944 | ||
|
|
a187e1a7a2 | ||
|
|
7d7a19c7c2 | ||
|
|
6a2ae2153a | ||
|
|
f50ba1a91e | ||
|
|
b09575dbd6 | ||
|
|
c52ee3d91d | ||
|
|
1ced5c0425 | ||
|
|
e399a5c9b1 | ||
|
|
08dff5fb67 | ||
|
|
912760f2a7 | ||
|
|
4b62fdbf93 | ||
|
|
df593d43a7 | ||
|
|
89b1fba771 | ||
|
|
5505c91ae0 | ||
|
|
cc720ff399 | ||
|
|
29f07f6c56 | ||
|
|
a69e197a1f | ||
|
|
6dddd8eff8 | ||
|
|
51aada8922 | ||
|
|
b47bf40e8f | ||
|
|
48151bf365 | ||
|
|
659950996d | ||
|
|
6e656a4d0a | ||
|
|
bf308dd13d | ||
|
|
982f3b1943 | ||
|
|
7a4ea8cf1b | ||
|
|
7c0482a87c | ||
|
|
101ededd89 | ||
|
|
a8cbc0040e | ||
|
|
ed91bb445b | ||
|
|
f158a222f4 | ||
|
|
46bcad1fca | ||
|
|
5e147afb9f | ||
|
|
b061d4593f | ||
|
|
3fa520b4ef | ||
|
|
2df05b4afd | ||
|
|
e0569a6748 | ||
|
|
2e6ab398d9 | ||
|
|
94b03d2a6b | ||
|
|
f84fe30bb6 | ||
|
|
049ddef531 | ||
|
|
a1a214c742 | ||
|
|
c40a483ffa | ||
|
|
aff99acfae | ||
|
|
628129c08d | ||
|
|
679d34dfdf | ||
|
|
b2f65903ae | ||
|
|
2daefe0382 | ||
|
|
1214a11704 | ||
|
|
e55e0118c2 | ||
|
|
a1ac81e907 | ||
|
|
14ad68bf69 | ||
|
|
e3118c6e99 | ||
|
|
b41aeab8f8 | ||
|
|
1a738b358e | ||
|
|
f4b1e8035b | ||
|
|
324e985247 | ||
|
|
60faacd7d0 | ||
|
|
d4a4d7c346 | ||
|
|
189f8f1d27 | ||
|
|
ed353461da | ||
|
|
e9330d6c62 | ||
|
|
f53e7cc3f6 | ||
|
|
4f4258d532 | ||
|
|
c80910f355 | ||
|
|
12470ab178 | ||
|
|
704e091e9b | ||
|
|
f30f1e5c1f | ||
|
|
0741ce2197 | ||
|
|
fc81bae9b7 | ||
|
|
3fa68e4f96 | ||
|
|
05a767c7be | ||
|
|
8c1e8f6377 | ||
|
|
0fbae8d7da | ||
|
|
cec4b339f5 | ||
|
|
1a7656d8ee | ||
|
|
e173815810 | ||
|
|
620c768e05 | ||
|
|
7740679077 | ||
|
|
1ebc8ebff3 | ||
|
|
fa4fac70d5 | ||
|
|
8c83a18f93 | ||
|
|
a151ecfda0 | ||
|
|
dec0c0649c | ||
|
|
a356f893ac | ||
|
|
e2af4f74c3 | ||
|
|
99022b76e5 | ||
|
|
9dd6dc7523 | ||
|
|
6f588200d7 | ||
|
|
f3dbeae28e | ||
|
|
9f70361298 | ||
|
|
534353ce13 | ||
|
|
3f391a7354 | ||
|
|
0dd0752710 | ||
|
|
710fae4b0e | ||
|
|
de6c57a7ee | ||
|
|
7eee1fcd6a | ||
|
|
1fd2aedf96 | ||
|
|
ffdd7c063b | ||
|
|
a87af264eb | ||
|
|
3640cd2fdd | ||
|
|
f7cf25c4bb | ||
|
|
77dbd0d4c7 | ||
|
|
5a77c83e68 | ||
|
|
0a6763e08c | ||
|
|
b5aa206670 | ||
|
|
dbb4d9b64b | ||
|
|
6775edfd66 | ||
|
|
b06750ed65 | ||
|
|
57879c1891 | ||
|
|
65b55a76f2 | ||
|
|
8562a68306 | ||
|
|
160da46b5a | ||
|
|
ae5f70a739 | ||
|
|
4456e57466 | ||
|
|
24cd090c61 | ||
|
|
4f849f5d5f | ||
|
|
db2e86e84c | ||
|
|
14748ce2b3 | ||
|
|
527509732c | ||
|
|
aa9214477c | ||
|
|
db24ed8b55 | ||
|
|
349d1849d9 | ||
|
|
188be2fa8c | ||
|
|
5c4d7df734 | ||
|
|
37de18c670 | ||
|
|
0001e3844b | ||
|
|
65f171f701 | ||
|
|
c4308c239c | ||
|
|
2b6af3cb37 | ||
|
|
c5a16fbbc1 | ||
|
|
1db83f509b | ||
|
|
2a06304a2c | ||
|
|
7bbe684135 | ||
|
|
a996a88fc4 | ||
|
|
8cf9e7db51 | ||
|
|
ed981c67b7 | ||
|
|
6a77a0a5b9 | ||
|
|
bfb4b4431b | ||
|
|
3de44c0335 | ||
|
|
37afd1aec9 | ||
|
|
b2ac561673 | ||
|
|
c9bf7d9138 | ||
|
|
153a689bdb | ||
|
|
26b88f1a22 | ||
|
|
c66a5d028f | ||
|
|
8441c95a5c | ||
|
|
e0e32b0199 | ||
|
|
2bb76564e9 | ||
|
|
86ec25d4de | ||
|
|
ed9929963c | ||
|
|
36f50118ac | ||
|
|
72ddf27504 | ||
|
|
9bd36931ff | ||
|
|
056ef7a3d5 | ||
|
|
e483945d05 | ||
|
|
7a9c637e03 | ||
|
|
09ce543ea6 | ||
|
|
0ef24ebc3f | ||
|
|
a4419d3af6 | ||
|
|
935983c02a | ||
|
|
bd54ce5017 | ||
|
|
af6ed78b8e | ||
|
|
f72438996d | ||
|
|
9db2a55a5a | ||
|
|
950d90badb | ||
|
|
1864d180af | ||
|
|
0e82e98382 | ||
|
|
46023d35dc | ||
|
|
90d2e0a40b | ||
|
|
5f884e0796 | ||
|
|
16477052e2 | ||
|
|
66a05603f7 | ||
|
|
fe2a883386 | ||
|
|
ca7fdaa125 | ||
|
|
1283ec2008 | ||
|
|
d1f78f9048 | ||
|
|
45f43bfade | ||
|
|
4793ab4bc9 | ||
|
|
88b174dea8 | ||
|
|
06ca382bd7 | ||
|
|
b09175a8db | ||
|
|
677f68b08d | ||
|
|
8058b8dba4 | ||
|
|
4bc26f13c1 | ||
|
|
e6cf77e724 | ||
|
|
f8737c112e | ||
|
|
74b72ed9d4 | ||
|
|
4950391201 | ||
|
|
64a38d6e45 | ||
|
|
e2d8ee53f8 | ||
|
|
0204ff8dd5 | ||
|
|
4630ee6e93 | ||
|
|
c4f2abf143 | ||
|
|
e7444a0194 | ||
|
|
ffed7305ed | ||
|
|
334054bcd4 | ||
|
|
5bb98eb5b2 | ||
|
|
e842bd882a | ||
|
|
74bfb46f73 | ||
|
|
cd45bf4b70 | ||
|
|
51600bbcb0 | ||
|
|
8811a9bcaa | ||
|
|
4521f8a774 | ||
|
|
5b427783f8 | ||
|
|
9508983b15 | ||
|
|
fd09912cd7 | ||
|
|
01cc8584b6 | ||
|
|
c0b104e7c8 | ||
|
|
9a82021a82 | ||
|
|
1fb54a0b0f | ||
|
|
aa5ea61638 | ||
|
|
a59ec0258b | ||
|
|
b403f5cf71 | ||
|
|
0ac6a62b86 | ||
|
|
ed3743d3b6 | ||
|
|
3468cba000 | ||
|
|
5a4cdc5354 | ||
|
|
af4e19a57e | ||
|
|
26bb743679 | ||
|
|
96cc726e22 | ||
|
|
f4b00b01d0 | ||
|
|
127b2ca86d | ||
|
|
4b8b93e1b8 | ||
|
|
3aea412fe9 | ||
|
|
2aef96ad4f | ||
|
|
ec0a77230c | ||
|
|
b35e4bddd0 | ||
|
|
aa32beb341 | ||
|
|
efafe89b42 | ||
|
|
5ef2e49d9c | ||
|
|
1cfbf61a30 | ||
|
|
2bb5494b84 | ||
|
|
e8a41ccb47 | ||
|
|
7184f7f635 | ||
|
|
cc5727b7fa | ||
|
|
7f3d6ef6a5 | ||
|
|
44ce68097b | ||
|
|
d976cc6c19 | ||
|
|
d19d7d2a43 | ||
|
|
9bfaa02f97 | ||
|
|
2d9396b245 | ||
|
|
56e0da64ee | ||
|
|
997e9213f2 | ||
|
|
366569a23b | ||
|
|
36d5747b4f | ||
|
|
ea026c726c | ||
|
|
1e71e32c74 | ||
|
|
ed729bbd4f | ||
|
|
1a925221b7 | ||
|
|
af7b4db062 | ||
|
|
cfefe35e3f | ||
|
|
8d7845a44d | ||
|
|
3a15e6e5e9 | ||
|
|
3b8a51e855 | ||
|
|
f2cb34cc35 | ||
|
|
723ce62a34 | ||
|
|
2c9a2ea021 | ||
|
|
2be008d6d1 | ||
|
|
7fb51bdd0a | ||
|
|
abdbe3694f | ||
|
|
bd87e5012f | ||
|
|
55e18f2364 | ||
|
|
655e84072d | ||
|
|
ab5409d5b6 | ||
|
|
5a5da6486c | ||
|
|
7e99252429 | ||
|
|
10e0dcef8b | ||
|
|
80c8433aad | ||
|
|
5b0ae372f8 | ||
|
|
b5a1b529ab | ||
|
|
af821dcd9a | ||
|
|
10d3c81520 | ||
|
|
e3811b453a | ||
|
|
ca0fb05baa | ||
|
|
2b4b01bf6e | ||
|
|
3a1244bddc | ||
|
|
b8fd922b19 | ||
|
|
03f856025e | ||
|
|
45f04d24c3 | ||
|
|
2435d610c7 | ||
|
|
06572bcd14 | ||
|
|
3fab79afc0 | ||
|
|
2b7dd3fed5 | ||
|
|
d65efed032 | ||
|
|
c6896ea936 | ||
|
|
b7a41bddec | ||
|
|
6fd80c3737 | ||
|
|
0aa690b3b1 | ||
|
|
6d5bcfc6ed | ||
|
|
b60de69fa5 | ||
|
|
d991b57d08 | ||
|
|
1133d7fcba | ||
|
|
6fbf0383bb | ||
|
|
0296230219 | ||
|
|
dedfbba27a | ||
|
|
dc5441da07 | ||
|
|
585648b7f3 | ||
|
|
cd0ee1c26d | ||
|
|
d7a7be5af4 | ||
|
|
13e4267c11 | ||
|
|
15bc47e3e8 | ||
|
|
1a4380dcd7 | ||
|
|
5ace34b5cd | ||
|
|
f010c0f1fa | ||
|
|
2c721ad5dd | ||
|
|
8a7c2ce2c2 | ||
|
|
b2a7b813de | ||
|
|
e85cf04b99 | ||
|
|
ab6fa48172 | ||
|
|
c81905882f | ||
|
|
f0d31c1114 | ||
|
|
497b255216 | ||
|
|
d96286d771 | ||
|
|
2441647d75 | ||
|
|
77b780d631 | ||
|
|
cdd03bf3d4 | ||
|
|
e786c35f7d | ||
|
|
e83399acb1 | ||
|
|
ffea9e3210 | ||
|
|
f71a3a1ce3 | ||
|
|
a5ccc23604 | ||
|
|
1ed4c8a094 | ||
|
|
4a74549c1b | ||
|
|
a499cfb98f | ||
|
|
01038c8296 | ||
|
|
7407f79b3d | ||
|
|
a845dd1976 | ||
|
|
9d7549da70 | ||
|
|
f5cc61384f | ||
|
|
844778d36a | ||
|
|
871b0a274e | ||
|
|
737740a3ca | ||
|
|
ae8d600600 | ||
|
|
2f1691bfb0 | ||
|
|
a3d25f91c8 | ||
|
|
ff84b50817 | ||
|
|
0be8242284 | ||
|
|
e25a6259ea | ||
|
|
1578f82efb | ||
|
|
b5f75f0c5e | ||
|
|
601caab3b6 | ||
|
|
6d3092f440 | ||
|
|
ef27937c7a | ||
|
|
f4a7b79c4f | ||
|
|
2531776a48 | ||
|
|
ced80419aa | ||
|
|
ad2816f4e8 | ||
|
|
3528b8c674 | ||
|
|
80c83c626d | ||
|
|
6a355f2aea | ||
|
|
7d94e4a53a | ||
|
|
823c04742e | ||
|
|
043fb1abd1 | ||
|
|
f01e85b690 | ||
|
|
7eb1298847 | ||
|
|
a5480c9a96 | ||
|
|
f948600e86 | ||
|
|
420182b9bf | ||
|
|
8c430ce1a6 | ||
|
|
d3717dbe19 | ||
|
|
caa83d769f | ||
|
|
365f87828f | ||
|
|
f780887866 | ||
|
|
43627d4bb8 | ||
|
|
5d7b276cd1 | ||
|
|
23597eb997 | ||
|
|
b6948367f8 | ||
|
|
db6dd5abee | ||
|
|
702eb9e466 | ||
|
|
3c99006226 | ||
|
|
aaaf5683a5 | ||
|
|
92aa2236c0 | ||
|
|
d2592f72d6 | ||
|
|
27120d6cc9 | ||
|
|
a2669d35c3 | ||
|
|
574164081b | ||
|
|
00c2f8dcd8 | ||
|
|
0202bf60e5 | ||
|
|
16436a171b | ||
|
|
4cf4e0eabd | ||
|
|
5edb98dfb4 | ||
|
|
899bf22e15 | ||
|
|
9a33167a6c | ||
|
|
2dc068aa21 | ||
|
|
0f2aa4d2e1 | ||
|
|
a699f5c7bc | ||
|
|
ab807de3c0 | ||
|
|
68514bcb36 | ||
|
|
422bf8da59 | ||
|
|
de92dc93dd | ||
|
|
5a91b143b6 | ||
|
|
c745e85706 | ||
|
|
0f698e0c50 | ||
|
|
cd16d77bf0 | ||
|
|
fdbdd0ef4c | ||
|
|
71485f4a82 | ||
|
|
013056a6e5 | ||
|
|
263498fbdc | ||
|
|
f46ac2c007 | ||
|
|
c23841b6db | ||
|
|
18b1b5fca5 | ||
|
|
6707ac2697 | ||
|
|
d85a2fc8ce | ||
|
|
a8997e8f17 | ||
|
|
c8a22d9537 | ||
|
|
910669f786 | ||
|
|
89312c6796 | ||
|
|
a6aa96fdb0 | ||
|
|
7deb0b06af | ||
|
|
784f3771b3 | ||
|
|
80316824f7 | ||
|
|
cbaebb7b7c | ||
|
|
97d3764b6e | ||
|
|
002f8117cb | ||
|
|
0dfdb12431 | ||
|
|
d77e898929 | ||
|
|
4c937be884 | ||
|
|
c25cce4398 | ||
|
|
58c422285a | ||
|
|
f2f22f35e8 | ||
|
|
15867a7807 | ||
|
|
22e3161a9b | ||
|
|
b0c8203b24 | ||
|
|
e2c404d8bb | ||
|
|
5ee1731c92 | ||
|
|
06e8d3b660 | ||
|
|
f09709aedb | ||
|
|
598d5decac | ||
|
|
7832a6053e | ||
|
|
588748230e | ||
|
|
8668165c72 | ||
|
|
a967332f89 | ||
|
|
6719d242bd | ||
|
|
3565185808 | ||
|
|
ce51262197 | ||
|
|
992a786c2b | ||
|
|
038df95aa0 | ||
|
|
4ebd660de2 | ||
|
|
a9ea41a488 | ||
|
|
196594b490 | ||
|
|
1516d5af10 | ||
|
|
ffcd1595a9 | ||
|
|
42e5c6e111 | ||
|
|
8a9651dc50 | ||
|
|
4be2f4f2d9 | ||
|
|
369bf7a235 | ||
|
|
136545b404 | ||
|
|
73ea17ae0f | ||
|
|
23010acef4 | ||
|
|
4f74962ce2 | ||
|
|
7f978c7845 | ||
|
|
731fd46ea2 | ||
|
|
859759d691 | ||
|
|
5e9567158f | ||
|
|
51d244f3aa | ||
|
|
4c5a07edd5 | ||
|
|
8113a754a1 | ||
|
|
3cd40c6195 | ||
|
|
56cfb2ce06 | ||
|
|
72087823ac | ||
|
|
bcf7866fe2 | ||
|
|
b64772e484 | ||
|
|
088d665942 | ||
|
|
7d6c10befb | ||
|
|
f88e3d04b8 | ||
|
|
f57676a22b | ||
|
|
8618cb62e4 | ||
|
|
c31c88ed1f | ||
|
|
6022acd21f | ||
|
|
b3071b9272 | ||
|
|
ec6a6d6653 | ||
|
|
52f552b4de | ||
|
|
2643d3396b | ||
|
|
62d59a09cf | ||
|
|
a68ff8a4f0 | ||
|
|
93d4bfcd7a | ||
|
|
5fb80c1a7b | ||
|
|
04b38444ce | ||
|
|
ca3df02002 | ||
|
|
d952c33fab | ||
|
|
93bc1cd5af | ||
|
|
0e5b370ee8 | ||
|
|
f4fd4e28c9 | ||
|
|
ec8433eb45 | ||
|
|
0d4a40e9bb | ||
|
|
c3668f4179 | ||
|
|
3adb7ecb1c | ||
|
|
aa5b84a214 | ||
|
|
3d83fccbf1 | ||
|
|
4c4255be6b | ||
|
|
ed6054b082 | ||
|
|
f60f7a4dc1 | ||
|
|
998dc95cb2 | ||
|
|
12f5d7b846 | ||
|
|
906ae0a43e | ||
|
|
485b774876 | ||
|
|
3121cf5b75 | ||
|
|
ce7570dae2 | ||
|
|
b5ea0dfbc7 | ||
|
|
dd0f8f4772 | ||
|
|
9d0ab34274 | ||
|
|
e6038be6f7 | ||
|
|
bd2153a0ed | ||
|
|
e526deac20 | ||
|
|
b65f165dcf | ||
|
|
f9fbe5adc2 | ||
|
|
d1e73571a1 | ||
|
|
1047058676 | ||
|
|
29a1e77da8 | ||
|
|
290547e482 | ||
|
|
3df45ca20f | ||
|
|
cc06258484 | ||
|
|
2c262d0655 | ||
|
|
1875e1e158 | ||
|
|
1c95967b31 | ||
|
|
2ae98929de | ||
|
|
6a1dd38cbb | ||
|
|
c458a77d27 | ||
|
|
352c23f389 | ||
|
|
6c5a1dc2e1 | ||
|
|
004ff1e91d | ||
|
|
f7de4c567b | ||
|
|
b2ad1010ac | ||
|
|
660e164ff8 | ||
|
|
eb6dc80b50 | ||
|
|
a9882cc96a | ||
|
|
fb5a2ce178 | ||
|
|
d441b31dc7 | ||
|
|
d4239fe7e0 | ||
|
|
6a98999105 | ||
|
|
5caa1fe7d4 | ||
|
|
ef33bdb65e | ||
|
|
14a463382b | ||
|
|
1ad79874c8 | ||
|
|
1a61a58d28 | ||
|
|
3e3c973679 | ||
|
|
9dede84d20 | ||
|
|
e78b11cf62 | ||
|
|
341ca5a330 | ||
|
|
e5d8c02def | ||
|
|
947aa08c42 | ||
|
|
9a04d436bd | ||
|
|
be0754f568 | ||
|
|
93c4bbc0d1 | ||
|
|
b98535810b | ||
|
|
393cf8da1f | ||
|
|
ea2846973a | ||
|
|
3398c1cbfe | ||
|
|
39c79ea68e | ||
|
|
66900f4a32 | ||
|
|
df71dd14f7 | ||
|
|
8113ca3069 | ||
|
|
d468e675c2 | ||
|
|
1e84cedf82 | ||
|
|
7f4087e3de | ||
|
|
facd3c8956 | ||
|
|
3e50deecb7 | ||
|
|
628c504f84 | ||
|
|
e1ada78c1f | ||
|
|
09fa1e365c | ||
|
|
28b6b03d06 | ||
|
|
a4395b62ff | ||
|
|
4511acf273 | ||
|
|
fde53a2d83 | ||
|
|
f6cd04fdb8 | ||
|
|
a5eede6c7a | ||
|
|
2e83817c83 | ||
|
|
82aea40ae4 | ||
|
|
1b289eae24 | ||
|
|
81420b2c88 | ||
|
|
775731b745 | ||
|
|
489e7d4a67 | ||
|
|
b6508764d8 | ||
|
|
6dedd87305 | ||
|
|
89fa29b310 | ||
|
|
be5df9c22a | ||
|
|
db17739716 | ||
|
|
1439362a5e | ||
|
|
65c1b04772 | ||
|
|
83d30ebdc4 | ||
|
|
b7928a5255 | ||
|
|
a83ff9253d | ||
|
|
8cdcdaf6d9 | ||
|
|
d208edf153 | ||
|
|
8ada4765b1 | ||
|
|
9d3a808c24 | ||
|
|
828eb80266 | ||
|
|
0639f6c1d1 | ||
|
|
49b971280f | ||
|
|
3682a1010d | ||
|
|
f8b58866dc | ||
|
|
5d8d815d84 | ||
|
|
67743c7597 | ||
|
|
b33cc90447 | ||
|
|
78177f7890 | ||
|
|
3c417bfa99 | ||
|
|
f2c8d80ff8 | ||
|
|
8c1eb1e19f | ||
|
|
eb7188e81b | ||
|
|
f352fc5c67 | ||
|
|
ac313722f9 | ||
|
|
64c7f1962c | ||
|
|
c83f994b21 | ||
|
|
10f38a72f7 | ||
|
|
da35434f58 | ||
|
|
c79409e094 | ||
|
|
355ce00968 | ||
|
|
56c832f3ba | ||
|
|
40abb2720e | ||
|
|
9b6051afee | ||
|
|
dcf6f26792 | ||
|
|
85734c1146 | ||
|
|
c3fb8fcb6e | ||
|
|
013bbc1638 | ||
|
|
b0669a3b60 | ||
|
|
6f5da1ce7c | ||
|
|
3351732a2f | ||
|
|
d4d4034ecd | ||
|
|
b45e2742c3 | ||
|
|
42eba290e4 | ||
|
|
32d1e08b57 | ||
|
|
7b7d19da7c | ||
|
|
f78e400918 | ||
|
|
be4e9bf7e9 | ||
|
|
48f2079f88 | ||
|
|
36ad1ceef2 | ||
|
|
1f2e2148d5 | ||
|
|
76f3d345f9 | ||
|
|
f2c854b53a | ||
|
|
e2d336d90b | ||
|
|
c74065ff26 | ||
|
|
b6d97dc5d5 | ||
|
|
1c1c26948a | ||
|
|
d954e3ffb7 | ||
|
|
d9759a95eb | ||
|
|
d196402dd0 | ||
|
|
0f2b5531de | ||
|
|
26e4398905 | ||
|
|
06995f22fe | ||
|
|
caa2073d48 | ||
|
|
d9466fe4bc | ||
|
|
72e623a3a7 | ||
|
|
69bc06685f | ||
|
|
bf175984f3 | ||
|
|
4d4a7bfd0d | ||
|
|
044cee30a5 | ||
|
|
3c0c5aed60 | ||
|
|
e194971727 | ||
|
|
bbe85def23 | ||
|
|
9c2d53f2ae | ||
|
|
1cf36e2156 | ||
|
|
ce96e309af | ||
|
|
9ea7dc8b3c | ||
|
|
282242efd2 | ||
|
|
351ba22d64 | ||
|
|
2916625747 | ||
|
|
744dbeb7a3 | ||
|
|
fbb3c309ee | ||
|
|
473c265bac | ||
|
|
01b06c2c3c | ||
|
|
8b1f95c743 | ||
|
|
ff5dbec579 | ||
|
|
9620c5a98f | ||
|
|
7e849a2e95 | ||
|
|
229aed306e | ||
|
|
d718969cbd | ||
|
|
8593f4c3a9 | ||
|
|
d4f71d5026 | ||
|
|
a683a5f206 | ||
|
|
7917cc3eb5 | ||
|
|
6cd7ca755e | ||
|
|
fb7dfdc800 | ||
|
|
e096af320f | ||
|
|
65bff8f511 | ||
|
|
ca19d8a842 | ||
|
|
cde08c64ce | ||
|
|
3d6d7b7cb9 | ||
|
|
a56ef6f398 | ||
|
|
5a38b6614d | ||
|
|
3d5c44e8aa | ||
|
|
46a68bd5e7 | ||
|
|
c77ded51a9 | ||
|
|
59bd9fdc22 | ||
|
|
b81941394c | ||
|
|
197d5ca1f2 | ||
|
|
97eb986fa6 | ||
|
|
aba3110e31 | ||
|
|
51c27be236 | ||
|
|
592255284f | ||
|
|
82fd41dd4c | ||
|
|
4ae8df9652 | ||
|
|
759b19e444 | ||
|
|
36f705e46f | ||
|
|
1f0347682e | ||
|
|
1aee385679 | ||
|
|
5dd8191692 | ||
|
|
b6422f9b80 | ||
|
|
a6c2c9c92f | ||
|
|
247c573a6e | ||
|
|
aa435d6e94 | ||
|
|
f6858c221b | ||
|
|
437aa4510b | ||
|
|
430572fb32 | ||
|
|
9f0b755d6f | ||
|
|
66acb55a57 | ||
|
|
f936d4c36e | ||
|
|
c3ddb8df56 | ||
|
|
81ce920e69 | ||
|
|
3cb875d139 | ||
|
|
fdb217d5c6 | ||
|
|
085cae3b5f | ||
|
|
e2f99a1554 | ||
|
|
aee0c27da7 | ||
|
|
accbf4ad8b | ||
|
|
0ac1901f6b | ||
|
|
c0a0deea78 | ||
|
|
e3f099441c | ||
|
|
840dd25cd1 | ||
|
|
a493969f9b | ||
|
|
cca42a10a1 | ||
|
|
2f4aa98c3c | ||
|
|
175cb9588c | ||
|
|
a8976e7559 | ||
|
|
b663528fb0 | ||
|
|
1a2ad55677 | ||
|
|
513d2a88c0 | ||
|
|
57ef4c0396 | ||
|
|
e9599ca2f4 | ||
|
|
2fc6dbc222 | ||
|
|
225933c442 | ||
|
|
36e2f048d7 | ||
|
|
0e158bce59 | ||
|
|
202201fd31 | ||
|
|
4f8c928f44 | ||
|
|
ada062cf00 | ||
|
|
32dc12912a | ||
|
|
671d21adf4 | ||
|
|
4e3f8e4b67 | ||
|
|
586bb62073 | ||
|
|
151bf83ab1 | ||
|
|
8c618edb5a | ||
|
|
6d6b0cf8c9 | ||
|
|
c610608890 | ||
|
|
28da61daab | ||
|
|
cbfc90f60b | ||
|
|
fec92959d4 | ||
|
|
bb00d81eba | ||
|
|
bb55644c06 | ||
|
|
16215847cd | ||
|
|
8ee9aed817 | ||
|
|
515c6402b9 | ||
|
|
d1b94ec203 | ||
|
|
a037d9167e | ||
|
|
3054d33e62 | ||
|
|
6651e641e1 | ||
|
|
35f873a342 | ||
|
|
2d03ff38a1 | ||
|
|
44a3db417b | ||
|
|
f9ca702a12 | ||
|
|
4073ff38eb | ||
|
|
f0e2c9f1b6 | ||
|
|
cf040f51b5 | ||
|
|
dcf919fb36 | ||
|
|
aa19b81a68 | ||
|
|
db8d77365c | ||
|
|
280eaea84a | ||
|
|
340ef46469 | ||
|
|
a5f8651941 | ||
|
|
8a18ee548e | ||
|
|
ef791aa22a | ||
|
|
4bdf2e1cc0 | ||
|
|
ffa0ca9379 | ||
|
|
d3b3d46fc1 | ||
|
|
7e7dd8efa9 | ||
|
|
b4506f1133 | ||
|
|
7350c79113 | ||
|
|
5f7b60d3f4 | ||
|
|
266495a611 | ||
|
|
f5b4a7e77b | ||
|
|
51ed0f6487 | ||
|
|
bd70def18a | ||
|
|
93a9cf3b31 | ||
|
|
751287999f | ||
|
|
d433850cbf | ||
|
|
c93e70e2dc | ||
|
|
a129dd989a | ||
|
|
40999c3ff4 | ||
|
|
18a01d672c | ||
|
|
3648f99920 | ||
|
|
e823412f56 | ||
|
|
d090f446c9 | ||
|
|
19985d1742 | ||
|
|
ab52251116 | ||
|
|
acb6c0187c | ||
|
|
90f9c24720 | ||
|
|
415bec4646 | ||
|
|
5559f20511 | ||
|
|
6ea3399829 | ||
|
|
9e3df97737 | ||
|
|
0ac40a4d77 | ||
|
|
24bf5e8102 | ||
|
|
56eebb03c7 | ||
|
|
1cd4d6d0df | ||
|
|
22d4a8232a | ||
|
|
48076c25bf | ||
|
|
478a308e73 | ||
|
|
8ca2f0a49f | ||
|
|
4a35d7364b | ||
|
|
8b2e55a969 | ||
|
|
9b14b70687 | ||
|
|
1c01087eda | ||
|
|
5137e5a301 | ||
|
|
0fea8365b8 | ||
|
|
116a615b84 | ||
|
|
ef272dd6a8 | ||
|
|
4462ae9cae | ||
|
|
66ffad0b4e | ||
|
|
81e0fc940c | ||
|
|
3e8794db1b | ||
|
|
617622d4d8 | ||
|
|
a4240f972b | ||
|
|
42feff8882 | ||
|
|
70edd9686f | ||
|
|
d298cb2e1c | ||
|
|
3fef3f2dc3 | ||
|
|
c85f9d6ae4 | ||
|
|
85e7ac7cd7 | ||
|
|
5c7db61a7e | ||
|
|
497f9ca0b1 | ||
|
|
0c50f7a322 | ||
|
|
740bcbd12c | ||
|
|
c92be4f270 | ||
|
|
e2a7271ab2 | ||
|
|
64766713fa | ||
|
|
59bba83b1d | ||
|
|
8ac1217165 | ||
|
|
5443629ec5 | ||
|
|
8f9b41cb62 | ||
|
|
7df5eba775 | ||
|
|
e29c6ac593 | ||
|
|
4f3190bf73 | ||
|
|
f0878fccb5 | ||
|
|
b0e121a53f | ||
|
|
eda48ab0e6 | ||
|
|
6a1014d8c1 | ||
|
|
e0f87315bc | ||
|
|
f2c5dca5e7 | ||
|
|
3c435a804f | ||
|
|
7ee6775251 | ||
|
|
98adc2ecc1 | ||
|
|
45afbbdac6 | ||
|
|
a18260747b | ||
|
|
0c80e28754 | ||
|
|
719bc374ac | ||
|
|
d7a290c595 | ||
|
|
4b2bd33baa | ||
|
|
efbe4d697c | ||
|
|
a8662bdb8b | ||
|
|
d822225465 | ||
|
|
c6373ef582 | ||
|
|
5a2af6d0f9 | ||
|
|
52000d6a41 | ||
|
|
befa13eaf2 | ||
|
|
4ac68a81a3 | ||
|
|
71e472f330 | ||
|
|
ada8809ec0 | ||
|
|
25ea518266 | ||
|
|
f3720c3b94 | ||
|
|
b942a84b15 | ||
|
|
574ca90229 | ||
|
|
5610a14e49 | ||
|
|
c5bc62e6df | ||
|
|
e9f6a85cad | ||
|
|
8921011b27 | ||
|
|
90ef3f6c94 | ||
|
|
e84c75f4a8 | ||
|
|
68e20cd860 | ||
|
|
76bdcea4b1 | ||
|
|
f758cb7e6e | ||
|
|
1108477810 | ||
|
|
deb6a9e51c | ||
|
|
9660f20b87 | ||
|
|
4d26468ede | ||
|
|
125a0979d5 | ||
|
|
98bdfd3dbe | ||
|
|
ea72880e74 | ||
|
|
17fec7d6e1 | ||
|
|
d9de64604e | ||
|
|
6275ab23ff | ||
|
|
8c0271643d | ||
|
|
c3f041dc87 | ||
|
|
80e5e84341 | ||
|
|
938eee80a9 | ||
|
|
7abc2289de | ||
|
|
bb79b9ed74 | ||
|
|
1e89a8625c | ||
|
|
90b0f04b3c | ||
|
|
0f019cd9b6 | ||
|
|
1209739398 | ||
|
|
b99db2b353 | ||
|
|
f057688e7d | ||
|
|
12ae2d0c76 | ||
|
|
9d91d5a127 | ||
|
|
6015f0887a | ||
|
|
d4c473d7b3 | ||
|
|
2f3978deed | ||
|
|
34af53a15b | ||
|
|
a4eb3d7a0b | ||
|
|
5b46dd7293 | ||
|
|
4b17847ea5 | ||
|
|
acbe000f97 | ||
|
|
599071b68b | ||
|
|
2bacee919d | ||
|
|
50d35e4196 | ||
|
|
e321998b85 | ||
|
|
340c02b2af | ||
|
|
69a295fe57 | ||
|
|
64830aae9f | ||
|
|
8969b1273f | ||
|
|
475faf7943 | ||
|
|
45b1d405a6 | ||
|
|
f60c8078e4 | ||
|
|
6701e49f9a | ||
|
|
0efb3c3284 | ||
|
|
aaf3cdfdac | ||
|
|
e00c261777 | ||
|
|
1eafed755d | ||
|
|
7f6a08ae50 | ||
|
|
9901816fb9 | ||
|
|
3cbe6c1e95 | ||
|
|
679c99274e | ||
|
|
b6fa0f1ff6 | ||
|
|
9ff64b95e1 | ||
|
|
3a9885abd8 | ||
|
|
503210942c | ||
|
|
0178e015e3 | ||
|
|
7604992c35 | ||
|
|
ee9e551788 | ||
|
|
22063248ca | ||
|
|
e4ce18b35c | ||
|
|
532890674e | ||
|
|
791a3b67e6 | ||
|
|
84b560ef29 | ||
|
|
03f081f3f4 | ||
|
|
08213ae86e | ||
|
|
73abc511a8 | ||
|
|
1c943cc259 | ||
|
|
af62a89a6b | ||
|
|
af7ca5b897 | ||
|
|
27356ef747 | ||
|
|
b27f80ef87 | ||
|
|
b4aa73fc64 | ||
|
|
bbf444572b | ||
|
|
b3706574de | ||
|
|
83062f8bfb | ||
|
|
1d7fcfdaa1 | ||
|
|
58ab12b4cb | ||
|
|
28c649629f | ||
|
|
5ec190225d | ||
|
|
0e0d404997 | ||
|
|
3f16b908ca | ||
|
|
54549b261c | ||
|
|
8ce07ced9e | ||
|
|
37a5144e8f | ||
|
|
59fed7dda8 | ||
|
|
0a85a2868c | ||
|
|
0d493b3250 | ||
|
|
74530d4b1e | ||
|
|
7548c52e21 | ||
|
|
fa49844c64 | ||
|
|
3508ae1e0a | ||
|
|
f013ee64a2 | ||
|
|
af3da7ca6e | ||
|
|
cb728f65b3 | ||
|
|
af6af190cc | ||
|
|
cbcc8455ca | ||
|
|
af35fb79fe | ||
|
|
0515aaa946 | ||
|
|
1d00330e7a | ||
|
|
3fa6dcea16 | ||
|
|
51425cac4a | ||
|
|
7ce61a5d2b | ||
|
|
5e5caa979b | ||
|
|
a719518f8f | ||
|
|
d0e5aef443 | ||
|
|
b82a811c33 | ||
|
|
3356dcf8f7 | ||
|
|
8f1cc26537 | ||
|
|
27dafa83ac | ||
|
|
c1be1f329f | ||
|
|
c757cef549 | ||
|
|
da9e4c026c | ||
|
|
5821d67e69 | ||
|
|
ed14f6d13f | ||
|
|
35bdd8b4ef | ||
|
|
9cf2d30e77 | ||
|
|
a2140a3b7b | ||
|
|
900d026bcb | ||
|
|
68604ec15a | ||
|
|
f7c0ebb8d3 | ||
|
|
8be5d0c72d | ||
|
|
15c8b724e6 | ||
|
|
d6949200f9 | ||
|
|
1c2abe794a | ||
|
|
0bcf393586 | ||
|
|
bc0573918f | ||
|
|
175675ad99 | ||
|
|
b00c2afc46 | ||
|
|
c125a7fe07 | ||
|
|
a42ab32436 | ||
|
|
797a64976e | ||
|
|
ac377a8b68 | ||
|
|
842b7b1402 | ||
|
|
d449396ad5 | ||
|
|
532a87d064 | ||
|
|
e1cdc7b387 | ||
|
|
d9d917e267 | ||
|
|
8048788042 | ||
|
|
790fc88e47 | ||
|
|
7f970d489f | ||
|
|
9d85ca07f4 | ||
|
|
d75e3acdf3 | ||
|
|
694cda0e99 | ||
|
|
94f134c6a7 | ||
|
|
83f329d93c | ||
|
|
de49387fca | ||
|
|
e5567f2f46 | ||
|
|
80f545f3d5 | ||
|
|
06f0cc70b8 | ||
|
|
cf6b7544b0 | ||
|
|
d511c6334a | ||
|
|
0224fd6d54 | ||
|
|
e95ae8dbcf | ||
|
|
5fbd64da71 | ||
|
|
b282a2a621 | ||
|
|
9a7a534051 | ||
|
|
52fd030b6e | ||
|
|
dfe530a764 | ||
|
|
b079956075 | ||
|
|
e30037c4d1 | ||
|
|
eda7be627c | ||
|
|
af0a649656 | ||
|
|
8b6a3c4236 | ||
|
|
0988a92d8a | ||
|
|
9cf67764b7 | ||
|
|
6c4e1d1c41 | ||
|
|
b1cd1ea8b3 | ||
|
|
5169ed494c | ||
|
|
824211c31b | ||
|
|
be3dd83bc7 | ||
|
|
c2911c1e48 | ||
|
|
9a66c5c07d | ||
|
|
34d393b986 | ||
|
|
d4da02318d | ||
|
|
47162af6d5 | ||
|
|
8cd6a72dd3 | ||
|
|
ba0a183b6f | ||
|
|
73781c7edb | ||
|
|
6d99852c81 | ||
|
|
9325c726fd | ||
|
|
947bb8530e | ||
|
|
2b32f316ee | ||
|
|
4b1f23a189 | ||
|
|
71d220e7a4 | ||
|
|
747d0d0d17 | ||
|
|
5c72e6d335 | ||
|
|
e1ac2b0e10 | ||
|
|
0ba94f3bc9 | ||
|
|
ba0bfafcd5 | ||
|
|
81adb80b7e | ||
|
|
cb238ef170 | ||
|
|
b9b921de82 | ||
|
|
d10e31c278 | ||
|
|
fd641d77c7 | ||
|
|
2aa9710dd1 | ||
|
|
4afb2ef2b8 | ||
|
|
a54e6e7c4b | ||
|
|
7af26a356f | ||
|
|
2f66165f8c | ||
|
|
e86ce8fc38 | ||
|
|
9b48c65129 | ||
|
|
434cd133df | ||
|
|
aa91e4cdee | ||
|
|
d57c1e7ff0 | ||
|
|
13e97703e5 | ||
|
|
c597b293b8 | ||
|
|
6620d64ce7 | ||
|
|
2ae120c878 | ||
|
|
7a25035fb1 | ||
|
|
bf4052b550 | ||
|
|
5ca5ad2cee | ||
|
|
2848f1e13c | ||
|
|
f7895823cb | ||
|
|
b841c5c5e5 | ||
|
|
a7952a4633 | ||
|
|
d047d26dbf | ||
|
|
ddedc1640f | ||
|
|
021d7e5efc | ||
|
|
332e528012 | ||
|
|
a0155da06b | ||
|
|
d58d22adbe | ||
|
|
653352bff0 | ||
|
|
4f5b33d8df | ||
|
|
7e7d83ac36 | ||
|
|
0de5c043bb | ||
|
|
ec2769ea3c | ||
|
|
3f742f5f8e | ||
|
|
50849101d4 | ||
|
|
1ccf4a74c9 | ||
|
|
7cd4967963 | ||
|
|
21e2700da5 | ||
|
|
395a68ad80 | ||
|
|
b7f0132675 | ||
|
|
4ac827b9e8 | ||
|
|
d96963862e | ||
|
|
53217b061d | ||
|
|
4770daa7c6 | ||
|
|
2b709ad094 | ||
|
|
378b81b13b | ||
|
|
56ee72214f | ||
|
|
2e7c3167f5 | ||
|
|
a2fb728d3b | ||
|
|
be1c936e90 | ||
|
|
af8037ab03 | ||
|
|
3b8dc98226 | ||
|
|
b411b4e61b | ||
|
|
436eb30490 | ||
|
|
09b8087787 | ||
|
|
4ac4c69820 | ||
|
|
3ebc5c0865 | ||
|
|
7b94f8f105 | ||
|
|
ec994b3e97 | ||
|
|
86ae0182fd | ||
|
|
bfa891f0ca | ||
|
|
ef843cac63 | ||
|
|
78d68d0a4f | ||
|
|
4bceba777d | ||
|
|
2e537b1e5e | ||
|
|
df8463b625 | ||
|
|
f40371e0cf | ||
|
|
a565e7aed6 | ||
|
|
c948361090 | ||
|
|
4cf2b74a01 | ||
|
|
de9c8362ac | ||
|
|
b5bb5d35e7 | ||
|
|
d910cfa919 | ||
|
|
5857e3d5c6 | ||
|
|
25daa343c6 | ||
|
|
85224c8f0c | ||
|
|
5f8583e3eb | ||
|
|
062821d267 | ||
|
|
1fdaaf82d2 | ||
|
|
e4c1b17810 | ||
|
|
70057542c1 | ||
|
|
f2255ee707 | ||
|
|
791cc70b09 | ||
|
|
24dcb9973b | ||
|
|
adfd0d5c18 | ||
|
|
cfb128acb8 | ||
|
|
1e8e246ffb | ||
|
|
6162244730 | ||
|
|
86cbdf2442 | ||
|
|
5334626efb | ||
|
|
708d473b47 | ||
|
|
ead954ddaa | ||
|
|
294f511b9a | ||
|
|
b5ebcc3e07 | ||
|
|
7b8e7ac5c2 | ||
|
|
904d39beac | ||
|
|
8958b61fdd | ||
|
|
09293f7d9a | ||
|
|
d520c3d674 | ||
|
|
430e616328 | ||
|
|
976ad5fcee | ||
|
|
4d58ee2162 | ||
|
|
03b6abe5ec | ||
|
|
4479304f3f | ||
|
|
ed83405254 | ||
|
|
b815d945d9 | ||
|
|
2b8e024f48 | ||
|
|
2a0399b98d | ||
|
|
4e333e2d75 | ||
|
|
4f25b7bbbe | ||
|
|
3678db649b | ||
|
|
9d8b1fd99b | ||
|
|
7b62c06be3 | ||
|
|
bc978a91e3 | ||
|
|
611ba97b60 | ||
|
|
39dff1ea7c | ||
|
|
bcf7bcba1d | ||
|
|
8f00730189 | ||
|
|
1dde79ace2 | ||
|
|
5954fb91be | ||
|
|
e192383662 | ||
|
|
4a20fad4e5 | ||
|
|
a469e0864e | ||
|
|
cfce2bdbd9 | ||
|
|
529b5c0a00 | ||
|
|
7cc5787779 | ||
|
|
48eeb279e2 | ||
|
|
d67566252a | ||
|
|
8a2d79e17d | ||
|
|
17370dff54 | ||
|
|
f08a8e7634 | ||
|
|
83543bbddc | ||
|
|
b898c90f41 | ||
|
|
1ec927cf4f | ||
|
|
aef21ba9d5 | ||
|
|
0b31709aee | ||
|
|
7c3433256a | ||
|
|
c79c638f35 | ||
|
|
a148941a39 | ||
|
|
23abe26b0b | ||
|
|
8f21d6dde0 | ||
|
|
b74a6c9e03 | ||
|
|
b63f73ef93 | ||
|
|
1f0b369a15 | ||
|
|
7a43473513 | ||
|
|
07f367a2a5 | ||
|
|
c52ad68d92 | ||
|
|
85467dbd2a | ||
|
|
aa767846f0 | ||
|
|
6d3ad15d20 | ||
|
|
7b95db4d13 | ||
|
|
160b3ff655 | ||
|
|
757a39a75e | ||
|
|
4c08a527be | ||
|
|
578b7b0512 | ||
|
|
45e0259099 | ||
|
|
1ac1933ec1 | ||
|
|
43353e89bb | ||
|
|
010e4610f7 | ||
|
|
ba38853406 | ||
|
|
4944a9e51e | ||
|
|
73ad97859c | ||
|
|
e600da229c | ||
|
|
273b4f20db | ||
|
|
9843dccdf0 | ||
|
|
60ae1ec1e8 | ||
|
|
650c6670f2 | ||
|
|
d6dcba7a60 | ||
|
|
5b3849082f | ||
|
|
5de988a2e6 | ||
|
|
26220b2f54 | ||
|
|
8d8436f1f1 | ||
|
|
93057d9449 | ||
|
|
2addcc3ab5 | ||
|
|
b2419abdc8 | ||
|
|
9f654918ae | ||
|
|
cb086fe08d | ||
|
|
1482166ba3 | ||
|
|
6b8dd9bf03 | ||
|
|
a8af9da249 | ||
|
|
e410f82fdb | ||
|
|
4f70f228f1 | ||
|
|
ddc97b99fa | ||
|
|
5f42918cc9 | ||
|
|
e6dfe6fe89 | ||
|
|
fdbe693139 | ||
|
|
0633b2876c | ||
|
|
ee45ed8ec2 | ||
|
|
0c6726e3f7 | ||
|
|
2a75995189 | ||
|
|
9f074c1ab7 | ||
|
|
2917ee3b9d | ||
|
|
03f631f537 | ||
|
|
fb4171ae6f | ||
|
|
aec7d5aff7 | ||
|
|
045c225c8c | ||
|
|
c49ef49371 | ||
|
|
9fd012229b | ||
|
|
bb27099f9e | ||
|
|
411634ecda | ||
|
|
bb59f66008 | ||
|
|
b8a0f34c33 | ||
|
|
784955cdbd | ||
|
|
83764ce3d5 | ||
|
|
438cf271c0 | ||
|
|
8631eacdf3 | ||
|
|
6e7ce10585 | ||
|
|
3d4a58cb96 | ||
|
|
6fde3f8453 | ||
|
|
016d2f7ab1 | ||
|
|
a8ffc414fe | ||
|
|
b0c1c6e880 | ||
|
|
d3e6177a2b | ||
|
|
8adbdcc675 | ||
|
|
d1d7ff0d0a | ||
|
|
4b05a49b46 | ||
|
|
0309a866c8 | ||
|
|
bbc77f81ca | ||
|
|
483f6f2188 | ||
|
|
e0ef3ca39a | ||
|
|
aef29ff6c5 | ||
|
|
ec7bd8c1c8 | ||
|
|
fe0d69739d | ||
|
|
448e3a4639 | ||
|
|
2e74d6f652 | ||
|
|
237108a6d1 | ||
|
|
ed3a222ed4 | ||
|
|
8a696f6c52 | ||
|
|
184aa4fd20 | ||
|
|
ce14b9e43e | ||
|
|
1cdedb89f1 | ||
|
|
18efb098f9 | ||
|
|
fb3a27c02f | ||
|
|
6d41bcd511 | ||
|
|
e6b981fc38 | ||
|
|
74a3ea2a01 | ||
|
|
4bfb7a5d29 | ||
|
|
55ba040e3d | ||
|
|
e4e78e2083 | ||
|
|
82f25cc2e5 | ||
|
|
7b67c9aa8d | ||
|
|
854f3b5257 | ||
|
|
e37bc99c36 | ||
|
|
52121b90ff | ||
|
|
3eb9de57ef | ||
|
|
4ed501f4d4 | ||
|
|
0bb8ae540d | ||
|
|
963957f7a6 | ||
|
|
93a152068d | ||
|
|
bb318aefd2 | ||
|
|
0cafef7ab4 | ||
|
|
055e54966d | ||
|
|
c58254f951 | ||
|
|
ce710134ac | ||
|
|
fa68bdc82b | ||
|
|
cd9f2ab232 | ||
|
|
91136fe48f | ||
|
|
40cc0f721d | ||
|
|
72567c43e6 | ||
|
|
db82d7a6b3 | ||
|
|
c469a61908 | ||
|
|
1c4bcc5697 | ||
|
|
0f3b0309e8 | ||
|
|
5a393de1cb | ||
|
|
1617a61b33 | ||
|
|
ccd18c4f10 | ||
|
|
e287f5774a | ||
|
|
2dec3195ac | ||
|
|
2bea6694c7 | ||
|
|
38c468a204 | ||
|
|
6a80258163 | ||
|
|
55fe2600b7 | ||
|
|
cad4e183c5 | ||
|
|
ba447d2b00 | ||
|
|
fa5e32c46b | ||
|
|
e51b7c22a6 | ||
|
|
0473cdc5e1 | ||
|
|
36c65e8b5f | ||
|
|
d0e81b7778 | ||
|
|
7df63c87c9 | ||
|
|
0021f7b4ba | ||
|
|
540283970a | ||
|
|
5a2b9b60e6 | ||
|
|
12e09269d3 | ||
|
|
bcad5eda81 | ||
|
|
09ad56056e | ||
|
|
0c8dc63085 | ||
|
|
68e3bcba05 | ||
|
|
75a6111a9b | ||
|
|
1d9f970169 | ||
|
|
7cc328ed3b | ||
|
|
9a1c88c80d | ||
|
|
b1446438fb | ||
|
|
6ba1d71a81 | ||
|
|
a4f885a2c5 | ||
|
|
a8a3c63f64 | ||
|
|
ace7b4e2af | ||
|
|
a2885a90b7 | ||
|
|
4457487e2a | ||
|
|
830bb5f70d | ||
|
|
9106f9676c | ||
|
|
df1690515c | ||
|
|
8e287da7b5 | ||
|
|
128ebe2cb2 | ||
|
|
404964dfde | ||
|
|
7f74e79253 | ||
|
|
11b5cc83dc | ||
|
|
bda9d1f8e2 | ||
|
|
32efeee4c7 | ||
|
|
8adbd90cc3 | ||
|
|
074fcaaa73 | ||
|
|
d2209c8ead | ||
|
|
31f1079dfa | ||
|
|
2b87c83fa7 | ||
|
|
1c31c1947c | ||
|
|
50a8a83408 | ||
|
|
020fa4eefa | ||
|
|
64cf02bc45 | ||
|
|
06bd964adc | ||
|
|
2218b7b853 | ||
|
|
f6c45cd85a | ||
|
|
cf4907011b | ||
|
|
dea85add5d | ||
|
|
aa3325ef35 | ||
|
|
8059ba7306 | ||
|
|
c346c4178d | ||
|
|
f2555563a8 | ||
|
|
b948961d52 | ||
|
|
43987d844f | ||
|
|
cc3b14dbf8 | ||
|
|
3cc2d3260e | ||
|
|
be155f8c6c | ||
|
|
74952cf62d | ||
|
|
4a71df7859 | ||
|
|
7d7ea4c34b | ||
|
|
026d22f7f9 | ||
|
|
babd351151 | ||
|
|
d11894fd74 | ||
|
|
6072eab01d | ||
|
|
64e4c77428 | ||
|
|
48b70d0254 | ||
|
|
db2015f826 | ||
|
|
f893cf268b | ||
|
|
ad12d42b35 | ||
|
|
63a0c7c10b | ||
|
|
99d4d5ed9a | ||
|
|
7910c28be7 | ||
|
|
32e1ade388 | ||
|
|
a8aac7e1b4 | ||
|
|
7404aaf4d2 | ||
|
|
2f25bd476c | ||
|
|
681e4ae7fc | ||
|
|
e1ed0d2b09 | ||
|
|
32567a1c66 | ||
|
|
25753af331 | ||
|
|
6babcf9536 | ||
|
|
9a05f659f1 | ||
|
|
ca58bf661d | ||
|
|
62695d4d9a | ||
|
|
d723ef0a13 | ||
|
|
18160818d5 | ||
|
|
83bfaabd95 | ||
|
|
e23ac8496e | ||
|
|
2b7d05df6b | ||
|
|
606cb249e8 | ||
|
|
0ae739b437 | ||
|
|
f18e57b50e | ||
|
|
4c92c582d7 | ||
|
|
7b1bfeaca1 | ||
|
|
dd1d0d1cb8 | ||
|
|
995bd4953b | ||
|
|
32b61ac730 | ||
|
|
19bd54f425 | ||
|
|
76680777ff | ||
|
|
75b9a8a826 | ||
|
|
f439541844 | ||
|
|
65fb21a062 | ||
|
|
aa5df3c309 | ||
|
|
788394a1c8 | ||
|
|
7286c2f603 | ||
|
|
aff230ec7e | ||
|
|
3d538d0566 | ||
|
|
a1b287bd9d | ||
|
|
1d187f525d | ||
|
|
6c9ab0c019 | ||
|
|
e7157a8528 | ||
|
|
e2182344a2 | ||
|
|
9ebd882c3f | ||
|
|
f017b6b92f | ||
|
|
1c24c4e776 | ||
|
|
758e94a8ec | ||
|
|
9a7199bf7f | ||
|
|
1c691fa2c2 | ||
|
|
b435f49611 | ||
|
|
bcd7b2552e | ||
|
|
e76126fdd9 | ||
|
|
3fe55e7395 | ||
|
|
98e11c16fa | ||
|
|
0c5b2d302b | ||
|
|
0d526a2c4f | ||
|
|
0a8d49c233 | ||
|
|
4618d3e38a | ||
|
|
5521785b87 | ||
|
|
f12b404d58 | ||
|
|
6924e3a7d7 | ||
|
|
6fbe459903 | ||
|
|
0a245e5d65 | ||
|
|
2ed593c5e0 | ||
|
|
ce934b8998 | ||
|
|
aac4d3eefb | ||
|
|
176dc17c70 | ||
|
|
553bbda769 | ||
|
|
d4fd2d9ae0 | ||
|
|
c20138c9fc | ||
|
|
7f1ff11a4c | ||
|
|
ed9121a06f | ||
|
|
c110611d82 | ||
|
|
cc222a6f4a | ||
|
|
8293709f34 | ||
|
|
707740b7f1 | ||
|
|
98de661868 | ||
|
|
64df057e2f | ||
|
|
9d9c5cff75 | ||
|
|
3ed6f360da | ||
|
|
9f07f6441a | ||
|
|
fc0b326443 | ||
|
|
4ac0483ec6 | ||
|
|
724e78d4d5 | ||
|
|
6efa0490ea | ||
|
|
99e032eeb7 | ||
|
|
c80c212a54 | ||
|
|
95460038cf | ||
|
|
586e3b891c | ||
|
|
4402645b37 | ||
|
|
42624246bd | ||
|
|
ddd39ea0c9 | ||
|
|
a95cde372c | ||
|
|
dcbf03c355 | ||
|
|
ce44fb29c9 | ||
|
|
35f5df63e2 | ||
|
|
60c9200c06 | ||
|
|
25dacccb3b | ||
|
|
c0d08d966a | ||
|
|
cdfae2e8cb | ||
|
|
a3b0c351d0 | ||
|
|
92719d095a | ||
|
|
b2dc397fb0 | ||
|
|
55e09529c1 | ||
|
|
aed91d42bf | ||
|
|
eef58f4da0 | ||
|
|
4c427116a8 | ||
|
|
00e9624670 | ||
|
|
b52878fc97 | ||
|
|
68d9984d64 | ||
|
|
c4d293143d | ||
|
|
b08d91a218 | ||
|
|
1c586d8811 | ||
|
|
f5e13c25a7 | ||
|
|
6e267b8825 | ||
|
|
6cdfe70ac9 | ||
|
|
e8a0c693c9 | ||
|
|
ac7db87592 | ||
|
|
3d37a9eb8b | ||
|
|
8c5613b182 | ||
|
|
9e068335b6 | ||
|
|
19ed98f265 | ||
|
|
2aff36a1c0 | ||
|
|
eac7fccbb4 | ||
|
|
7562eaeb5f | ||
|
|
7b38683985 | ||
|
|
27763afbbe | ||
|
|
7a2b6f63c9 | ||
|
|
2ad8413c6d | ||
|
|
956c9000b8 | ||
|
|
838c2994a9 | ||
|
|
9974095659 | ||
|
|
85e289fd4e | ||
|
|
fa17e7019a | ||
|
|
edcb24068a | ||
|
|
38ead4909e | ||
|
|
06b2eb7662 | ||
|
|
5685f2699b | ||
|
|
c6916fd71e | ||
|
|
df64abaf9c | ||
|
|
6ed717556a | ||
|
|
e8c64074f3 | ||
|
|
4af13058f0 | ||
|
|
f3f223fa22 | ||
|
|
e244b4b263 | ||
|
|
ab09810bef | ||
|
|
fbcde465ad | ||
|
|
0f0d168976 | ||
|
|
33d15ca66d | ||
|
|
4fad97e8b1 | ||
|
|
81071771d9 | ||
|
|
4db3bafeda | ||
|
|
29aff298b8 | ||
|
|
4271289db0 | ||
|
|
9818e7c101 | ||
|
|
121a865bb8 | ||
|
|
7af340de85 | ||
|
|
cdc0dee6f5 | ||
|
|
4cbefff8b4 | ||
|
|
acc562bd7f | ||
|
|
44b87dca2a | ||
|
|
be98f786b3 | ||
|
|
6f215cbff9 | ||
|
|
96a29e91e8 | ||
|
|
e01a1a456c | ||
|
|
2561a6e00e | ||
|
|
e0bee0e0db | ||
|
|
7910d006ca | ||
|
|
778a651f7b | ||
|
|
2267dcb768 | ||
|
|
21e7c78744 | ||
|
|
54418957b6 | ||
|
|
77ce31128c | ||
|
|
79ce990644 | ||
|
|
2262d44638 | ||
|
|
972601caf6 | ||
|
|
114ebac21b | ||
|
|
57a86cd836 | ||
|
|
a741b1b9ac | ||
|
|
5d64ea48ba | ||
|
|
db6c65c7e8 | ||
|
|
b68fc6187f | ||
|
|
766a190015 | ||
|
|
2c17551d50 | ||
|
|
739bc361bd | ||
|
|
100fc20928 | ||
|
|
f42e79f5cf | ||
|
|
04da989108 | ||
|
|
11017dd8dc | ||
|
|
8d0aa65ab2 | ||
|
|
8ffb9e6f11 | ||
|
|
b0487798c2 | ||
|
|
740b89258d | ||
|
|
9026921214 | ||
|
|
df2aac3946 | ||
|
|
829e004ee5 | ||
|
|
072384eede | ||
|
|
dcc8043cf6 | ||
|
|
62c9484208 | ||
|
|
ebadccbe25 | ||
|
|
8013716158 | ||
|
|
ed7904b673 | ||
|
|
b2e3d33c80 | ||
|
|
7d1b4d916e | ||
|
|
0fb45d4584 | ||
|
|
0008919631 | ||
|
|
6bf8d19775 | ||
|
|
2e3ef87c42 | ||
|
|
abdd5cf19b | ||
|
|
8a636736b4 | ||
|
|
8fe2529306 | ||
|
|
6db217387f | ||
|
|
d33172df11 | ||
|
|
206a83c935 | ||
|
|
6a0935960b | ||
|
|
3665a792d8 | ||
|
|
90a58a1f37 | ||
|
|
f81eaf62ff | ||
|
|
8567f9b19f | ||
|
|
e33fc1a89b | ||
|
|
872ec90654 | ||
|
|
921f5524e7 | ||
|
|
a65959de3f | ||
|
|
bc095d087c | ||
|
|
3db744269e | ||
|
|
701e2f819b | ||
|
|
88bf569752 | ||
|
|
7283f48b36 | ||
|
|
f677b63c5c | ||
|
|
b2e4172c12 | ||
|
|
e0bec82a1c | ||
|
|
6cf117b3d0 | ||
|
|
6bcf47fc4b | ||
|
|
6d968bbbd8 | ||
|
|
8b803faf72 | ||
|
|
d9bcb16d9e | ||
|
|
ead9f0b3c6 | ||
|
|
6168621a36 | ||
|
|
d48c257b19 | ||
|
|
d723bbe3b7 | ||
|
|
f68e9c7681 | ||
|
|
f7a59edf19 | ||
|
|
a4741e28c5 | ||
|
|
c48ca99817 | ||
|
|
2b6965f801 | ||
|
|
caec4f80bb | ||
|
|
9aa7672b17 | ||
|
|
8e03cf9fc8 | ||
|
|
8d9a050ad6 | ||
|
|
266e08817e | ||
|
|
6c3526d47e | ||
|
|
19adfd5116 | ||
|
|
485f7fdcef | ||
|
|
856afcf3bf | ||
|
|
934b5e344f | ||
|
|
82eb8f14aa | ||
|
|
caa9cc213d | ||
|
|
403325d9dd | ||
|
|
62b3ca2fb9 | ||
|
|
e6034223f8 | ||
|
|
da02bea945 | ||
|
|
dd308d9276 | ||
|
|
fce72cad55 | ||
|
|
901b23e5ae | ||
|
|
df4d4d36bc | ||
|
|
408c56bfcf | ||
|
|
ea75ddfa85 | ||
|
|
bda5619b97 | ||
|
|
f6fb3cc766 | ||
|
|
7c3cb118a4 | ||
|
|
d03565ad95 | ||
|
|
c0f5746d1d | ||
|
|
a686bec025 | ||
|
|
c0d36bb85e | ||
|
|
e6df8dcd2e | ||
|
|
6f3fd99130 | ||
|
|
b5f7106780 | ||
|
|
e2f6dcab01 | ||
|
|
ff2f218bf0 | ||
|
|
902ed6e287 | ||
|
|
a40b49f2ed | ||
|
|
fc3b55b135 | ||
|
|
07bd5c633e | ||
|
|
f70dc14340 | ||
|
|
3d49ab2ae0 | ||
|
|
a133c39167 | ||
|
|
c9c8e20511 | ||
|
|
509ff7e833 | ||
|
|
a7a5fdb7f5 | ||
|
|
cf9ef281ce | ||
|
|
6345335be2 | ||
|
|
a008ec3729 | ||
|
|
e7a48f3909 | ||
|
|
718553373f | ||
|
|
310772d7fe | ||
|
|
213e6fab57 | ||
|
|
b7dbed5cd4 | ||
|
|
b621f2c24d | ||
|
|
21b9b090f9 | ||
|
|
709121b07f | ||
|
|
bb02a17e44 | ||
|
|
fd40c58b9c | ||
|
|
b7a6f1249d | ||
|
|
71125f07cc | ||
|
|
8500f5b437 | ||
|
|
eb25d9d7af | ||
|
|
033c85fc4b | ||
|
|
1663731489 | ||
|
|
1f65d47811 | ||
|
|
897cba3d07 | ||
|
|
f0e2247f06 | ||
|
|
1470ec60db | ||
|
|
7c04792777 | ||
|
|
6e750a2af0 | ||
|
|
7640dab380 | ||
|
|
6c10908cb7 | ||
|
|
abd235f332 | ||
|
|
4a638e82cc | ||
|
|
156d39490e | ||
|
|
a39968868f | ||
|
|
e0587ad17b | ||
|
|
06106f3831 | ||
|
|
1e332ed075 | ||
|
|
cef56cb5f1 | ||
|
|
97762712e6 | ||
|
|
4575996995 | ||
|
|
9a4594227e | ||
|
|
399bec62c2 | ||
|
|
177d015fc6 | ||
|
|
afd62ceeeb | ||
|
|
44c088c96d | ||
|
|
cf65347cd8 | ||
|
|
9fe54c5d21 | ||
|
|
0210ef2764 | ||
|
|
c66982c9bc | ||
|
|
a88be30c3f | ||
|
|
155cd31ced | ||
|
|
52dc77a00f | ||
|
|
c5de56790a | ||
|
|
d97cb4c360 | ||
|
|
8e499d164a | ||
|
|
764e1ac35f | ||
|
|
69e4fcfc76 | ||
|
|
951071e7ba | ||
|
|
5e82700871 | ||
|
|
4c706a213a | ||
|
|
d0da1f43a9 | ||
|
|
4afc439169 | ||
|
|
e47690dd52 | ||
|
|
7859aa0ce8 | ||
|
|
710c192e39 | ||
|
|
50e179f7a8 | ||
|
|
980a9d0640 | ||
|
|
fdfbf54808 | ||
|
|
fe8b0d99e5 | ||
|
|
7e817a5808 | ||
|
|
23b68bd9f9 | ||
|
|
0a8bbc7d3a | ||
|
|
0f5200aaf8 | ||
|
|
82d7b9cf05 | ||
|
|
0f5fafac8d | ||
|
|
7fcfffba6f | ||
|
|
13e9ef4c34 | ||
|
|
ea04c7d5eb | ||
|
|
8bf29528e3 | ||
|
|
c6f65debcf | ||
|
|
370b39a139 | ||
|
|
7b7e64576f | ||
|
|
d8b717a879 | ||
|
|
3967017ebb | ||
|
|
d93862043a | ||
|
|
719276b32a | ||
|
|
bf13f06e68 | ||
|
|
9f770d08f5 | ||
|
|
e62c529c1f | ||
|
|
4e1fc9b031 | ||
|
|
7d175ad38d | ||
|
|
de2c56560e | ||
|
|
0db413cb18 | ||
|
|
7c6424e05c | ||
|
|
3c328a00b5 | ||
|
|
e36f13c595 | ||
|
|
38a5cc33e8 | ||
|
|
245a3a73d8 | ||
|
|
d57d7ec4d8 | ||
|
|
09a4e4a6a5 | ||
|
|
d9beb4b660 | ||
|
|
63507a5567 | ||
|
|
e42dc259d8 | ||
|
|
f59f8d1937 | ||
|
|
0387bde81b | ||
|
|
b8fe95b945 | ||
|
|
792a1652e3 | ||
|
|
4816bfa26d | ||
|
|
95d1680ac3 | ||
|
|
5e1e6aea16 | ||
|
|
00696943d1 | ||
|
|
c1d293f9f9 | ||
|
|
d4d61d10bc | ||
|
|
c09e5e6552 | ||
|
|
ab08732ba8 | ||
|
|
584a7eb9e2 | ||
|
|
d44b04f425 | ||
|
|
eeec04b21a | ||
|
|
0642bc28bc | ||
|
|
2b12b67582 | ||
|
|
d9b575b051 | ||
|
|
c6dbb41724 | ||
|
|
ea4c1bf5e5 | ||
|
|
67afc050b8 | ||
|
|
6e90da90f1 | ||
|
|
26872c829b | ||
|
|
34f32c8ba5 | ||
|
|
b0d847dc18 | ||
|
|
adabef9706 | ||
|
|
474adf2d53 | ||
|
|
4f23ad37f5 | ||
|
|
eb6b160377 | ||
|
|
0770e00d8d | ||
|
|
ab0e95177e | ||
|
|
cfd9e4b256 | ||
|
|
bfed0ed791 | ||
|
|
3cec9f7ee0 | ||
|
|
3a5dc62908 | ||
|
|
e0cdfcf403 | ||
|
|
439866216c | ||
|
|
a1b0731665 | ||
|
|
bcc216fd2e | ||
|
|
574d848828 | ||
|
|
22acc2307c | ||
|
|
d01a3c327e | ||
|
|
7c52b6ab23 | ||
|
|
23d27aee6b | ||
|
|
9315358fa6 | ||
|
|
83aaa94cfe | ||
|
|
7744099ee5 | ||
|
|
35bb6b62d8 | ||
|
|
99844931f4 | ||
|
|
d8c34a7cdf | ||
|
|
c284edcd81 | ||
|
|
f5d4b352b3 | ||
|
|
e44b31d755 | ||
|
|
3a28068b48 | ||
|
|
03433b00d2 | ||
|
|
6f39ebf48f | ||
|
|
949ca00dbb | ||
|
|
bfdf1ee8f5 | ||
|
|
e047d4e8a6 | ||
|
|
15cf2069b6 | ||
|
|
564b0ee0c4 | ||
|
|
b454932ff5 | ||
|
|
191c07b6a1 | ||
|
|
5b0deb9fc2 | ||
|
|
297ec64bde | ||
|
|
a9b85caeb7 | ||
|
|
e30b883148 | ||
|
|
cd6be0d755 | ||
|
|
95b74c5f2f | ||
|
|
45f26bb22e | ||
|
|
09b0e09932 | ||
|
|
ea9d88009f | ||
|
|
e5013c918e | ||
|
|
4b45103a4e | ||
|
|
1ea28411da | ||
|
|
e27fce1227 | ||
|
|
cf3b8378bd | ||
|
|
9f07532140 | ||
|
|
f1aedc619e | ||
|
|
5f778e706f | ||
|
|
ee15663679 | ||
|
|
c271d4bfe4 | ||
|
|
99188f7427 | ||
|
|
88a07ecf63 | ||
|
|
f1af2e35cd | ||
|
|
352e20f01b | ||
|
|
f7d10a6cf1 | ||
|
|
6a5808077c | ||
|
|
09be11b5a8 | ||
|
|
5117c1a528 | ||
|
|
ead9857203 | ||
|
|
cd3493e040 | ||
|
|
02c22c1894 | ||
|
|
8b41a03080 | ||
|
|
306f4f119b | ||
|
|
3dc28c7291 | ||
|
|
f2f2215059 | ||
|
|
f75179b2f6 | ||
|
|
3ead35c984 | ||
|
|
efe20a1fe8 | ||
|
|
ab1e56a14e | ||
|
|
055d6bf601 | ||
|
|
683c3e96d6 | ||
|
|
d8e414a4a2 | ||
|
|
9237d68b79 | ||
|
|
12a14fbe29 | ||
|
|
22e4e4efc1 | ||
|
|
588327fd68 | ||
|
|
d35ef2d360 | ||
|
|
5ce5193430 | ||
|
|
6e74215b2e | ||
|
|
884a9835c4 | ||
|
|
233f40f3e9 | ||
|
|
9b24e61033 | ||
|
|
286928d59e | ||
|
|
47cee5f6a1 | ||
|
|
6820b748f7 | ||
|
|
42422845b0 | ||
|
|
8ed11cdc07 | ||
|
|
006cd1ee89 | ||
|
|
49f7ee6042 | ||
|
|
9612e4d4e9 | ||
|
|
4b938db0c1 | ||
|
|
b0a079dce9 | ||
|
|
52069f35c5 | ||
|
|
140535d875 | ||
|
|
dd2a698147 | ||
|
|
cc65555c3d | ||
|
|
63e3923349 |
@@ -1,2 +0,0 @@
|
|||||||
[config]
|
|
||||||
project = Oqtane.Server/Oqtane.Server.csproj
|
|
||||||
26
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: Create a bug report to help us improve the product
|
||||||
|
title: "[BUG] "
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Oqtane Info
|
||||||
|
|
||||||
|
Version - #.#.#
|
||||||
|
Render Mode - Static
|
||||||
|
Interactivity - Server
|
||||||
|
Database - SQL Server
|
||||||
|
|
||||||
|
### Describe the bug
|
||||||
|
|
||||||
|
|
||||||
|
### Expected Behavior
|
||||||
|
|
||||||
|
|
||||||
|
### Steps To Reproduce
|
||||||
|
|
||||||
|
|
||||||
|
### Anything else?
|
||||||
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Enhancement Request
|
||||||
|
about: 'Suggest a product enhancement '
|
||||||
|
title: "[ENH] "
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Oqtane Info
|
||||||
|
|
||||||
|
Version - #.#.#
|
||||||
|
Render Mode - Static
|
||||||
|
Interactivity - Server
|
||||||
|
Database - SQL Server
|
||||||
|
|
||||||
|
### Describe the enhancement
|
||||||
|
|
||||||
|
|
||||||
|
### Anything else?
|
||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -22,10 +22,17 @@ Oqtane.Server/Packages
|
|||||||
Oqtane.Server/wwwroot/Content
|
Oqtane.Server/wwwroot/Content
|
||||||
Oqtane.Server/wwwroot/Packages/*.log
|
Oqtane.Server/wwwroot/Packages/*.log
|
||||||
|
|
||||||
Oqtane.Server/wwwroot/Modules
|
Oqtane.Server/wwwroot/_content/*
|
||||||
|
!Oqtane.Server/wwwroot/_content/Placeholder.txt
|
||||||
|
|
||||||
|
Oqtane.Server/wwwroot/Modules/*
|
||||||
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
|
||||||
!Oqtane.Server/wwwroot/Modules/Templates
|
!Oqtane.Server/wwwroot/Modules/Templates
|
||||||
|
Oqtane.Server/wwwroot/Modules/Templates/*
|
||||||
|
!Oqtane.Server/wwwroot/Modules/Templates/External
|
||||||
|
|
||||||
Oqtane.Server/wwwroot/Themes
|
Oqtane.Server/wwwroot/Themes/*
|
||||||
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
|
||||||
!Oqtane.Server/wwwroot/Themes/Templates
|
!Oqtane.Server/wwwroot/Themes/Templates
|
||||||
|
Oqtane.Server/wwwroot/Themes/Templates/*
|
||||||
|
!Oqtane.Server/wwwroot/Themes/Templates/External
|
||||||
|
|||||||
24
CONTRIBUTING.md
Normal file
24
CONTRIBUTING.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Contributing to Oqtane
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
We track all of our issues on Github. If you want to contribute, everything starts with an issue. If you don't have an issue yet, you can add one. Then a core contributor will tag it as either an enhancement [ENH] or a bug [BUG]. Tagged issues are open for contribution.
|
||||||
|
|
||||||
|
## Use GitHub-flow process
|
||||||
|
- Make a comment on the issue that you intend to work on it and read all the comments to gain a full understanding.
|
||||||
|
- Fork the repository
|
||||||
|
- Create a new branch and update your comment on the issue with a llink to the branch
|
||||||
|
- Make your changes and commit them
|
||||||
|
- Push to the branch
|
||||||
|
- Create a pull request
|
||||||
|
|
||||||
|
## Reporting Bugs
|
||||||
|
|
||||||
|
- Check if the issue has already been reported.
|
||||||
|
- Open a new issue if it hasn’t been reported.
|
||||||
|
|
||||||
|
## Requesting Features
|
||||||
|
|
||||||
|
- Use the feature request template in the Issues tab.
|
||||||
|
|
||||||
|
Thank you for contributing!
|
||||||
17
Directory.Build.props
Normal file
17
Directory.Build.props
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Configurations>Debug;Release</Configurations>
|
||||||
|
<Version>10.0.3</Version>
|
||||||
|
<Product>Oqtane</Product>
|
||||||
|
<Authors>Shaun Walker</Authors>
|
||||||
|
<Company>.NET Foundation</Company>
|
||||||
|
<Description>CMS and Application Framework for Blazor and .NET MAUI</Description>
|
||||||
|
<Copyright>.NET Foundation</Copyright>
|
||||||
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v10.0.3</PackageReleaseNotes>
|
||||||
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
|
<RepositoryType>Git</RepositoryType>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2023 .NET Foundation
|
Copyright (c) 2018-2025 .NET Foundation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
9
Oqtane.Application/.gitignore
vendored
Normal file
9
Oqtane.Application/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.vs/
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
*.user
|
||||||
|
artifacts/
|
||||||
|
msbuild.binlog
|
||||||
|
.vscode/
|
||||||
|
*.binlog
|
||||||
|
*.nupkg
|
||||||
81
Oqtane.Application/.template.config/template.json
Normal file
81
Oqtane.Application/.template.config/template.json
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/template",
|
||||||
|
"author": "Shaun Walker",
|
||||||
|
"classifications": [
|
||||||
|
"Web",
|
||||||
|
"ASP.NET",
|
||||||
|
"Blazor",
|
||||||
|
"Oqtane"
|
||||||
|
],
|
||||||
|
"name": "Oqtane Application Template",
|
||||||
|
"shortName": "oqtane-app",
|
||||||
|
"defaultName": "MyCompany.MyProject",
|
||||||
|
"identity": "Oqtane.Application.Template",
|
||||||
|
"tags": {
|
||||||
|
"language": "C#",
|
||||||
|
"type": "solution",
|
||||||
|
"editorTreatAs":"solution"
|
||||||
|
},
|
||||||
|
"sourceName": "Oqtane.Application",
|
||||||
|
"preferNameDirectory": true,
|
||||||
|
"symbols": {
|
||||||
|
"Framework": {
|
||||||
|
"type": "parameter",
|
||||||
|
"description": "The target framework for the project",
|
||||||
|
"datatype": "choice",
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"choice": "net10.0",
|
||||||
|
"description": "Target net10.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"replaces": "net10.0",
|
||||||
|
"defaultValue": "net10.0"
|
||||||
|
},
|
||||||
|
"HttpPort": {
|
||||||
|
"type": "parameter",
|
||||||
|
"datatype": "integer",
|
||||||
|
"description": "Port number to use for the HTTP endpoint in launchSettings.json."
|
||||||
|
},
|
||||||
|
"HttpPortGenerated": {
|
||||||
|
"type": "generated",
|
||||||
|
"generator": "port"
|
||||||
|
},
|
||||||
|
"HttpPortReplacer": {
|
||||||
|
"type": "generated",
|
||||||
|
"generator": "coalesce",
|
||||||
|
"parameters": {
|
||||||
|
"sourceVariableName": "HttpPort",
|
||||||
|
"fallbackVariableName": "HttpPortGenerated"
|
||||||
|
},
|
||||||
|
"replaces": "44358"
|
||||||
|
},
|
||||||
|
"HttpsPort": {
|
||||||
|
"type": "parameter",
|
||||||
|
"datatype": "integer",
|
||||||
|
"description": "Port number to use for the HTTPS endpoint in launchSettings.json."
|
||||||
|
},
|
||||||
|
"HttpsPortGenerated": {
|
||||||
|
"type": "generated",
|
||||||
|
"generator": "port",
|
||||||
|
"parameters": {
|
||||||
|
"low": 44300,
|
||||||
|
"high": 44399
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"HttpsPortReplacer": {
|
||||||
|
"type": "generated",
|
||||||
|
"generator": "coalesce",
|
||||||
|
"parameters": {
|
||||||
|
"sourceVariableName": "HttpsPort",
|
||||||
|
"fallbackVariableName": "HttpsPortGenerated"
|
||||||
|
},
|
||||||
|
"replaces": "44359"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"primaryOutputs": [
|
||||||
|
{
|
||||||
|
"path": "Oqtane.Application.slnx"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
Oqtane.Application/Client/AssemblyInfo.cs
Normal file
3
Oqtane.Application/Client/AssemblyInfo.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
[assembly: RootNamespace("Oqtane.Application.Client")]
|
||||||
29
Oqtane.Application/Client/Oqtane.Application.Client.csproj
Normal file
29
Oqtane.Application/Client/Oqtane.Application.Client.csproj
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<AssemblyName>Oqtane.Application.Client.Oqtane</AssemblyName>
|
||||||
|
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||||
|
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||||
|
<PublishTrimmed>false</PublishTrimmed>
|
||||||
|
<BlazorEnableCompression>false</BlazorEnableCompression>
|
||||||
|
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Oqtane.Client" Version="10.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
13
Oqtane.Application/Client/Program.cs
Normal file
13
Oqtane.Application/Client/Program.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Oqtane.Application.Client
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
// defer client startup to Oqtane - do not modify
|
||||||
|
await Oqtane.Client.Program.Main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Oqtane.Application/Client/_Imports.razor
Normal file
25
Oqtane.Application/Client/_Imports.razor
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@using System
|
||||||
|
@using System.Linq
|
||||||
|
@using System.Collections.Generic
|
||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using Microsoft.Extensions.Localization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
|
||||||
|
@using Oqtane
|
||||||
|
@using Oqtane.Models
|
||||||
|
@using Oqtane.Modules
|
||||||
|
@using Oqtane.Modules.Controls
|
||||||
|
@using Oqtane.Providers
|
||||||
|
@using Oqtane.Security
|
||||||
|
@using Oqtane.Services
|
||||||
|
@using Oqtane.Shared
|
||||||
|
@using Oqtane.Themes
|
||||||
|
@using Oqtane.Themes.Controls
|
||||||
|
@using Oqtane.UI
|
||||||
|
@using Oqtane.Enums
|
||||||
|
@using Oqtane.Interfaces
|
||||||
21
Oqtane.Application/Oqtane.Application.Template.nuspec
Normal file
21
Oqtane.Application/Oqtane.Application.Template.nuspec
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
|
<metadata>
|
||||||
|
<id>Oqtane.Application.Template</id>
|
||||||
|
<version>10.0.3</version>
|
||||||
|
<title>Oqtane Application Template For Blazor</title>
|
||||||
|
<authors>Shaun Walker</authors>
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
<license type="expression">MIT</license>
|
||||||
|
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
|
||||||
|
<icon>icon.png</icon>
|
||||||
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
|
<description>Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).</description>
|
||||||
|
<language>en-US</language>
|
||||||
|
<tags>Web ASP.NET Blazor Oqtane Modular Multi-Tenant "Open Source" "SQL Server" MySQL PostgreSQL SQLite</tags>
|
||||||
|
<readme>README.md</readme>
|
||||||
|
<packageTypes>
|
||||||
|
<packageType name="Template" />
|
||||||
|
</packageTypes>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
||||||
5
Oqtane.Application/Oqtane.Application.slnx
Normal file
5
Oqtane.Application/Oqtane.Application.slnx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="Server\Oqtane.Application.Server.csproj" DefaultStartup="true" />
|
||||||
|
<Project Path="Client\Oqtane.Application.Client.csproj" />
|
||||||
|
<Project Path="Shared\Oqtane.Application.Shared.csproj" />
|
||||||
|
</Solution>
|
||||||
22
Oqtane.Application/README.md
Normal file
22
Oqtane.Application/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Oqtane Application Template
|
||||||
|
|
||||||
|
This is a Visual Studio Project Template designed for Oqtane development projects. This template relies on the native templating capabilities of the .NET Command Line Interface (CLI):
|
||||||
|
|
||||||
|
```
|
||||||
|
dotnet new install Oqtane.Application.Template
|
||||||
|
dotnet new oqtane-app -o MyCompany.MyProject
|
||||||
|
cd MyCompany.MyProject
|
||||||
|
dotnet build
|
||||||
|
cd Server
|
||||||
|
dotnet run
|
||||||
|
browse to Url
|
||||||
|
```
|
||||||
|
|
||||||
|
When using this approach you do not need to have a local copy of the oqtane.framework source code - you simply utilize Oqtane as a standard application dependency.
|
||||||
|
|
||||||
|
The solution also contains Client, Server, and Shared folders which is where you you would implement your custom functionality. An example module and theme are included for reference, and you can add additional modules and themes within the same projects by following the standard Oqtane folder/namespace conventions.
|
||||||
|
|
||||||
|
*Known Issues*
|
||||||
|
|
||||||
|
- do not use the term "Oqtane" or "Module" in your output name or else you will experience namespace conflicts
|
||||||
|
|
||||||
3
Oqtane.Application/Server/AssemblyInfo.cs
Normal file
3
Oqtane.Application/Server/AssemblyInfo.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
[assembly: RootNamespace("Oqtane.Application.Server")]
|
||||||
39
Oqtane.Application/Server/Oqtane.Application.Server.csproj
Normal file
39
Oqtane.Application/Server/Oqtane.Application.Server.csproj
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<AssemblyName>Oqtane.Application.Server.Oqtane</AssemblyName>
|
||||||
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
|
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
||||||
|
<CompressionEnabled>false</CompressionEnabled>
|
||||||
|
<StaticWebAssetsFingerprintContent>false</StaticWebAssetsFingerprintContent>
|
||||||
|
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||||
|
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="wwwroot\Modules\Templates\**" />
|
||||||
|
<Compile Remove="wwwroot\Themes\Templates\**" />
|
||||||
|
<Content Remove="wwwroot\Modules\Templates\**" />
|
||||||
|
<Content Remove="wwwroot\Themes\Templates\**" />
|
||||||
|
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
|
||||||
|
<EmbeddedResource Remove="wwwroot\Themes\Templates\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Client\Oqtane.Application.Client.csproj" />
|
||||||
|
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Oqtane.Server" Version="10.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
55
Oqtane.Application/Server/Program.cs
Normal file
55
Oqtane.Application/Server/Program.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Oqtane.Extensions;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Application.Server
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(builder.Environment.ContentRootPath, "Data"));
|
||||||
|
|
||||||
|
var configurationBuilder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(builder.Environment.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", true, true)
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
var configuration = configurationBuilder.Build();
|
||||||
|
|
||||||
|
builder.Services.AddOqtane(configuration, builder.Environment);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
var corsService = app.Services.GetRequiredService<ICorsService>();
|
||||||
|
var corsPolicyProvider = app.Services.GetRequiredService<ICorsPolicyProvider>();
|
||||||
|
var syncManager = app.Services.GetRequiredService<ISyncManager>();
|
||||||
|
|
||||||
|
app.UseOqtane(configuration, builder.Environment, corsService, corsPolicyProvider, syncManager);
|
||||||
|
|
||||||
|
var databaseManager = app.Services.GetService<IDatabaseManager>();
|
||||||
|
var install = databaseManager.Install();
|
||||||
|
if (!string.IsNullOrEmpty(install.Message))
|
||||||
|
{
|
||||||
|
var filelogger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
if (filelogger != null)
|
||||||
|
{
|
||||||
|
filelogger.LogError($"[Oqtane.Server.Program.Main] {install.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Oqtane.Application/Server/Properties/launchSettings.json
Normal file
25
Oqtane.Application/Server/Properties/launchSettings.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "http://localhost:44358",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "https://localhost:44359;http://localhost:44358",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Oqtane.Application/Server/appsettings.json
Normal file
63
Oqtane.Application/Server/appsettings.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"RenderMode": "Static",
|
||||||
|
"Runtime": "Server",
|
||||||
|
"Database": {
|
||||||
|
"DefaultDBType": ""
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": ""
|
||||||
|
},
|
||||||
|
"Installation": {
|
||||||
|
"DefaultAlias": "",
|
||||||
|
"HostPassword": "",
|
||||||
|
"HostEmail": "",
|
||||||
|
"SiteTemplate": "",
|
||||||
|
"DefaultTheme": "",
|
||||||
|
"DefaultContainer": ""
|
||||||
|
},
|
||||||
|
"Localization": {
|
||||||
|
"DefaultCulture": "en"
|
||||||
|
},
|
||||||
|
"AvailableDatabases": [
|
||||||
|
{
|
||||||
|
"Name": "LocalDB",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SQL Server",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SQLite",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.MySQL.MySQLDatabase, Oqtane.Server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.PostgreSQLConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.PostgreSQL.PostgreSQLDatabase, Oqtane.Server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure SQL",
|
||||||
|
"ControlType": "Oqtane.Installer.Controls.AzureSqlConfig, Oqtane.Client",
|
||||||
|
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Logging": {
|
||||||
|
"FileLogger": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
@using Oqtane.Modules.Controls
|
||||||
|
@using [Owner].Module.[Module].Services
|
||||||
|
@using [Owner].Module.[Module].Models
|
||||||
|
|
||||||
|
@namespace [Owner].Module.[Module]
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject I[Module]Service [Module]Service
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
|
|
||||||
|
<form @ref="form" class="@(validated ? " was-validated" : "needs-validation" )" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="name" HelpText="Enter a name" ResourceKey="Name">Name: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="name" class="form-control" @bind="@_name" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-success" @onclick="Save">@Localizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||||
|
<br /><br />
|
||||||
|
@if (PageState.Action == "Edit")
|
||||||
|
{
|
||||||
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
|
public override string Actions => "Add,Edit";
|
||||||
|
|
||||||
|
public override string Title => "Manage [Module]";
|
||||||
|
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
|
||||||
|
private int _id;
|
||||||
|
private string _name;
|
||||||
|
private string _createdby;
|
||||||
|
private DateTime _createdon;
|
||||||
|
private string _modifiedby;
|
||||||
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (PageState.Action == "Edit")
|
||||||
|
{
|
||||||
|
_id = Int32.Parse(PageState.QueryString["id"]);
|
||||||
|
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
|
||||||
|
if ([Module] != null)
|
||||||
|
{
|
||||||
|
_name = [Module].Name;
|
||||||
|
_createdby = [Module].CreatedBy;
|
||||||
|
_createdon = [Module].CreatedOn;
|
||||||
|
_modifiedby = [Module].ModifiedBy;
|
||||||
|
_modifiedon = [Module].ModifiedOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Oqtane.UI.Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
if (PageState.Action == "Add")
|
||||||
|
{
|
||||||
|
[Module] [Module] = new [Module]();
|
||||||
|
[Module].ModuleId = ModuleState.ModuleId;
|
||||||
|
[Module].Name = _name;
|
||||||
|
[Module] = await [Module]Service.Add[Module]Async([Module]);
|
||||||
|
await logger.LogInformation("[Module] Added {[Module]}", [Module]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
|
||||||
|
[Module].Name = _name;
|
||||||
|
await [Module]Service.Update[Module]Async([Module]);
|
||||||
|
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);
|
||||||
|
}
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.SaveValidation"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Message.SaveError"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
@using [Owner].Module.[Module].Services
|
||||||
|
@using [Owner].Module.[Module].Models
|
||||||
|
|
||||||
|
@namespace [Owner].Module.[Module]
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject I[Module]Service [Module]Service
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject IStringLocalizer<Index> Localizer
|
||||||
|
|
||||||
|
@if (_[Module]s == null)
|
||||||
|
{
|
||||||
|
<p><em>Loading...</em></p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add [Module]" ResourceKey="Add" />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
@if (@_[Module]s.Count != 0)
|
||||||
|
{
|
||||||
|
<Pager Items="@_[Module]s">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@Localizer["Name"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.[Module]Id.ToString())" ResourceKey="Edit" /></td>
|
||||||
|
<td><ActionDialog Header="Delete [Module]" Message="Are You Sure You Wish To Delete This [Module]?" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" Id="@context.[Module]Id.ToString()" /></td>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>@Localizer["Message.DisplayNone"]</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override string RenderMode => RenderModes.Static;
|
||||||
|
|
||||||
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
|
{
|
||||||
|
new Stylesheet(ModulePath() + "Module.css"),
|
||||||
|
new Script(ModulePath() + "Module.js")
|
||||||
|
};
|
||||||
|
|
||||||
|
List<[Module]> _[Module]s;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Delete([Module] [Module])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
|
||||||
|
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
|
||||||
|
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Message.DeleteError"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.JSInterop;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module]
|
||||||
|
{
|
||||||
|
public class Interop
|
||||||
|
{
|
||||||
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
|
public Interop(IJSRuntime jsRuntime)
|
||||||
|
{
|
||||||
|
_jsRuntime = jsRuntime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module]
|
||||||
|
{
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "[Module]",
|
||||||
|
Description = "[Description]",
|
||||||
|
Version = "1.0.0",
|
||||||
|
ServerManagerType = "[ServerManagerType]"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
@namespace [Owner].Module.[Module]
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="value" HelpText="Enter a value" ResourceKey="SettingName" ResourceType="@resourceType">Name: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="value" type="text" class="form-control" @bind="@_value" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string resourceType = "[Owner].Module.[Module].Settings, [Owner].Module.[Module].Client.Oqtane"; // for localization
|
||||||
|
public override string Title => "[Module] Settings";
|
||||||
|
|
||||||
|
string _value;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<string, string> settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
_value = SettingService.GetSetting(settings, "SettingName", "");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
settings = SettingService.SetSetting(settings, "SettingName", _value);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Name.Text" xml:space="preserve">
|
||||||
|
<value>Name: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Name.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter the name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Save" xml:space="preserve">
|
||||||
|
<value>Save</value>
|
||||||
|
</data>
|
||||||
|
<data name="Cancel" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.LoadError" xml:space="preserve">
|
||||||
|
<value>Error Loading [Module]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.SaveValidation" xml:space="preserve">
|
||||||
|
<value>Please Provide All Required Information</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.SaveError" xml:space="preserve">
|
||||||
|
<value>Error Saving [Module]</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Name" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add.Text" xml:space="preserve">
|
||||||
|
<value>Add [Module]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Edit.Text" xml:space="preserve">
|
||||||
|
<value>Edit</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Text" xml:space="preserve">
|
||||||
|
<value>Delete</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Header" xml:space="preserve">
|
||||||
|
<value>Delete [Module]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Message" xml:space="preserve">
|
||||||
|
<value>Are You Sure You Wish To Delete This [Module]?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.DisplayNone" xml:space="preserve">
|
||||||
|
<value>No [Module]s To Display</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.LoadError" xml:space="preserve">
|
||||||
|
<value>Error Loading [Module]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.DeleteError" xml:space="preserve">
|
||||||
|
<value>Error Deleting [Module]</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="SettingName.Text" xml:space="preserve">
|
||||||
|
<value>Name: </value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingName.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a value</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Services
|
||||||
|
{
|
||||||
|
public interface I[Module]Service
|
||||||
|
{
|
||||||
|
Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId);
|
||||||
|
|
||||||
|
Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
|
||||||
|
|
||||||
|
Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module]);
|
||||||
|
|
||||||
|
Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module]);
|
||||||
|
|
||||||
|
Task Delete[Module]Async(int [Module]Id, int ModuleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class [Module]Service : ServiceBase, I[Module]Service
|
||||||
|
{
|
||||||
|
public [Module]Service(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
|
private string Apiurl => CreateApiUrl("[Module]");
|
||||||
|
|
||||||
|
public async Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId)
|
||||||
|
{
|
||||||
|
List<Models.[Module]> [Module]s = await GetJsonAsync<List<Models.[Module]>>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", EntityNames.Module, ModuleId), Enumerable.Empty<Models.[Module]>().ToList());
|
||||||
|
return [Module]s.OrderBy(item => item.Name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module]Id}/{ModuleId}", EntityNames.Module, ModuleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
return await PostJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}", EntityNames.Module, [Module].ModuleId), [Module]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
return await PutJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", EntityNames.Module, [Module].ModuleId), [Module]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
|
||||||
|
{
|
||||||
|
await DeleteAsync(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module]Id}/{ModuleId}", EntityNames.Module, ModuleId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Linq;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using [Owner].Module.[Module].Services;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Startup
|
||||||
|
{
|
||||||
|
public class ClientStartup : IClientStartup
|
||||||
|
{
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
if (!services.Any(s => s.ServiceType == typeof(I[Module]Service)))
|
||||||
|
{
|
||||||
|
services.AddScoped<I[Module]Service, [Module]Service>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
using Oqtane.Enums;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using [Owner].Module.[Module].Services;
|
||||||
|
using Oqtane.Controllers;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Controllers
|
||||||
|
{
|
||||||
|
[Route(ControllerRoutes.ApiRoute)]
|
||||||
|
public class [Module]Controller : ModuleControllerBase
|
||||||
|
{
|
||||||
|
private readonly I[Module]Service _[Module]Service;
|
||||||
|
|
||||||
|
public [Module]Controller(I[Module]Service [Module]Service, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
|
||||||
|
{
|
||||||
|
_[Module]Service = [Module]Service;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: api/<controller>?moduleid=x
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||||
|
public async Task<IEnumerable<Models.[Module]>> Get(string moduleid)
|
||||||
|
{
|
||||||
|
int ModuleId;
|
||||||
|
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||||
|
{
|
||||||
|
return await _[Module]Service.Get[Module]sAsync(ModuleId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {ModuleId}", moduleid);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/<controller>/5
|
||||||
|
[HttpGet("{id}/{moduleid}")]
|
||||||
|
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||||
|
public async Task<Models.[Module]> Get(int id, int moduleid)
|
||||||
|
{
|
||||||
|
Models.[Module] [Module] = await _[Module]Service.Get[Module]Async(id, moduleid);
|
||||||
|
if ([Module] != null && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
|
||||||
|
{
|
||||||
|
return [Module];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {[Module]Id} {ModuleId}", id, moduleid);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/<controller>
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Policy = PolicyNames.EditModule)]
|
||||||
|
public async Task<Models.[Module]> Post([FromBody] Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
|
||||||
|
{
|
||||||
|
[Module] = await _[Module]Service.Add[Module]Async([Module]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Post Attempt {[Module]}", [Module]);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
[Module] = null;
|
||||||
|
}
|
||||||
|
return [Module];
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT api/<controller>/5
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
[Authorize(Policy = PolicyNames.EditModule)]
|
||||||
|
public async Task<Models.[Module]> Put(int id, [FromBody] Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid && [Module].[Module]Id == id && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
|
||||||
|
{
|
||||||
|
[Module] = await _[Module]Service.Update[Module]Async([Module]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Put Attempt {[Module]}", [Module]);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
[Module] = null;
|
||||||
|
}
|
||||||
|
return [Module];
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE api/<controller>/5
|
||||||
|
[HttpDelete("{id}/{moduleid}")]
|
||||||
|
[Authorize(Policy = PolicyNames.EditModule)]
|
||||||
|
public async Task Delete(int id, int moduleid)
|
||||||
|
{
|
||||||
|
Models.[Module] [Module] = await _[Module]Service.Get[Module]Async(id, moduleid);
|
||||||
|
if ([Module] != null && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
|
||||||
|
{
|
||||||
|
await _[Module]Service.Delete[Module]Async(id, [Module].ModuleId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Delete Attempt {[Module]Id} {ModuleId}", id, moduleid);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Interfaces;
|
||||||
|
using Oqtane.Enums;
|
||||||
|
using Oqtane.Repository;
|
||||||
|
using [Owner].Module.[Module].Repository;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Manager
|
||||||
|
{
|
||||||
|
public class [Module]Manager : MigratableModuleBase, IInstallable, IPortable, ISearchable
|
||||||
|
{
|
||||||
|
private readonly I[Module]Repository _[Module]Repository;
|
||||||
|
private readonly IDBContextDependencies _DBContextDependencies;
|
||||||
|
|
||||||
|
public [Module]Manager(I[Module]Repository [Module]Repository, IDBContextDependencies DBContextDependencies)
|
||||||
|
{
|
||||||
|
_[Module]Repository = [Module]Repository;
|
||||||
|
_DBContextDependencies = DBContextDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Install(Tenant tenant, string version)
|
||||||
|
{
|
||||||
|
return Migrate(new [Module]Context(_DBContextDependencies), tenant, MigrationType.Up);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Uninstall(Tenant tenant)
|
||||||
|
{
|
||||||
|
return Migrate(new [Module]Context(_DBContextDependencies), tenant, MigrationType.Down);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ExportModule(Oqtane.Models.Module module)
|
||||||
|
{
|
||||||
|
string content = "";
|
||||||
|
List<Models.[Module]> [Module]s = _[Module]Repository.Get[Module]s(module.ModuleId).ToList();
|
||||||
|
if ([Module]s != null)
|
||||||
|
{
|
||||||
|
content = JsonSerializer.Serialize([Module]s);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ImportModule(Oqtane.Models.Module module, string content, string version)
|
||||||
|
{
|
||||||
|
List<Models.[Module]> [Module]s = null;
|
||||||
|
if (!string.IsNullOrEmpty(content))
|
||||||
|
{
|
||||||
|
[Module]s = JsonSerializer.Deserialize<List<Models.[Module]>>(content);
|
||||||
|
}
|
||||||
|
if ([Module]s != null)
|
||||||
|
{
|
||||||
|
foreach(var [Module] in [Module]s)
|
||||||
|
{
|
||||||
|
_[Module]Repository.Add[Module](new Models.[Module] { ModuleId = module.ModuleId, Name = [Module].Name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<SearchContent>> GetSearchContentsAsync(PageModule pageModule, DateTime lastIndexedOn)
|
||||||
|
{
|
||||||
|
var searchContentList = new List<SearchContent>();
|
||||||
|
|
||||||
|
foreach (var [Module] in _[Module]Repository.Get[Module]s(pageModule.ModuleId))
|
||||||
|
{
|
||||||
|
if ([Module].ModifiedOn >= lastIndexedOn)
|
||||||
|
{
|
||||||
|
searchContentList.Add(new SearchContent
|
||||||
|
{
|
||||||
|
EntityName = "[Owner][Module]",
|
||||||
|
EntityId = [Module].[Module]Id.ToString(),
|
||||||
|
Title = [Module].Name,
|
||||||
|
Body = [Module].Name,
|
||||||
|
ContentModifiedBy = [Module].ModifiedBy,
|
||||||
|
ContentModifiedOn = [Module].ModifiedOn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(searchContentList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Oqtane.Databases.Interfaces;
|
||||||
|
using Oqtane.Migrations;
|
||||||
|
using [Owner].Module.[Module].Migrations.EntityBuilders;
|
||||||
|
using [Owner].Module.[Module].Repository;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof([Module]Context))]
|
||||||
|
[Migration("[Owner].Module.[Module].01.00.00.00")]
|
||||||
|
public class [Module]Initialize : MultiDatabaseMigration
|
||||||
|
{
|
||||||
|
public [Module]Initialize(IDatabase database) : base(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var entityBuilder = new [Module]EntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
entityBuilder.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var entityBuilder = new [Module]EntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
entityBuilder.Drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||||
|
using Oqtane.Databases.Interfaces;
|
||||||
|
using Oqtane.Migrations;
|
||||||
|
using Oqtane.Migrations.EntityBuilders;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Migrations.EntityBuilders
|
||||||
|
{
|
||||||
|
public class [Module]EntityBuilder : AuditableBaseEntityBuilder<[Module]EntityBuilder>
|
||||||
|
{
|
||||||
|
private const string _entityTableName = "[Owner][Module]";
|
||||||
|
private readonly PrimaryKey<[Module]EntityBuilder> _primaryKey = new("PK_[Owner][Module]", x => x.[Module]Id);
|
||||||
|
private readonly ForeignKey<[Module]EntityBuilder> _moduleForeignKey = new("FK_[Owner][Module]_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
public [Module]EntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
|
||||||
|
{
|
||||||
|
EntityTableName = _entityTableName;
|
||||||
|
PrimaryKey = _primaryKey;
|
||||||
|
ForeignKeys.Add(_moduleForeignKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override [Module]EntityBuilder BuildTable(ColumnsBuilder table)
|
||||||
|
{
|
||||||
|
[Module]Id = AddAutoIncrementColumn(table,"[Module]Id");
|
||||||
|
ModuleId = AddIntegerColumn(table,"ModuleId");
|
||||||
|
Name = AddMaxStringColumn(table,"Name");
|
||||||
|
AddAuditableColumns(table);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationBuilder<AddColumnOperation> [Module]Id { get; set; }
|
||||||
|
public OperationBuilder<AddColumnOperation> ModuleId { get; set; }
|
||||||
|
public OperationBuilder<AddColumnOperation> Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
using Oqtane.Repository;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Repository.Databases.Interfaces;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Repository
|
||||||
|
{
|
||||||
|
public class [Module]Context : DBContextBase, ITransientService, IMultiDatabase
|
||||||
|
{
|
||||||
|
public virtual DbSet<Models.[Module]> [Module] { get; set; }
|
||||||
|
|
||||||
|
public [Module]Context(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies)
|
||||||
|
{
|
||||||
|
// ContextBase handles multi-tenant database connections
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
|
builder.Entity<Models.[Module]>().ToTable(ActiveDatabase.RewriteName("[Owner][Module]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Repository
|
||||||
|
{
|
||||||
|
public interface I[Module]Repository
|
||||||
|
{
|
||||||
|
IEnumerable<Models.[Module]> Get[Module]s(int ModuleId);
|
||||||
|
Models.[Module] Get[Module](int [Module]Id);
|
||||||
|
Models.[Module] Get[Module](int [Module]Id, bool tracking);
|
||||||
|
Models.[Module] Add[Module](Models.[Module] [Module]);
|
||||||
|
Models.[Module] Update[Module](Models.[Module] [Module]);
|
||||||
|
void Delete[Module](int [Module]Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class [Module]Repository : I[Module]Repository, ITransientService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<[Module]Context> _factory;
|
||||||
|
|
||||||
|
public [Module]Repository(IDbContextFactory<[Module]Context> factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Models.[Module]> Get[Module]s(int ModuleId)
|
||||||
|
{
|
||||||
|
using var db = _factory.CreateDbContext();
|
||||||
|
return db.[Module].Where(item => item.ModuleId == ModuleId).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.[Module] Get[Module](int [Module]Id)
|
||||||
|
{
|
||||||
|
return Get[Module]([Module]Id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.[Module] Get[Module](int [Module]Id, bool tracking)
|
||||||
|
{
|
||||||
|
using var db = _factory.CreateDbContext();
|
||||||
|
if (tracking)
|
||||||
|
{
|
||||||
|
return db.[Module].Find([Module]Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return db.[Module].AsNoTracking().FirstOrDefault(item => item.[Module]Id == [Module]Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.[Module] Add[Module](Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
using var db = _factory.CreateDbContext();
|
||||||
|
db.[Module].Add([Module]);
|
||||||
|
db.SaveChanges();
|
||||||
|
return [Module];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.[Module] Update[Module](Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
using var db = _factory.CreateDbContext();
|
||||||
|
db.Entry([Module]).State = EntityState.Modified;
|
||||||
|
db.SaveChanges();
|
||||||
|
return [Module];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete[Module](int [Module]Id)
|
||||||
|
{
|
||||||
|
using var db = _factory.CreateDbContext();
|
||||||
|
Models.[Module] [Module] = db.[Module].Find([Module]Id);
|
||||||
|
db.[Module].Remove([Module]);
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Oqtane.Enums;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Security;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
using [Owner].Module.[Module].Repository;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Services
|
||||||
|
{
|
||||||
|
public class Server[Module]Service : I[Module]Service
|
||||||
|
{
|
||||||
|
private readonly I[Module]Repository _[Module]Repository;
|
||||||
|
private readonly IUserPermissions _userPermissions;
|
||||||
|
private readonly ILogManager _logger;
|
||||||
|
private readonly IHttpContextAccessor _accessor;
|
||||||
|
private readonly Alias _alias;
|
||||||
|
|
||||||
|
public Server[Module]Service(I[Module]Repository [Module]Repository, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
|
||||||
|
{
|
||||||
|
_[Module]Repository = [Module]Repository;
|
||||||
|
_userPermissions = userPermissions;
|
||||||
|
_logger = logger;
|
||||||
|
_accessor = accessor;
|
||||||
|
_alias = tenantManager.GetAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId)
|
||||||
|
{
|
||||||
|
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
|
||||||
|
{
|
||||||
|
return Task.FromResult(_[Module]Repository.Get[Module]s(ModuleId).ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {ModuleId}", ModuleId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
|
||||||
|
{
|
||||||
|
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
|
||||||
|
{
|
||||||
|
return Task.FromResult(_[Module]Repository.Get[Module]([Module]Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {[Module]Id} {ModuleId}", [Module]Id, ModuleId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, [Module].ModuleId, PermissionNames.Edit))
|
||||||
|
{
|
||||||
|
[Module] = _[Module]Repository.Add[Module]([Module]);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Add Attempt {[Module]}", [Module]);
|
||||||
|
[Module] = null;
|
||||||
|
}
|
||||||
|
return Task.FromResult([Module]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module])
|
||||||
|
{
|
||||||
|
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, [Module].ModuleId, PermissionNames.Edit))
|
||||||
|
{
|
||||||
|
[Module] = _[Module]Repository.Update[Module]([Module]);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Update Attempt {[Module]}", [Module]);
|
||||||
|
[Module] = null;
|
||||||
|
}
|
||||||
|
return Task.FromResult([Module]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Delete[Module]Async(int [Module]Id, int ModuleId)
|
||||||
|
{
|
||||||
|
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
|
||||||
|
{
|
||||||
|
_[Module]Repository.Delete[Module]([Module]Id);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", [Module]Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Delete Attempt {[Module]Id} {ModuleId}", [Module]Id, ModuleId);
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using [Owner].Module.[Module].Repository;
|
||||||
|
using [Owner].Module.[Module].Services;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Startup
|
||||||
|
{
|
||||||
|
public class [Module]ServerStartup : IServerStartup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureMvc(IMvcBuilder mvcBuilder)
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddTransient<I[Module]Service, Server[Module]Service>();
|
||||||
|
services.AddDbContextFactory<[Module]Context>(opt => { }, ServiceLifetime.Transient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Oqtane.Models;
|
||||||
|
|
||||||
|
namespace [Owner].Module.[Module].Models
|
||||||
|
{
|
||||||
|
[Table("[Owner][Module]")]
|
||||||
|
public class [Module] : ModelBase
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int [Module]Id { get; set; }
|
||||||
|
public int ModuleId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"Title": "Default Module Template",
|
||||||
|
"Type": "Internal",
|
||||||
|
"Version": "10.0.0",
|
||||||
|
"Namespace": "[Owner].Module.[Module]"
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 1.3
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">1.3</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1">this is my long string</data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
[base64 mime encoded serialized .NET Framework object]
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>1.3</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -117,16 +117,10 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="SynchronizeContent" xml:space="preserve">
|
<data name="Title.HelpText" xml:space="preserve">
|
||||||
<value>Synchronize Content</value>
|
<value>Specify If The Module Title Should Be Displayed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InsertImage" xml:space="preserve">
|
<data name="Title.Text" xml:space="preserve">
|
||||||
<value>Insert Image</value>
|
<value>Display Title</value>
|
||||||
</data>
|
|
||||||
<data name="Close" xml:space="preserve">
|
|
||||||
<value>Close</value>
|
|
||||||
</data>
|
|
||||||
<data name="Message.Require.Image" xml:space="preserve">
|
|
||||||
<value>You Must Select An Image To Insert</value>
|
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 1.3
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">1.3</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1">this is my long string</data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
[base64 mime encoded serialized .NET Framework object]
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>1.3</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Login.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Login.Text" xml:space="preserve">
|
||||||
|
<value>Show Login?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Register.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Register.Text" xml:space="preserve">
|
||||||
|
<value>Show Register?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scope.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if the settings are applicable to this page or the entire site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scope.Text" xml:space="preserve">
|
||||||
|
<value>Setting Scope:</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
@namespace [Owner].Theme.[Theme]
|
||||||
|
@inherits ContainerBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
|
||||||
|
<div class="@_classes">
|
||||||
|
@if (_title && ModuleState.Title != "-")
|
||||||
|
{
|
||||||
|
<div class="row px-4">
|
||||||
|
<div class="d-flex flex-nowrap">
|
||||||
|
<ModuleActions /><h2><ModuleTitle /></h2>
|
||||||
|
</div>
|
||||||
|
<hr class="app-rule" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ModuleActions />
|
||||||
|
}
|
||||||
|
<div class="row px-4">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<ModuleInstance />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override string Name => "[Owner] [Theme] - Container1";
|
||||||
|
|
||||||
|
private bool _title = true;
|
||||||
|
private string _classes = "container-fluid";
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
_title = bool.Parse(SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Title", "true"));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// error loading container settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
@namespace [Owner].Theme.[Theme]
|
||||||
|
@inherits ModuleBase
|
||||||
|
@implements Oqtane.Interfaces.ISettingsControl
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@attribute [OqtaneIgnore]
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="title" ResourceKey="Title" ResourceType="@resourceType" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="title" class="form-select" @bind="@_title">
|
||||||
|
<option value="true">Yes</option>
|
||||||
|
<option value="false">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string resourceType = "[Owner].Theme.[Theme].ContainerSettings, [Owner].Theme.[Theme].Client.Oqtane"; // for localization
|
||||||
|
private string _title = "true";
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
_title = SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Title", "true");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Title", _title);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
@namespace [Owner].Theme.[Theme]
|
||||||
|
@inherits ThemeBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<nav class="navbar navbar-dark bg-primary fixed-top">
|
||||||
|
<Logo /><Menu Orientation="Horizontal" />
|
||||||
|
<div class="controls ms-auto">
|
||||||
|
<div class="controls-group"><UserProfile ShowRegister="@_register" /> <Login ShowLogin="@_login" /> <ControlPanel ButtonClass="btn-outline-light" /></div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="content">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<Pane Name="@PaneNames.Admin" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Pane Name="Top Full Width" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<Pane Name="Top 100%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<Pane Name="Left 50%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<Pane Name="Right 50%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<Pane Name="Left 33%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<Pane Name="Center 33%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<Pane Name="Right 33%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Left Outer 25%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Left Inner 25%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Right Inner 25%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Right Outer 25%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Left 25%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<Pane Name="Center 50%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<Pane Name="Right 25%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<Pane Name="Left Sidebar 66%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<Pane Name="Right Sidebar 33%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<Pane Name="Left Sidebar 33%" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<Pane Name="Right Sidebar 66%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<Pane Name="Bottom 100%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Pane Name="Bottom Full Width" />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override string Name => "Theme1";
|
||||||
|
|
||||||
|
public override string Panes => PaneNames.Admin + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width";
|
||||||
|
|
||||||
|
private bool _login = true;
|
||||||
|
private bool _register = true;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = SettingService.MergeSettings(PageState.Site.Settings, PageState.Page.Settings);
|
||||||
|
_login = bool.Parse(SettingService.GetSetting(settings, GetType().Namespace + ":Login", "true"));
|
||||||
|
_register = bool.Parse(SettingService.GetSetting(settings, GetType().Namespace + ":Register", "true"));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// error loading theme settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Themes;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace [Owner].Theme.[Theme]
|
||||||
|
{
|
||||||
|
public class ThemeInfo : ITheme
|
||||||
|
{
|
||||||
|
public Oqtane.Models.Theme Theme => new Oqtane.Models.Theme
|
||||||
|
{
|
||||||
|
Name = "[Owner] [Theme]",
|
||||||
|
Version = "1.0.0",
|
||||||
|
PackageName = "[Owner].Theme.[Theme]",
|
||||||
|
ThemeSettingsType = "[Owner].Theme.[Theme].ThemeSettings, [Owner].Theme.[Theme].Client.Oqtane",
|
||||||
|
ContainerSettingsType = "[Owner].Theme.[Theme].ContainerSettings, [Owner].Theme.[Theme].Client.Oqtane",
|
||||||
|
Resources = new List<Resource>()
|
||||||
|
{
|
||||||
|
// obtained from https://cdnjs.com/libraries
|
||||||
|
new Stylesheet(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
|
||||||
|
new Stylesheet("~/Theme.css"),
|
||||||
|
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
@namespace [Owner].Theme.[Theme]
|
||||||
|
@inherits ModuleBase
|
||||||
|
@implements Oqtane.Interfaces.ISettingsControl
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<ThemeSettings> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@attribute [OqtaneIgnore]
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" ResourceType="@resourceType" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
|
{
|
||||||
|
<option value="site">@Localizer["Site"]</option>
|
||||||
|
}
|
||||||
|
<option value="page">@Localizer["Page"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="login" ResourceKey="Login" ResourceType="@resourceType" HelpText="Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.">Show Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="login" class="form-select" @bind="@_login">
|
||||||
|
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="register" ResourceKey="Register" ResourceType="@resourceType" HelpText="Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.">Show Register?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="register" class="form-select" @bind="@_register">
|
||||||
|
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private int pageId = -1;
|
||||||
|
private string resourceType = "[Owner].Theme.[Theme].ThemeSettings, [Owner].Theme.[Theme].Client.Oqtane"; // for localization
|
||||||
|
private string _scope = "page";
|
||||||
|
private string _login = "-";
|
||||||
|
private string _register = "-";
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
|
{
|
||||||
|
pageId = int.Parse(PageState.QueryString["id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await LoadSettings();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage("Error Loading Settings", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadSettings()
|
||||||
|
{
|
||||||
|
if (_scope == "site")
|
||||||
|
{
|
||||||
|
var settings = PageState.Site.Settings;
|
||||||
|
_login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "true");
|
||||||
|
_register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "true");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetPageSettingsAsync(pageId);
|
||||||
|
settings = SettingService.MergeSettings(PageState.Site.Settings, settings);
|
||||||
|
_login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "-");
|
||||||
|
_register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "-");
|
||||||
|
}
|
||||||
|
await Task.Yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ScopeChanged(ChangeEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_scope = (string)eventArgs.Value;
|
||||||
|
await LoadSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage("Error Loading Settings", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_scope == "site")
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
if (_login != "-")
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_register != "-")
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Register", _register);
|
||||||
|
}
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetPageSettingsAsync(pageId);
|
||||||
|
if (_login != "-")
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login);
|
||||||
|
}
|
||||||
|
if (_register != "-")
|
||||||
|
{
|
||||||
|
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Register", _register);
|
||||||
|
}
|
||||||
|
await SettingService.UpdatePageSettingsAsync(settings, pageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage("Error Saving Settings", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Oqtane Styles */
|
/* Oqtane Styles */
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding-top: 7rem;
|
padding-top: 7rem;
|
||||||
@@ -48,6 +48,7 @@ body {
|
|||||||
.app-menu .nav-item {
|
.app-menu .nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-menu .nav-item a {
|
.app-menu .nav-item a {
|
||||||
@@ -89,10 +90,10 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
|||||||
mix-blend-mode: difference;
|
mix-blend-mode: difference;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767.98px) {
|
||||||
|
|
||||||
.app-menu {
|
.app-menu {
|
||||||
width: 100%
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"Title": "Default Theme Template",
|
||||||
|
"Type": "Internal",
|
||||||
|
"Version": "10.0.0",
|
||||||
|
"Namespace": "[Owner].Theme.[Theme]"
|
||||||
|
}
|
||||||
17
Oqtane.Application/Shared/Oqtane.Application.Shared.csproj
Normal file
17
Oqtane.Application/Shared/Oqtane.Application.Shared.csproj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<AssemblyName>Oqtane.Application.Shared.Oqtane</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Oqtane.Shared" Version="10.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
BIN
Oqtane.Application/icon.png
Normal file
BIN
Oqtane.Application/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@@ -1,91 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Http
|
|
||||||
@inject IInstallationService InstallationService
|
|
||||||
@inject IJSRuntime JSRuntime
|
|
||||||
@inject SiteState SiteState
|
|
||||||
|
|
||||||
@if (_initialized)
|
|
||||||
{
|
|
||||||
@if (!_installation.Success)
|
|
||||||
{
|
|
||||||
<Installer />
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@if (string.IsNullOrEmpty(_installation.Message))
|
|
||||||
{
|
|
||||||
<div style="@_display">
|
|
||||||
<CascadingAuthenticationState>
|
|
||||||
<CascadingValue Value="@PageState">
|
|
||||||
<SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" VisitorId="@VisitorId" OnStateChange="@ChangeState" />
|
|
||||||
</CascadingValue>
|
|
||||||
</CascadingAuthenticationState>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="app-alert">
|
|
||||||
@_installation.Message
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter]
|
|
||||||
public string AntiForgeryToken { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string Runtime { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string RenderMode { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int VisitorId { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string RemoteIPAddress { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string AuthorizationToken { get; set; }
|
|
||||||
|
|
||||||
[CascadingParameter]
|
|
||||||
HttpContext HttpContext { get; set; }
|
|
||||||
|
|
||||||
private bool _initialized = false;
|
|
||||||
private string _display = "display: none;";
|
|
||||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
|
||||||
|
|
||||||
private PageState PageState { get; set; }
|
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
|
||||||
{
|
|
||||||
SiteState.RemoteIPAddress = RemoteIPAddress;
|
|
||||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
|
||||||
SiteState.AuthorizationToken = AuthorizationToken;
|
|
||||||
SiteState.IsPrerendering = (HttpContext != null) ? true : false;
|
|
||||||
|
|
||||||
_installation = await InstallationService.IsInstalled();
|
|
||||||
if (_installation.Alias != null)
|
|
||||||
{
|
|
||||||
SiteState.Alias = _installation.Alias;
|
|
||||||
}
|
|
||||||
_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
// prevents flash on initial page load
|
|
||||||
_display = "";
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ChangeState(PageState pageState)
|
|
||||||
{
|
|
||||||
PageState = pageState;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,31 @@
|
|||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Oqtane.Interfaces;
|
||||||
using Oqtane.Providers;
|
using Oqtane.Providers;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class OqtaneServiceCollectionExtensions
|
public static class OqtaneServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddOqtaneAuthorization(this IServiceCollection services)
|
public static IServiceCollection AddOqtaneAuthentication(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddAuthorizationCore();
|
services.AddAuthorizationCore();
|
||||||
|
services.AddCascadingAuthenticationState();
|
||||||
services.AddScoped<IdentityAuthenticationStateProvider>();
|
services.AddScoped<IdentityAuthenticationStateProvider>();
|
||||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<IdentityAuthenticationStateProvider>());
|
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<IdentityAuthenticationStateProvider>());
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
public static IServiceCollection AddOqtaneClientScopedServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<SiteState>();
|
services.AddScoped<SiteState>();
|
||||||
services.AddScoped<IInstallationService, InstallationService>();
|
services.AddScoped<IInstallationService, InstallationService>();
|
||||||
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
|
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
|
||||||
services.AddScoped<IThemeService, ThemeService>();
|
services.AddScoped<IThemeService, Oqtane.Services.ThemeService>();
|
||||||
services.AddScoped<IAliasService, AliasService>();
|
services.AddScoped<IAliasService, AliasService>();
|
||||||
services.AddScoped<ITenantService, TenantService>();
|
services.AddScoped<ITenantService, TenantService>();
|
||||||
services.AddScoped<ISiteService, SiteService>();
|
services.AddScoped<ISiteService, SiteService>();
|
||||||
@@ -37,7 +41,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<ILogService, LogService>();
|
services.AddScoped<ILogService, LogService>();
|
||||||
services.AddScoped<IJobService, JobService>();
|
services.AddScoped<IJobService, JobService>();
|
||||||
services.AddScoped<IJobLogService, JobLogService>();
|
services.AddScoped<IJobLogService, JobLogService>();
|
||||||
services.AddScoped<INotificationService, NotificationService>();
|
services.AddScoped<INotificationService, Oqtane.Services.NotificationService>();
|
||||||
services.AddScoped<IFolderService, FolderService>();
|
services.AddScoped<IFolderService, FolderService>();
|
||||||
services.AddScoped<IFileService, FileService>();
|
services.AddScoped<IFileService, FileService>();
|
||||||
services.AddScoped<ISiteTemplateService, SiteTemplateService>();
|
services.AddScoped<ISiteTemplateService, SiteTemplateService>();
|
||||||
@@ -49,6 +53,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.AddScoped<IUrlMappingService, UrlMappingService>();
|
services.AddScoped<IUrlMappingService, UrlMappingService>();
|
||||||
services.AddScoped<IVisitorService, VisitorService>();
|
services.AddScoped<IVisitorService, VisitorService>();
|
||||||
services.AddScoped<ISyncService, SyncService>();
|
services.AddScoped<ISyncService, SyncService>();
|
||||||
|
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||||
|
services.AddScoped<ICookieConsentService, CookieConsentService>();
|
||||||
|
services.AddScoped<ITimeZoneService, TimeZoneService>();
|
||||||
|
services.AddScoped<IMigrationHistoryService, MigrationHistoryService>();
|
||||||
|
services.AddScoped<IOutputCacheService, OutputCacheService>();
|
||||||
|
|
||||||
|
// providers
|
||||||
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||||
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.TextAreaTextEditor>();
|
||||||
|
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.RadzenTextEditor>();
|
||||||
|
|
||||||
|
services.AddRadzenComponents();
|
||||||
|
|
||||||
|
var localizer = services.BuildServiceProvider().GetService<IStringLocalizer<Oqtane.Modules.Controls.RadzenTextEditor>>();
|
||||||
|
Oqtane.Modules.Controls.RadzenEditorDefinitions.Localizer = localizer;
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
119
Oqtane.Client/Installer/Controls/AzureSqlConfig.razor
Normal file
119
Oqtane.Client/Installer/Controls/AzureSqlConfig.razor
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
@namespace Oqtane.Installer.Controls
|
||||||
|
@implements Oqtane.Interfaces.IDatabaseConfigControl
|
||||||
|
@inject IStringLocalizer<SqlServerConfig> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="database" type="text" class="form-control" @bind="@_database" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="security" HelpText="Select your security method" ResourceKey="Security">Security:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="security" class="form-select custom-select" @bind="@_security">
|
||||||
|
<option value="integrated" selected>@Localizer["Integrated"]</option>
|
||||||
|
<option value="custom">@Localizer["Custom"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_security == "custom")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="uid" type="text" class="form-control" @bind="@_uid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="encryption" HelpText="Specify if you are using an encrypted database connection. It is highly recommended to use encryption in a production environment." ResourceKey="Encryption">Encryption:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="encryption" class="form-select custom-select" @bind="@_encryption">
|
||||||
|
<option value="true">@SharedLocalizer["True"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["False"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_encryption == "true")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
||||||
|
<option value="true">@Localizer["Self Signed"]</option>
|
||||||
|
<option value="false">@Localizer["Verifiable"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _server = "tcp:{SQL Server Name}.database.windows.net,1433";
|
||||||
|
private string _database = "{SQL Database Name}";
|
||||||
|
private string _security = "custom";
|
||||||
|
private string _uid = "{SQL Administrator Login}";
|
||||||
|
private string _pwd = String.Empty;
|
||||||
|
private string _passwordType = "password";
|
||||||
|
private string _togglePassword = string.Empty;
|
||||||
|
private string _encryption = "true";
|
||||||
|
private string _trustservercertificate = "false";
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetConnectionString()
|
||||||
|
{
|
||||||
|
var connectionString = String.Empty;
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
|
||||||
|
{
|
||||||
|
connectionString = $"Data Source={_server};Initial Catalog={_database};";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_security == "integrated")
|
||||||
|
{
|
||||||
|
connectionString += "Integrated Security=SSPI;";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionString += $"User ID={_uid};Password={_pwd};";
|
||||||
|
}
|
||||||
|
connectionString += $"Encrypt={_encryption};";
|
||||||
|
connectionString += $"TrustServerCertificate={_trustservercertificate};";
|
||||||
|
|
||||||
|
return connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordType == "password")
|
||||||
|
{
|
||||||
|
_passwordType = "text";
|
||||||
|
_togglePassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordType = "password";
|
||||||
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
|
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mx-auto text-center">
|
<div class="mx-auto text-center">
|
||||||
<img src="oqtane-black.png" />
|
<img src="installer-logo.png" />
|
||||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 8)</div>
|
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET @Environment.Version.Major)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
@@ -69,16 +69,16 @@
|
|||||||
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="username" HelpText="Provide a username for the primary user accountt" ResourceKey="Username">Username:</Label>
|
<Label Class="col-sm-3" For="username" HelpText="Provide a username for the primary user account" ResourceKey="Username">Username:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="username" type="text" class="form-control" @bind="@_hostUsername" />
|
<input id="username" type="text" class="form-control" maxlength="256" @bind="@_hostUsername" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
<input id="password" type="@_passwordType" class="form-control" maxlength="256" @bind="@_hostPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
<input id="confirm" type="@_confirmPasswordType" class="form-control" maxlength="256" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword" tabindex="-1">@_toggleConfirmPassword</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword" tabindex="-1">@_toggleConfirmPassword</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +95,13 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
|
<Label Class="col-sm-3" For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" @bind="@_hostEmail" />
|
<input type="text" class="form-control" maxlength="256" @bind="@_hostEmail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="name" HelpText="Provide the full name of the host user" ResourceKey="Name">Full Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" maxlength="50" @bind="@_hostName" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -153,130 +159,134 @@
|
|||||||
private string _toggleConfirmPassword = string.Empty;
|
private string _toggleConfirmPassword = string.Empty;
|
||||||
private string _confirmPassword = string.Empty;
|
private string _confirmPassword = string.Empty;
|
||||||
private string _hostEmail = string.Empty;
|
private string _hostEmail = string.Empty;
|
||||||
|
private string _hostName = string.Empty;
|
||||||
private List<SiteTemplate> _templates;
|
private List<SiteTemplate> _templates;
|
||||||
private string _template = Constants.DefaultSiteTemplate;
|
private string _template = Constants.DefaultSiteTemplate;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
private string _loadingDisplay = "display: none;";
|
private string _loadingDisplay = "display: none;";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
// include CSS
|
// include CSS
|
||||||
var content = "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css\" integrity=\"sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==\" crossorigin=\"anonymous\" type=\"text/css\"/>";
|
var content = $"<link rel=\"stylesheet\" href=\"{Constants.BootstrapStylesheetUrl}\" integrity=\"{Constants.BootstrapStylesheetIntegrity}\" crossorigin=\"anonymous\" type=\"text/css\"/>";
|
||||||
SiteState.AppendHeadContent(content);
|
SiteState.AppendHeadContent(content);
|
||||||
|
|
||||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
{
|
{
|
||||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_databaseName = "LocalDB";
|
_databaseName = Constants.DefaultDBName;
|
||||||
}
|
}
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
|
|
||||||
_templates = await SiteTemplateService.GetSiteTemplatesAsync();
|
_templates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
_showConnectionString = false;
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
_message = Localizer["Error.DbConfig.Load"];
|
_message = Localizer["Error.DbConfig.Load"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadDatabaseConfigComponent()
|
private void LoadDatabaseConfigComponent()
|
||||||
{
|
{
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
if (database != null)
|
if (database != null)
|
||||||
{
|
{
|
||||||
_databaseConfigType = Type.GetType(database.ControlType);
|
_databaseConfigType = Type.GetType(database.ControlType);
|
||||||
DatabaseConfigComponent = builder =>
|
DatabaseConfigComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _databaseConfigType);
|
builder.OpenComponent(0, _databaseConfigType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
// include JavaScript
|
// include JavaScript
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", "anonymous", "", "head");
|
await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Install()
|
private async Task Install()
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
if (_showConnectionString)
|
if (_showConnectionString)
|
||||||
{
|
{
|
||||||
connectionString = _connectionString;
|
connectionString = _connectionString;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
{
|
{
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@") && !string.IsNullOrEmpty(_hostName))
|
||||||
{
|
{
|
||||||
if (await UserService.ValidatePasswordAsync(_hostPassword))
|
var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword);
|
||||||
{
|
if (result.Succeeded)
|
||||||
_loadingDisplay = "";
|
{
|
||||||
StateHasChanged();
|
_loadingDisplay = "";
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
Uri uri = new Uri(NavigationManager.Uri);
|
Uri uri = new Uri(NavigationManager.Uri);
|
||||||
|
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
|
|
||||||
var config = new InstallConfig
|
var config = new InstallConfig
|
||||||
{
|
{
|
||||||
DatabaseType = database.DBType,
|
DatabaseType = database.DBType,
|
||||||
ConnectionString = connectionString,
|
ConnectionString = connectionString,
|
||||||
Aliases = uri.Authority,
|
Aliases = uri.Authority,
|
||||||
HostUsername = _hostUsername,
|
HostUsername = _hostUsername,
|
||||||
HostPassword = _hostPassword,
|
HostPassword = _hostPassword,
|
||||||
HostEmail = _hostEmail,
|
HostEmail = _hostEmail,
|
||||||
HostName = _hostUsername,
|
HostName = _hostName,
|
||||||
TenantName = TenantNames.Master,
|
TenantName = TenantNames.Master,
|
||||||
IsNewTenant = true,
|
IsNewTenant = true,
|
||||||
SiteName = Constants.DefaultSite,
|
SiteName = Constants.DefaultSite,
|
||||||
Register = _register,
|
Register = _register,
|
||||||
SiteTemplate = _template
|
SiteTemplate = _template,
|
||||||
};
|
RenderMode = "", // provided by appsettings.json
|
||||||
|
Runtime = "" // provided by appsettings.json
|
||||||
|
};
|
||||||
|
|
||||||
var installation = await InstallationService.Install(config);
|
var installation = await InstallationService.Install(config);
|
||||||
if (installation.Success)
|
if (installation.Success)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
|
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = installation.Message;
|
_message = installation.Message;
|
||||||
_loadingDisplay = "display: none;";
|
_loadingDisplay = "display: none;";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Password.Invalid"];
|
_message = string.Join("<br />", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -12,24 +12,31 @@
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
string url = NavigateUrl(p.Path);
|
string url = NavigateUrl(p.Path);
|
||||||
<p class="col-md-2 mx-auto text-center mb-3">
|
<div class="col-md-2 mx-auto text-center my-3">
|
||||||
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All">
|
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All" @attributes="_attributes">
|
||||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
||||||
<p class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</p>
|
<div class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</p>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
Dictionary<string, object> _attributes { get; set; } = new();
|
||||||
|
private List<Page> _pages;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
public override string RenderMode => RenderModes.Static;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
|
if (PageState.RenderMode == RenderModes.Static && !PageState.Site.EnhancedNavigation)
|
||||||
|
{
|
||||||
|
_attributes.Add("data-enhance-nav", "true"); // Admin Dashboard utilizes enhanced navigation
|
||||||
|
}
|
||||||
|
|
||||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||||
if (admin != null)
|
if (admin != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,10 +40,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
<br />
|
@if (_name.ToLower().EndsWith(".zip"))
|
||||||
<br />
|
{
|
||||||
|
<button type="button" class="btn btn-primary mx-1" @onclick="UnzipFile">Unzip</button>
|
||||||
|
}
|
||||||
|
<br /><br />
|
||||||
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
@@ -126,4 +130,18 @@
|
|||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UnzipFile()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await FileService.UnzipFileAsync(_fileId);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Unzipping File {FileId} {Error}", _fileId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.File.Unzip"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,67 +8,95 @@
|
|||||||
|
|
||||||
@if (_folders != null)
|
@if (_folders != null)
|
||||||
{
|
{
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<TabStrip>
|
||||||
<div class="container">
|
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||||
<div class="row mb-1 align-items-center">
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<select id="parent" class="form-select" @bind="@_parentId" required>
|
<Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
|
||||||
@if (PageState.QueryString.ContainsKey("id"))
|
<div class="col-sm-9">
|
||||||
{
|
@if (_parentId == -1)
|
||||||
<option value="-1"><@Localizer["NoParent"]></option>
|
{
|
||||||
}
|
<select id="parent" class="form-select" @bind="@_parentId" required>
|
||||||
@foreach (Folder folder in _folders)
|
<option value="-1"><@Localizer["NoParent"]></option>
|
||||||
{
|
</select>
|
||||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
}
|
||||||
}
|
else
|
||||||
</select>
|
{
|
||||||
|
<select id="parent" class="form-select" @bind="@_parentId" required>
|
||||||
|
@foreach (Folder folder in _folders)
|
||||||
|
{
|
||||||
|
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
@if (_isSystem)
|
||||||
|
{
|
||||||
|
<input id="name" class="form-control" @bind="@_name" readonly />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Type">Type: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
@if (PageState.QueryString.ContainsKey("id"))
|
||||||
|
{
|
||||||
|
<input id="type" class="form-control" readonly @bind="@_type" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<select id="type" class="form-select" @bind="@_type" required>
|
||||||
|
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
|
||||||
|
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="capacity" class="form-control" @bind="@_capacity" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="cachecontrol" HelpText="Optionally provide a Cache-Control directive for this folder. For example 'public, max-age=60' indicates that files in this folder should be cached for 60 seconds. Please note that when caching is enabled, changes to files will not be immediately reflected in the UI." ResourceKey="CacheControl">Caching: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="cachecontrol" class="form-control" @bind="@_cachecontrol" maxlength="50" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="imagesizes" HelpText="Optionally enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes (not recommended)." ResourceKey="ImageSizes">Image Sizes: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@if (PageState.QueryString.ContainsKey("id"))
|
||||||
<div class="row mb-1 align-items-center">
|
{
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
|
<br />
|
||||||
<div class="col-sm-9">
|
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
||||||
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
|
}
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</TabPanel>
|
||||||
<div class="row mb-1 align-items-center">
|
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
|
||||||
<Label Class="col-sm-3" For="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Type">Type: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
@if (PageState.QueryString.ContainsKey("id"))
|
|
||||||
{
|
|
||||||
<input id="type" class="form-control" readonly @bind="@_type" />
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<select id="type" class="form-select" @bind="@_type" required>
|
|
||||||
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
|
|
||||||
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
|
|
||||||
</select>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes." ResourceKey="ImageSizes">Image Sizes: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="capacity" class="form-control" @bind="@_capacity" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
|
|
||||||
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TabPanel>
|
||||||
</form>
|
</TabStrip>
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
|
<br />
|
||||||
@if (!_isSystem)
|
@if (!_isSystem)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button>
|
||||||
@@ -80,11 +108,6 @@
|
|||||||
@((MarkupString)" ")
|
@((MarkupString)" ")
|
||||||
<ActionDialog Header="Delete Folder" Message="Are You Sure You Wish To Delete This Folder?" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFolder())" ResourceKey="DeleteFolder" />
|
<ActionDialog Header="Delete Folder" Message="Are You Sure You Wish To Delete This Folder?" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFolder())" ResourceKey="DeleteFolder" />
|
||||||
}
|
}
|
||||||
<br /><br />
|
|
||||||
@if (PageState.QueryString.ContainsKey("id"))
|
|
||||||
{
|
|
||||||
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -95,8 +118,9 @@
|
|||||||
private int _parentId = -1;
|
private int _parentId = -1;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _type = FolderTypes.Private;
|
private string _type = FolderTypes.Private;
|
||||||
private string _imagesizes = string.Empty;
|
|
||||||
private string _capacity = "0";
|
private string _capacity = "0";
|
||||||
|
private string _cachecontrol = string.Empty;
|
||||||
|
private string _imagesizes = string.Empty;
|
||||||
private bool _isSystem;
|
private bool _isSystem;
|
||||||
private List<Permission> _permissions = null;
|
private List<Permission> _permissions = null;
|
||||||
private string _createdBy;
|
private string _createdBy;
|
||||||
@@ -127,8 +151,9 @@
|
|||||||
_parentId = folder.ParentId ?? -1;
|
_parentId = folder.ParentId ?? -1;
|
||||||
_name = folder.Name;
|
_name = folder.Name;
|
||||||
_type = folder.Type;
|
_type = folder.Type;
|
||||||
_imagesizes = folder.ImageSizes;
|
|
||||||
_capacity = folder.Capacity.ToString();
|
_capacity = folder.Capacity.ToString();
|
||||||
|
_cachecontrol = folder.CacheControl;
|
||||||
|
_imagesizes = folder.ImageSizes;
|
||||||
_isSystem = folder.IsSystem;
|
_isSystem = folder.IsSystem;
|
||||||
_permissions = folder.PermissionList;
|
_permissions = folder.PermissionList;
|
||||||
_createdBy = folder.CreatedBy;
|
_createdBy = folder.CreatedBy;
|
||||||
@@ -170,6 +195,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Folder folder;
|
Folder folder;
|
||||||
|
|
||||||
if (_folderId != -1)
|
if (_folderId != -1)
|
||||||
{
|
{
|
||||||
folder = await FolderService.GetFolderAsync(_folderId);
|
folder = await FolderService.GetFolderAsync(_folderId);
|
||||||
@@ -179,8 +205,6 @@
|
|||||||
folder = new Folder();
|
folder = new Folder();
|
||||||
}
|
}
|
||||||
|
|
||||||
folder.SiteId = PageState.Site.SiteId;
|
|
||||||
|
|
||||||
if (_parentId == -1)
|
if (_parentId == -1)
|
||||||
{
|
{
|
||||||
folder.ParentId = null;
|
folder.ParentId = null;
|
||||||
@@ -190,10 +214,19 @@
|
|||||||
folder.ParentId = _parentId;
|
folder.ParentId = _parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for duplicate folder names
|
||||||
|
if (_folders.Any(item => item.ParentId == folder.ParentId && item.Name == _name && item.FolderId != _folderId))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Folder.Duplicate"], MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.SiteId = PageState.Site.SiteId;
|
||||||
folder.Name = _name;
|
folder.Name = _name;
|
||||||
folder.Type = _type;
|
folder.Type = _type;
|
||||||
folder.ImageSizes = _imagesizes;
|
|
||||||
folder.Capacity = int.Parse(_capacity);
|
folder.Capacity = int.Parse(_capacity);
|
||||||
|
folder.CacheControl = _cachecontrol;
|
||||||
|
folder.ImageSizes = _imagesizes;
|
||||||
folder.IsSystem = _isSystem;
|
folder.IsSystem = _isSystem;
|
||||||
folder.PermissionList = _permissionGrid.GetPermissionList();
|
folder.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
|
|
||||||
@@ -208,7 +241,6 @@
|
|||||||
|
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
|
|
||||||
await logger.LogInformation("Folder Saved {Folder}", folder);
|
await logger.LogInformation("Folder Saved {Folder}", folder);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
@@ -244,17 +276,9 @@
|
|||||||
}
|
}
|
||||||
if (!isparent)
|
if (!isparent)
|
||||||
{
|
{
|
||||||
var files = await FileService.GetFilesAsync(_folderId);
|
await FolderService.DeleteFolderAsync(_folderId);
|
||||||
if (files.Count == 0)
|
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
|
||||||
{
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
await FolderService.DeleteFolderAsync(_folderId);
|
|
||||||
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Folder.Files.InvalidDelete"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,54 +3,92 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_files != null)
|
@if (_files == null)
|
||||||
{
|
{
|
||||||
<div class="row">
|
<p>
|
||||||
<div class="col-md mb-1">
|
<em>@SharedLocalizer["Loading"]</em>
|
||||||
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
|
</p>
|
||||||
</div>
|
}
|
||||||
<div class="col-md-8 mb-1">
|
else
|
||||||
<div class="input-group">
|
{
|
||||||
<span class="input-group-text">@Localizer["Folder"]:</span>
|
<TabStrip>
|
||||||
<select class="form-select" @onchange="(e => FolderChanged(e))">
|
<TabPanel Name="Files" Heading="Files" ResourceKey="Files">
|
||||||
@foreach (Folder folder in _folders)
|
<div class="row">
|
||||||
{
|
<div class="col-md mb-1">
|
||||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
|
||||||
}
|
</div>
|
||||||
</select>
|
<div class="col-md-8 mb-1">
|
||||||
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">@Localizer["Folder"]:</span>
|
||||||
|
<select class="form-select" @onchange="(e => FolderChanged(e))">
|
||||||
|
@foreach (Folder folder in _folders)
|
||||||
|
{
|
||||||
|
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md mb-1 text-end">
|
||||||
|
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col-md mb-1 text-end">
|
|
||||||
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Pager Items="@_files" SearchProperties="Name">
|
@if (_files.Count != 0)
|
||||||
<Header>
|
{
|
||||||
<th style="width: 1px;"> </th>
|
<Pager Items="@_files" SearchProperties="Name">
|
||||||
<th style="width: 1px;"> </th>
|
<Header>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Modified"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Type"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Size"]</th>
|
<th>@Localizer["Modified"]</th>
|
||||||
</Header>
|
<th>@Localizer["Type"]</th>
|
||||||
<Row>
|
<th>@Localizer["Size"]</th>
|
||||||
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
|
</Header>
|
||||||
<td><ActionDialog Header="Delete File" Message="@string.Format(Localizer["Confirm.File.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
|
<Row>
|
||||||
<td><a href="@context.Url" target="_new">@context.Name</a></td>
|
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
|
||||||
<td>@context.ModifiedOn</td>
|
<td><ActionDialog Header="Delete File" Message="@string.Format(Localizer["Confirm.File.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
|
||||||
<td>@context.Extension.ToUpper() @SharedLocalizer["File"]</td>
|
<td><a href="@context.Url" target="_new">@context.Name</a></td>
|
||||||
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
|
<td>@UtcToLocal(context.ModifiedOn)</td>
|
||||||
</Row>
|
<td>@context.Extension.ToUpper() @SharedLocalizer["File"]</td>
|
||||||
</Pager>
|
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
|
||||||
@if (_files.Count == 0)
|
</Row>
|
||||||
{
|
</Pager>
|
||||||
<div class="text-center">@Localizer["NoFiles"]</div>
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
<div class="text-center">@Localizer["NoFiles"]</div>
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="imageExt" HelpText="Enter a comma separated list of image file extensions" ResourceKey="ImageExtensions">Image Extensions: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="imageExt" spellcheck="false" class="form-control" @bind="@_imageFiles" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="uploadableFileExt" HelpText="Enter a comma separated list of uploadable file extensions" ResourceKey="UploadableFileExtensions">Uploadable File Extensions: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="uploadableFileExt" spellcheck="false" class="form-control" @bind="@_uploadableFiles" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="maxChunkSize" HelpText="Files are split into chunks to streamline the upload process. Specify the maximum chunk size in MB (note that higher chunk sizes should only be used on faster networks)." ResourceKey="MaxChunkSize">Max Upload Chunk Size (MB): </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="maxChunkSize" type="number" min="1" max="10" step="1" class="form-control" @bind="@_maxChunkSize" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -58,6 +96,10 @@
|
|||||||
private int _folderId = -1;
|
private int _folderId = -1;
|
||||||
private List<File> _files;
|
private List<File> _files;
|
||||||
|
|
||||||
|
private string _imageFiles = string.Empty;
|
||||||
|
private string _uploadableFiles = string.Empty;
|
||||||
|
private int _maxChunkSize = 1;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
@@ -71,6 +113,13 @@
|
|||||||
_folderId = _folders[0].FolderId;
|
_folderId = _folders[0].FolderId;
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
_imageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||||
|
_imageFiles = (string.IsNullOrEmpty(_imageFiles)) ? Constants.ImageFiles : _imageFiles;
|
||||||
|
_uploadableFiles = SettingService.GetSetting(settings, "UploadableFiles", Constants.UploadableFiles);
|
||||||
|
_uploadableFiles = (string.IsNullOrEmpty(_uploadableFiles)) ? Constants.UploadableFiles : _uploadableFiles;
|
||||||
|
_maxChunkSize = int.Parse(SettingService.GetSetting(settings, "MaxChunkSize", "1"));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -115,4 +164,23 @@
|
|||||||
AddModuleMessage(string.Format(Localizer["Error.File.Delete"], file.Name), MessageType.Error);
|
AddModuleMessage(string.Format(Localizer["Error.File.Delete"], file.Name), MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveSiteSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "ImageFiles", (_imageFiles != Constants.ImageFiles) ? _imageFiles.Replace(" ", "") : "", false);
|
||||||
|
settings = SettingService.SetSetting(settings, "UploadableFiles", (_uploadableFiles != Constants.UploadableFiles) ? _uploadableFiles.Replace(" ", "") : "", false);
|
||||||
|
settings = SettingService.SetSetting(settings, "MaxChunkSize", _maxChunkSize.ToString(), false);
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
19
Oqtane.Client/Modules/Admin/Files/ModuleInfo.cs
Normal file
19
Oqtane.Client/Modules/Admin/Files/ModuleInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Files
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "File Management",
|
||||||
|
Description = "File Management",
|
||||||
|
Version = Constants.Version,
|
||||||
|
Categories = "Admin",
|
||||||
|
ServerManagerType = "Oqtane.Modules.Admin.Files.Manager.FileManager, Oqtane.Server"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
|
<Label Class="col-sm-3" For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="runs-every" class="form-control" @bind="@_interval" maxlength="4" required />
|
<input id="runs-every" class="form-control mb-1" @bind="@_interval" maxlength="4" required />
|
||||||
<select id="runs-every" class="form-select" @bind="@_frequency" required>
|
<select id="runs-every" class="form-select" @bind="@_frequency" required>
|
||||||
<option value="m">@Localizer["Minute(s)"]</option>
|
<option value="m">@Localizer["Minute(s)"]</option>
|
||||||
<option value="H">@Localizer["Hour(s)"]</option>
|
<option value="H">@Localizer["Hour(s)"]</option>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="3" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<input id="starting" type="date" class="form-control" @bind="@_startDate" />
|
<input id="starting" type="date" class="form-control" @bind="@_startDate" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input id="starting" type="time" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
|
<input id="starting" type="time" class="form-control" @bind="@_startTime" placeholder="hh:mm" required="@(_startDate.HasValue)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<input id="ending" type="date" class="form-control" @bind="@_endDate" />
|
<input id="ending" type="date" class="form-control" @bind="@_endDate" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
|
<input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" required="@(_endDate.HasValue)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<input id="next" type="date" class="form-control" @bind="@_nextDate" />
|
<input id="next" type="date" class="form-control" @bind="@_nextDate" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
|
<input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" required="@(_nextDate.HasValue)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,13 +132,13 @@
|
|||||||
_isEnabled = job.IsEnabled.ToString();
|
_isEnabled = job.IsEnabled.ToString();
|
||||||
_interval = job.Interval.ToString();
|
_interval = job.Interval.ToString();
|
||||||
_frequency = job.Frequency;
|
_frequency = job.Frequency;
|
||||||
_startDate = Utilities.UtcAsLocalDate(job.StartDate);
|
_startDate = UtcToLocal(job.StartDate);
|
||||||
_startTime = Utilities.UtcAsLocalDateTime(job.StartDate);
|
_startTime = UtcToLocal(job.StartDate);
|
||||||
_endDate = Utilities.UtcAsLocalDate(job.EndDate);
|
_endDate = UtcToLocal(job.EndDate);
|
||||||
_endTime = Utilities.UtcAsLocalDateTime(job.EndDate);
|
_endTime = UtcToLocal(job.EndDate);
|
||||||
_retentionHistory = job.RetentionHistory.ToString();
|
_retentionHistory = job.RetentionHistory.ToString();
|
||||||
_nextDate = Utilities.UtcAsLocalDate(job.NextExecution);
|
_nextDate = UtcToLocal(job.NextExecution);
|
||||||
_nextTime = Utilities.UtcAsLocalDateTime(job.NextExecution);
|
_nextTime = UtcToLocal(job.NextExecution);
|
||||||
createdby = job.CreatedBy;
|
createdby = job.CreatedBy;
|
||||||
createdon = job.CreatedOn;
|
createdon = job.CreatedOn;
|
||||||
modifiedby = job.ModifiedBy;
|
modifiedby = job.ModifiedBy;
|
||||||
@@ -154,6 +154,11 @@
|
|||||||
|
|
||||||
private async Task SaveJob()
|
private async Task SaveJob()
|
||||||
{
|
{
|
||||||
|
if (!Utilities.ValidateEffectiveExpiryDates(_startDate, _endDate))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
@@ -171,10 +176,18 @@
|
|||||||
{
|
{
|
||||||
job.Interval = int.Parse(_interval);
|
job.Interval = int.Parse(_interval);
|
||||||
}
|
}
|
||||||
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
job.StartDate = _startDate.HasValue && _startTime.HasValue
|
||||||
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay))
|
||||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
: null;
|
||||||
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime);
|
|
||||||
|
job.EndDate = _endDate.HasValue && _endTime.HasValue
|
||||||
|
? LocalToUtc(_endDate.GetValueOrDefault().Date.Add(_endTime.GetValueOrDefault().TimeOfDay))
|
||||||
|
: null;
|
||||||
|
|
||||||
|
job.NextExecution = _nextDate.HasValue && _nextTime.HasValue
|
||||||
|
? LocalToUtc(_nextDate.GetValueOrDefault().Date.Add(_nextTime.GetValueOrDefault().TimeOfDay))
|
||||||
|
: null;
|
||||||
|
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -193,5 +206,4 @@
|
|||||||
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,11 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
|
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh.Text"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">@Localizer["Refresh.Text"]</button>
|
|
||||||
<br />
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Pager Items="@_jobs" SearchProperties="Name">
|
<Pager Items="@_jobs" SearchProperties="Name">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
@@ -27,13 +24,12 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
|
||||||
<td><ActionDialog Header="Delete Job" Message="Are You Sure You Wish To Delete This Job?" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteJob(context))" ResourceKey="DeleteJob" /></td>
|
<td><ActionLink Action="Log" Text="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
||||||
<td><ActionLink Action="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||||
<td>@context.NextExecution?.ToLocalTime()</td>
|
<td>@UtcToLocal(context.NextExecution)</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.IsStarted)
|
@if (context.IsStarted)
|
||||||
{
|
{
|
||||||
@@ -46,21 +42,25 @@ else
|
|||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<ActionLink Action="Log" Class="btn btn-secondary" Text="View All Logs" ResourceKey="ViewLogs" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Job> _jobs;
|
private List<Job> _jobs;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_jobs = await JobService.GetJobsAsync();
|
await GetJobs();
|
||||||
if (_jobs.Count == 0)
|
}
|
||||||
{
|
|
||||||
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
private async Task GetJobs()
|
||||||
}
|
{
|
||||||
}
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||||
{
|
{
|
||||||
@@ -112,31 +112,23 @@ else
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteJob(Job job)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await JobService.DeleteJobAsync(job.JobId);
|
|
||||||
await logger.LogInformation("Job Deleted {Job}", job);
|
|
||||||
_jobs = await JobService.GetJobsAsync();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Deleting Job {Job} {Error}", job, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Job.Delete"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartJob(int jobId)
|
private async Task StartJob(int jobId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await JobService.StartJobAsync(jobId);
|
Job _job = await JobService.GetJobAsync(jobId);
|
||||||
await logger.LogInformation("Job Started {JobId}", jobId);
|
if (!_job.IsEnabled)
|
||||||
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
|
{
|
||||||
_jobs = await JobService.GetJobsAsync();
|
AddModuleMessage(Localizer["Message.Job.Disabled"], MessageType.Warning);
|
||||||
StateHasChanged();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await JobService.StartJobAsync(jobId);
|
||||||
|
await logger.LogInformation("Job Started {JobId}", jobId);
|
||||||
|
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -164,7 +156,8 @@ else
|
|||||||
|
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
{
|
{
|
||||||
_jobs = await JobService.GetJobsAsync();
|
ShowProgressIndicator();
|
||||||
StateHasChanged();
|
await GetJobs();
|
||||||
|
HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh"]</button>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
<Pager Items="@_jobLogs">
|
<Pager Items="@_jobLogs">
|
||||||
<Header>
|
<Header>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
@@ -20,8 +23,8 @@ else
|
|||||||
<Row>
|
<Row>
|
||||||
<td>@context.Job.Name</td>
|
<td>@context.Job.Name</td>
|
||||||
<td>@DisplayStatus(context.Job.IsExecuting, context.Succeeded)</td>
|
<td>@DisplayStatus(context.Job.IsExecuting, context.Succeeded)</td>
|
||||||
<td>@context.StartDate</td>
|
<td>@UtcToLocal(context.StartDate)</td>
|
||||||
<td>@context.FinishDate</td>
|
<td>@UtcToLocal(context.FinishDate)</td>
|
||||||
</Row>
|
</Row>
|
||||||
<Detail>
|
<Detail>
|
||||||
<td colspan="4">@((MarkupString)context.Notes)</td>
|
<td colspan="4">@((MarkupString)context.Notes)</td>
|
||||||
@@ -36,14 +39,17 @@ else
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_jobLogs = await JobLogService.GetJobLogsAsync();
|
await GetJobLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetJobLogs()
|
||||||
|
{
|
||||||
|
var jobId = -1;
|
||||||
if (PageState.QueryString.ContainsKey("id"))
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
{
|
{
|
||||||
_jobLogs = _jobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList();
|
jobId = int.Parse(PageState.QueryString["id"]);
|
||||||
}
|
}
|
||||||
|
_jobLogs = await JobLogService.GetJobLogsAsync(jobId);
|
||||||
_jobLogs = _jobLogs.OrderByDescending(item => item.JobLogId).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DisplayStatus(bool isExecuting, bool? succeeded)
|
private string DisplayStatus(bool isExecuting, bool? succeeded)
|
||||||
@@ -67,4 +73,11 @@ else
|
|||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Refresh()
|
||||||
|
{
|
||||||
|
ShowProgressIndicator();
|
||||||
|
await GetJobLogs();
|
||||||
|
HideProgressIndicator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Languages
|
@namespace Oqtane.Modules.Admin.Languages
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILocalizationService LocalizationService
|
@inject ILocalizationService LocalizationService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@@ -94,7 +93,6 @@ else
|
|||||||
var language = new Language
|
var language = new Language
|
||||||
{
|
{
|
||||||
SiteId = PageState.Page.SiteId,
|
SiteId = PageState.Page.SiteId,
|
||||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
|
||||||
Code = _code,
|
Code = _code,
|
||||||
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
||||||
};
|
};
|
||||||
@@ -130,7 +128,7 @@ else
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Languages
|
@namespace Oqtane.Modules.Admin.Languages
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ILocalizationService LocalizationService
|
@inject ILocalizationService LocalizationService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@@ -103,7 +102,7 @@ else
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,89 +8,147 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
@if (PageState.User != null)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
|
||||||
</Authorizing>
|
}
|
||||||
<NotAuthorized>
|
else
|
||||||
@if (!twofactor)
|
{
|
||||||
{
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
||||||
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
@switch (_action)
|
||||||
@if (_allowexternallogin)
|
{
|
||||||
{
|
case "Login":
|
||||||
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
@if (_allowexternallogin)
|
||||||
<br /><br />
|
{
|
||||||
}
|
<button type="button" class="btn btn-primary col-12" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||||
@if (_allowsitelogin)
|
<hr class="app-rule mt-3 mb-2" />
|
||||||
{
|
}
|
||||||
<div class="form-group">
|
@if (_allowsitelogin)
|
||||||
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
{
|
||||||
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
<div class="form-group text-center">
|
||||||
</div>
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||||
<div class="form-group mt-2">
|
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" @bind:event="oninput" required />
|
||||||
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
</div>
|
||||||
<div class="input-group">
|
<div class="form-group text-center mt-2">
|
||||||
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
<div class="input-group">
|
||||||
</div>
|
<input id="password" type="@_passwordtype" @ref="password" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" @bind:event="oninput" required />
|
||||||
</div>
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||||
<div class="form-group mt-2">
|
</div>
|
||||||
@if (!_alwaysremember)
|
</div>
|
||||||
{
|
@if (!_alwaysremember)
|
||||||
<div class="form-check">
|
{
|
||||||
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
<div class="form-group text-center mt-2">
|
||||||
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
<div>
|
||||||
</div>
|
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||||
}
|
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Stay Signed In?</Label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
}
|
||||||
<br /><br />
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
<div class="btn-group mt-2 col-12" role="group">
|
||||||
}
|
<button type="button" class="btn btn-primary col-6" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
</div>
|
<button type="button" class="btn btn-secondary col-6" @onclick="CancelLogin">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</div>
|
||||||
}
|
|
||||||
else
|
<button type="button" class="btn btn-secondary col-12 mt-4" @onclick="@(() => SetAction("ForgotPassword"))">@Localizer["ForgotPassword"]</button>
|
||||||
{
|
}
|
||||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
|
||||||
<div class="container Oqtane-Modules-Admin-Login">
|
@if (_allowloginlink)
|
||||||
<div class="form-group">
|
{
|
||||||
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
|
<hr class="app-rule mt-3" />
|
||||||
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
|
<button type="button" class="btn btn-primary col-12 mt-2" @onclick="@(() => SetAction("LoginLink"))">@Localizer["UseLoginLink"]</button>
|
||||||
</div>
|
}
|
||||||
<br />
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
@if (_allowpasskeys)
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
|
{
|
||||||
</div>
|
<hr class="app-rule mt-3" />
|
||||||
</form>
|
<button type="button" class="btn btn-primary col-12 mt-2" @onclick="PasskeyLogin">@Localizer["Passkey"]</button>
|
||||||
}
|
}
|
||||||
</NotAuthorized>
|
|
||||||
</AuthorizeView>
|
@if (PageState.Site.AllowRegistration)
|
||||||
|
{
|
||||||
|
<hr class="app-rule mt-3" />
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<NavLink href="@_registerurl">@Localizer["Register"]</NavLink>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ForgotPassword":
|
||||||
|
<div class="form-group text-center">
|
||||||
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||||
|
<input id="username" type="text" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" @bind:event="oninput" required />
|
||||||
|
</div>
|
||||||
|
<div class="btn-group mt-4 col-12" role="group">
|
||||||
|
<button type="button" class="btn btn-primary col-6" @onclick="ForgotPassword">@SharedLocalizer["Send"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary col-6" @onclick="@(() => SetAction("Login"))">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-secondary col-12 mt-4" @onclick="@(() => SetAction("ForgotUsername"))">@Localizer["ForgotUsername"]</button>
|
||||||
|
break;
|
||||||
|
case "ForgotUsername":
|
||||||
|
<div class="form-group text-center">
|
||||||
|
<Label Class="control-label" For="email" HelpText="Please enter your Email" ResourceKey="Email">Email:</Label>
|
||||||
|
<input id="email" type="text" class="form-control" placeholder="@Localizer["Email.Placeholder"]" @bind="@_email" required />
|
||||||
|
</div>
|
||||||
|
<div class="btn-group mt-4 col-12" role="group">
|
||||||
|
<button type="button" class="btn btn-primary col-6" @onclick="ForgotUsername">@SharedLocalizer["Send"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary col-6" @onclick="@(() => SetAction("Login"))">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
case "LoginLink":
|
||||||
|
<div class="form-group text-center">
|
||||||
|
<Label Class="control-label" For="email" HelpText="Please enter your Email" ResourceKey="Email">Email:</Label>
|
||||||
|
<input id="email" type="text" class="form-control" placeholder="@Localizer["Email.Placeholder"]" @bind="@_email" required />
|
||||||
|
</div>
|
||||||
|
<div class="btn-group mt-4 col-12" role="group">
|
||||||
|
<button type="button" class="btn btn-primary col-6" @onclick="LoginLink">@SharedLocalizer["Send"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary col-6" @onclick="@(() => SetAction("Login"))">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
case "TwoFactor":
|
||||||
|
<div class="form-group">
|
||||||
|
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
|
||||||
|
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
|
||||||
|
</div>
|
||||||
|
<div class="btn-group mt-4 col-12" role="group">
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@(() => SetAction("Login"))">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _allowsitelogin = true;
|
private string _action = "Login";
|
||||||
private bool _allowexternallogin = false;
|
private bool _allowexternallogin = false;
|
||||||
|
private bool _allowsitelogin = true;
|
||||||
|
private bool _allowloginlink = false;
|
||||||
|
private bool _allowpasskeys = false;
|
||||||
|
private string _returnurl = string.Empty;
|
||||||
|
|
||||||
private ElementReference login;
|
private ElementReference login;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private bool twofactor = false;
|
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private ElementReference username;
|
private ElementReference username;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string _passwordtype = "password";
|
private string _passwordtype = "password";
|
||||||
private string _togglepassword = string.Empty;
|
private string _togglepassword = string.Empty;
|
||||||
|
private ElementReference password;
|
||||||
private bool _remember = false;
|
private bool _remember = false;
|
||||||
private bool _alwaysremember = false;
|
private bool _alwaysremember = false;
|
||||||
|
private string _registerurl = string.Empty;
|
||||||
|
private string _email = string.Empty;
|
||||||
private string _code = string.Empty;
|
private string _code = string.Empty;
|
||||||
|
|
||||||
private string _returnUrl = string.Empty;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
public override bool? Prerender => true;
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
new Stylesheet(ModulePath() + "Module.css")
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
@@ -99,14 +157,23 @@
|
|||||||
{
|
{
|
||||||
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||||
|
_allowloginlink = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LoginLink", "false"));
|
||||||
|
_allowpasskeys = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:Passkeys", "false"));
|
||||||
_alwaysremember = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AlwaysRemember", "false"));
|
_alwaysremember = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AlwaysRemember", "false"));
|
||||||
|
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
if (!string.IsNullOrEmpty(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "")))
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
|
||||||
{
|
{
|
||||||
_returnUrl = PageState.QueryString["returnurl"];
|
_registerurl = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_registerurl = NavigateUrl("register");
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageState.ReturnUrl is not specified if user navigated directly to login page
|
||||||
|
_returnurl = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : PageState.Alias.Path;
|
||||||
|
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("name"))
|
if (PageState.QueryString.ContainsKey("name"))
|
||||||
{
|
{
|
||||||
@@ -121,7 +188,7 @@
|
|||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("key"))
|
if (PageState.QueryString.ContainsKey("key"))
|
||||||
{
|
{
|
||||||
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
user = await UserService.AddLoginAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||||
@@ -139,7 +206,7 @@
|
|||||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Email Verified For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -153,7 +220,7 @@
|
|||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("status"))
|
if (PageState.QueryString.ContainsKey("status"))
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,17 +231,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
private async Task KeyPressed(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
if (firstRender && PageState.User == null && _allowsitelogin)
|
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||||
{
|
{
|
||||||
await username.FocusAsync();
|
switch (_action)
|
||||||
|
{
|
||||||
|
case "Login":
|
||||||
|
await Login();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// redirect logged in user to specified page
|
private void SetAction(string action)
|
||||||
if (PageState.User != null)
|
{
|
||||||
|
_action = action;
|
||||||
|
_username = "";
|
||||||
|
_password = "";
|
||||||
|
_email = "";
|
||||||
|
ClearModuleMessage();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExternalLogin()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + WebUtility.UrlEncode(_returnurl)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TogglePassword()
|
||||||
|
{
|
||||||
|
if (_passwordtype == "password")
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
_passwordtype = "text";
|
||||||
|
_togglepassword = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passwordtype = "password";
|
||||||
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +284,7 @@
|
|||||||
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
||||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
|
|
||||||
if (!twofactor)
|
if (_action == "Login")
|
||||||
{
|
{
|
||||||
_remember = _alwaysremember || _remember;
|
_remember = _alwaysremember || _remember;
|
||||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||||
@@ -199,36 +294,36 @@
|
|||||||
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.IsAuthenticated)
|
if (user != null && user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Successful For {Username} From IP Address {IPAddress}", _username, SiteState.RemoteIPAddress);
|
||||||
|
|
||||||
if (hybrid)
|
if (hybrid)
|
||||||
{
|
{
|
||||||
// hybrid apps utilize an interactive login
|
// hybrid apps utilize an interactive login
|
||||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
authstateprovider.NotifyAuthenticationChanged();
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
|
NavigationManager.NavigateTo(NavigateUrl(_returnurl, true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// post back to the Login page so that the cookies are set correctly
|
// post back to the Login page so that the cookies are set correctly
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = WebUtility.UrlEncode(_returnurl) };
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||||
await interop.SubmitForm(url, fields);
|
await interop.SubmitForm(url, fields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || user.TwoFactorRequired)
|
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || (user != null && user.TwoFactorRequired))
|
||||||
{
|
{
|
||||||
twofactor = true;
|
_action = "TwoFactor";
|
||||||
validated = false;
|
validated = false;
|
||||||
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!twofactor)
|
if (_action != "TwoFactor")
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
||||||
@@ -253,23 +348,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Cancel()
|
private void CancelLogin()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(_returnUrl);
|
NavigationManager.NavigateTo(_returnurl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Forgot()
|
private async Task PasskeyLogin()
|
||||||
|
{
|
||||||
|
// post back to the Passkey page so that the cookies are set correctly
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, operation = "request", returnurl = _returnurl };
|
||||||
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/passkey/");
|
||||||
|
await interop.SubmitForm(url, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ForgotPassword()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_username != string.Empty)
|
if (!string.IsNullOrEmpty(_username))
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
if (await UserService.ForgotPasswordAsync(_username))
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
await UserService.ForgotPasswordAsync(user);
|
|
||||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -278,10 +380,8 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -290,40 +390,114 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Reset()
|
private async Task ForgotUsername()
|
||||||
{
|
{
|
||||||
twofactor = false;
|
try
|
||||||
_username = "";
|
|
||||||
_password = "";
|
|
||||||
ClearModuleMessage();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task KeyPressed(KeyboardEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
|
||||||
{
|
{
|
||||||
await Login();
|
if (!string.IsNullOrEmpty(_email))
|
||||||
|
{
|
||||||
|
if (await UserService.ForgotUsernameAsync(_email))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.ForgotUsername"], MessageType.Info);
|
||||||
|
await logger.LogInformation(LogFunction.Security, "Username Reminder Notification Sent For Email {Email}", _email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Sending Username Reminder {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.ForgotUsername"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePassword()
|
private async Task LoginLink()
|
||||||
{
|
{
|
||||||
if (_passwordtype == "password")
|
try
|
||||||
{
|
{
|
||||||
_passwordtype = "text";
|
if (!string.IsNullOrEmpty(_email))
|
||||||
_togglepassword = SharedLocalizer["HidePassword"];
|
{
|
||||||
|
if (await UserService.SendLoginLinkAsync(_email, _returnurl))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.SendLoginLink"], MessageType.Info);
|
||||||
|
await logger.LogInformation(LogFunction.Security, "Login Link Sent To Email {Email}", _email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_passwordtype = "password";
|
await logger.LogError(ex, "Error Sending Login Link {Error}", ex.Message);
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
AddModuleMessage(Localizer["Error.SendLoginLink"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExternalLogin()
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
if (firstRender && PageState.QueryString.ContainsKey("options"))
|
||||||
}
|
{
|
||||||
|
// user has initiated a passkey login
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
var credential = await interop.RequestCredential(WebUtility.UrlDecode(PageState.QueryString["options"]));
|
||||||
|
if (!string.IsNullOrEmpty(credential))
|
||||||
|
{
|
||||||
|
// post back to the Passkey page so that the cookies are set correctly
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, operation = "login", credential = credential, returnurl = _returnurl };
|
||||||
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/passkey/");
|
||||||
|
await interop.SubmitForm(url, fields);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogError("Passkey Login Was Not Successful");
|
||||||
|
AddModuleMessage(Localizer["Error.Passkey.Fail"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Passkey Login Was Not Successful");
|
||||||
|
AddModuleMessage(Localizer["Error.Passkey.Fail"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstRender && PageState.User == null && _allowsitelogin && _action == "Login")
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_username))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(username.Id)) // ensure username is visible in UI
|
||||||
|
{
|
||||||
|
await username.FocusAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(password.Id)) // ensure password is visible in UI
|
||||||
|
{
|
||||||
|
await password.FocusAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect logged in user to specified page
|
||||||
|
if (PageState.User != null && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(_returnurl);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
var log = await LogService.GetLogAsync(_logId);
|
var log = await LogService.GetLogAsync(_logId);
|
||||||
if (log != null)
|
if (log != null)
|
||||||
{
|
{
|
||||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
_logDate = UtcToLocal(log.LogDate).Value.ToString(CultureInfo.CurrentCulture);
|
||||||
_level = log.Level;
|
_level = log.Level;
|
||||||
_feature = log.Feature;
|
_feature = log.Feature;
|
||||||
_function = log.Function;
|
_function = log.Function;
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ else
|
|||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Text="Details" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
||||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
<td class="@GetClass(context.Function)">@UtcToLocal(context.LogDate)</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Function</td>
|
<td class="@GetClass(context.Function)">@context.Function</td>
|
||||||
@@ -86,47 +86,48 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||||
|
<ActionDialog Header="Clear Events" Message="Are You Sure You Wish To Remove All Log Events?" Action="DeleteLogs" Class="btn btn-danger" OnClick="@(async () => await DeleteLogs())" ResourceKey="DeleteLogs" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _level = "-";
|
private string _level = "-";
|
||||||
private string _function = "-";
|
private string _function = "-";
|
||||||
private string _rows = "10";
|
private string _rows = "10";
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private List<Log> _logs;
|
private List<Log> _logs;
|
||||||
private int _retention = 30;
|
private int _retention = 30;
|
||||||
|
|
||||||
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (UrlParameters.ContainsKey("level"))
|
if (UrlParameters.ContainsKey("level"))
|
||||||
{
|
{
|
||||||
_level = UrlParameters["level"];
|
_level = UrlParameters["level"];
|
||||||
}
|
}
|
||||||
if (UrlParameters.ContainsKey("function"))
|
if (UrlParameters.ContainsKey("function"))
|
||||||
{
|
{
|
||||||
_function = UrlParameters["function"];
|
_function = UrlParameters["function"];
|
||||||
}
|
}
|
||||||
if (UrlParameters.ContainsKey("rows"))
|
if (UrlParameters.ContainsKey("rows"))
|
||||||
{
|
{
|
||||||
_rows = UrlParameters["rows"];
|
_rows = UrlParameters["rows"];
|
||||||
}
|
}
|
||||||
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
|
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
|
||||||
{
|
{
|
||||||
_page = page;
|
_page = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GetLogs();
|
await GetLogs();
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
|
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -213,22 +214,37 @@ else
|
|||||||
return classname;
|
return classname;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveSiteSettings()
|
private async Task SaveSiteSettings()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
|
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteLogs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await LogService.DeleteLogsAsync(PageState.Site.SiteId);
|
||||||
|
await GetLogs();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Logs {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.DeleteLogs"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnPageChange(int page)
|
private void OnPageChange(int page)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,13 +101,20 @@
|
|||||||
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
||||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
||||||
<br />
|
<br />
|
||||||
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
@if (_moduledefinitions.Exists(item => item.PackageName == context.PackageId))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
<button type="button" class="btn btn-info">@SharedLocalizer["Installed"]</button>
|
||||||
}
|
}
|
||||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
else
|
||||||
{
|
{
|
||||||
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
|
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||||
|
}
|
||||||
|
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||||
|
{
|
||||||
|
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
@@ -125,7 +132,7 @@
|
|||||||
<TabPanel Name="Upload" ResourceKey="Upload" Heading="Upload">
|
<TabPanel Name="Upload" ResourceKey="Upload" Heading="Upload">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label>
|
<Label Class="col-sm-3" HelpText="Upload one or more module packages." ResourceKey="Module">Module: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
|
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
|
||||||
</div>
|
</div>
|
||||||
@@ -171,6 +178,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
|
private List<ModuleDefinition> _moduledefinitions;
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
@@ -187,7 +195,8 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadModuleDefinitions();
|
_moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
|
await LoadPackages();
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -197,24 +206,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadModuleDefinitions()
|
private async Task LoadPackages()
|
||||||
{
|
{
|
||||||
ShowProgressIndicator();
|
ShowProgressIndicator();
|
||||||
|
|
||||||
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
|
||||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "", _sort);
|
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "", _sort);
|
||||||
|
|
||||||
if (_packages != null)
|
|
||||||
{
|
|
||||||
foreach (Package package in _packages.ToArray())
|
|
||||||
{
|
|
||||||
if (moduledefinitions.Exists(item => item.PackageName == package.PackageId))
|
|
||||||
{
|
|
||||||
_packages.Remove(package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HideProgressIndicator();
|
HideProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,25 +217,25 @@
|
|||||||
{
|
{
|
||||||
_price = price;
|
_price = price;
|
||||||
_sort = "popularity";
|
_sort = "popularity";
|
||||||
await LoadModuleDefinitions();
|
await LoadPackages();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Search()
|
private async Task Search()
|
||||||
{
|
{
|
||||||
await LoadModuleDefinitions();
|
await LoadPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reset()
|
private async Task Reset()
|
||||||
{
|
{
|
||||||
_page = 1;
|
_page = 1;
|
||||||
_search = "";
|
_search = "";
|
||||||
await LoadModuleDefinitions();
|
await LoadPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
{
|
{
|
||||||
await LoadModuleDefinitions();
|
await LoadPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPageChange(int page)
|
private void OnPageChange(int page)
|
||||||
@@ -251,7 +246,7 @@
|
|||||||
private async void SortChanged(ChangeEventArgs e)
|
private async void SortChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_sort = (string)e.Value;
|
_sort = (string)e.Value;
|
||||||
await LoadModuleDefinitions();
|
await LoadPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideModal()
|
private void HideModal()
|
||||||
@@ -310,6 +305,6 @@
|
|||||||
|
|
||||||
private void OnUpload()
|
private void OnUpload()
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Download"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Upload"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Text.RegularExpressions
|
@using System.Text.RegularExpressions
|
||||||
|
@using System.Reflection
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
@inject IModuleService ModuleService
|
@inject IModuleService ModuleService
|
||||||
@@ -13,13 +14,13 @@
|
|||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
|
<Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation or the word oqtane." ResourceKey="OwnerName">Owner Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="owner" class="form-control" @bind="@_owner" required />
|
<input id="owner" class="form-control" @bind="@_owner" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
|
<Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation or the word oqtane." ResourceKey="ModuleName">Module Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="module" class="form-control" @bind="@_module" required />
|
<input id="module" class="form-control" @bind="@_module" required />
|
||||||
</div>
|
</div>
|
||||||
@@ -27,7 +28,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
|
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000" required></textarea>
|
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -42,29 +43,32 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_type == "External")
|
||||||
<Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="reference" class="form-select" @bind="@_reference" required>
|
|
||||||
@foreach (string version in _versions)
|
|
||||||
{
|
|
||||||
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
|
|
||||||
{
|
|
||||||
<option value="@(version)">@(version)</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<option value="local">@SharedLocalizer["LocalVersion"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (!string.IsNullOrEmpty(_location))
|
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
|
<Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="module" class="form-control" @bind="@_location" readonly />
|
<select id="reference" class="form-select" @bind="@_reference" required>
|
||||||
|
@foreach (string version in _versions)
|
||||||
|
{
|
||||||
|
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
|
||||||
|
{
|
||||||
|
<option value="@(version)">@(version)</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<option value="local">@SharedLocalizer["LocalVersion"]</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (!string.IsNullOrEmpty(_location))
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="module" class="form-control" @bind="@_location" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button>
|
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button>
|
||||||
@@ -80,9 +84,10 @@
|
|||||||
private string _description = string.Empty;
|
private string _description = string.Empty;
|
||||||
private List<Template> _templates;
|
private List<Template> _templates;
|
||||||
private string _template = "-";
|
private string _template = "-";
|
||||||
|
private string _minversion = "2.0.0";
|
||||||
|
private string _type = "";
|
||||||
private string[] _versions;
|
private string[] _versions;
|
||||||
private string _reference = "local";
|
private string _reference = "local";
|
||||||
private string _minversion = "2.0.0";
|
|
||||||
private string _location = string.Empty;
|
private string _location = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
@@ -93,6 +98,16 @@
|
|||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info);
|
AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var entryAssemblyName = Assembly.GetEntryAssembly().GetName().Name;
|
||||||
|
if (entryAssemblyName.EndsWith(".Oqtane"))
|
||||||
|
{
|
||||||
|
// Oqtane Application assemblies end with .Server.Oqtane or .Client.Oqtane
|
||||||
|
string[] segments = entryAssemblyName.Split('.');
|
||||||
|
_owner = string.Join(".", segments, 0, segments.Length - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
@@ -111,6 +126,8 @@
|
|||||||
private async Task CreateModule()
|
private async Task CreateModule()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
|
_owner = _owner.Trim();
|
||||||
|
_module = _module.Trim();
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
@@ -118,13 +135,21 @@
|
|||||||
{
|
{
|
||||||
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(_description)) _description = _module;
|
||||||
if (IsValidXML(_description))
|
if (IsValidXML(_description))
|
||||||
{
|
{
|
||||||
|
if (_type == "Internal")
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Success.Module.Create.Internal"], MessageType.Success);
|
||||||
|
}
|
||||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference, ModuleDefinitionName = template.Namespace };
|
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference, ModuleDefinitionName = template.Namespace };
|
||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
GetLocation();
|
if (_type == "External")
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
{
|
||||||
|
GetLocation();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Module.Create.External"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -150,7 +175,7 @@
|
|||||||
private bool IsValid(string name)
|
private bool IsValid(string name)
|
||||||
{
|
{
|
||||||
// must contain letters, underscores and digits and first character must be letter or underscore
|
// must contain letters, underscores and digits and first character must be letter or underscore
|
||||||
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_.]*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidXML(string description)
|
private bool IsValidXML(string description)
|
||||||
@@ -162,11 +187,16 @@
|
|||||||
private void TemplateChanged(ChangeEventArgs e)
|
private void TemplateChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_template = (string)e.Value;
|
_template = (string)e.Value;
|
||||||
_minversion = "2.0.0";
|
|
||||||
if (_template != "-")
|
if (_template != "-")
|
||||||
{
|
{
|
||||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||||
_minversion = template.Version;
|
_minversion = template.Version;
|
||||||
|
_type = template.Type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_minversion = "2.0.0";
|
||||||
|
_type = "";
|
||||||
}
|
}
|
||||||
GetLocation();
|
GetLocation();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using Microsoft.AspNetCore.Localization
|
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
@inject IPackageService PackageService
|
@inject IPackageService PackageService
|
||||||
@inject ILanguageService LanguageService
|
@inject ILanguageService LanguageService
|
||||||
@@ -10,11 +9,12 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject IPageModuleService PageModuleService
|
@inject IPageModuleService PageModuleService
|
||||||
@inject IModuleService ModuleService
|
@inject IModuleService ModuleService
|
||||||
|
@inject IPageService PageService
|
||||||
|
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Definition" ResourceKey="Definition" Heading="Definition">
|
<TabPanel Name="Module" ResourceKey="Module" Heading="Module">
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
|
<Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="categories" class="form-control" @bind="@_categories" maxlength="200" required />
|
<input id="categories" class="form-control" @bind="@_categories" maxlength="200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -63,24 +63,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
|
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (!string.IsNullOrEmpty(_packagename))
|
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||||
{
|
|
||||||
<div class="input-group">
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
@if (string.IsNullOrEmpty(_packageurl))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -90,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
|
<Label Class="col-sm-3" For="url" HelpText="The url of the module" ResourceKey="Url">Url: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="url" class="form-control" @bind="@_url" disabled />
|
<input id="url" class="form-control" @bind="@_url" disabled />
|
||||||
</div>
|
</div>
|
||||||
@@ -244,7 +227,6 @@
|
|||||||
private string _moduledefinitionname = "";
|
private string _moduledefinitionname = "";
|
||||||
private string _version;
|
private string _version;
|
||||||
private string _packagename = "";
|
private string _packagename = "";
|
||||||
private string _packageurl = "";
|
|
||||||
private string _owner = "";
|
private string _owner = "";
|
||||||
private string _url = "";
|
private string _url = "";
|
||||||
private string _contact = "";
|
private string _contact = "";
|
||||||
@@ -254,11 +236,10 @@
|
|||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
private DateTime _modifiedon;
|
private DateTime _modifiedon;
|
||||||
private List<Page> _pagesWithModules;
|
|
||||||
|
|
||||||
#pragma warning disable 649
|
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
#pragma warning restore 649
|
|
||||||
|
private List<Page> _pagesWithModules;
|
||||||
|
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
@@ -306,15 +287,16 @@
|
|||||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group modules by PageId
|
// get distinct pages where module exists
|
||||||
// Get distinct PageIds where modules are present
|
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||||
var distinctPageIds = PageState.Modules
|
var distinctPageIds = modules
|
||||||
.Where(md => md.ModuleDefinition.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
.Where(md => md.ModuleDefinition?.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
||||||
.Select(md => md.PageId)
|
.Select(md => md.PageId)
|
||||||
.Distinct();
|
.Distinct();
|
||||||
|
|
||||||
// Filter and retrieve the corresponding pages
|
// retrieve the pages which contain the module
|
||||||
_pagesWithModules = PageState.Pages
|
var pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
_pagesWithModules = pages
|
||||||
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
|
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -444,27 +426,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidatePackage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
|
|
||||||
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_packageurl = package.PackageUrl;
|
|
||||||
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
|
||||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject IModuleService ModuleService
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
@inject IPackageService PackageService
|
@inject IPackageService PackageService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@@ -12,32 +13,32 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-3 align-items-center">
|
<div class="row mb-3 align-items-center">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||||
@((MarkupString)" ")
|
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary ms-1" />
|
||||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
<button type="button" class="btn btn-secondary ms-1" @onclick="@Synchronize">@Localizer["Synchronize"]</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||||
@foreach (var category in _categories)
|
@foreach (var category in _categories)
|
||||||
{
|
{
|
||||||
if (category == _category)
|
if (category == _category)
|
||||||
{
|
{
|
||||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<option value="@category">@category @Localizer["Modules"]</option>
|
<option value="@category">@category @Localizer["Modules"]</option>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pager Items="@_moduleDefinitions">
|
<Pager Items="@_moduleDefinitions">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@@ -50,7 +51,7 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != Constants.ClientId)
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
@@ -60,17 +61,17 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.IsEnabled)
|
@if (context.IsEnabled)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["No"]</span>
|
<span>@SharedLocalizer["No"]</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName == Constants.ClientId || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
@@ -86,9 +87,9 @@ else
|
|||||||
@((MarkupString)PurchaseLink(context.PackageName))
|
@((MarkupString)PurchaseLink(context.PackageName))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@{
|
@{
|
||||||
var version = UpgradeAvailable(context.PackageName, context.Version);
|
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||||
}
|
}
|
||||||
@if (version != context.Version)
|
@if (version != context.Version)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||||
@@ -99,6 +100,7 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private List<Module> _modules;
|
||||||
private List<ModuleDefinition> _allModuleDefinitions;
|
private List<ModuleDefinition> _allModuleDefinitions;
|
||||||
private List<ModuleDefinition> _moduleDefinitions;
|
private List<ModuleDefinition> _moduleDefinitions;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
@@ -111,6 +113,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||||
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
@@ -150,10 +153,10 @@ else
|
|||||||
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
|
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SupportLink(string packagename, string version)
|
private string SupportLink(string packagename, string version)
|
||||||
{
|
{
|
||||||
@@ -169,52 +172,75 @@ else
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string UpgradeAvailable(string packagename, string version)
|
private string UpgradeAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
{
|
{
|
||||||
return package.Version;
|
return package.Version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadModule(string packagename, string version)
|
private async Task DownloadModule(string packagename, string version)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(packagename, version);
|
await PackageService.DownloadPackageAsync(packagename, version);
|
||||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CategoryChanged(ChangeEventArgs e)
|
private async Task CategoryChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_category = (string)e.Value;
|
_category = (string)e.Value;
|
||||||
await LoadModuleDefinitions();
|
await LoadModuleDefinitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Synchronize()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ShowProgressIndicator();
|
||||||
|
foreach (var moduleDefinition in _moduleDefinitions)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(moduleDefinition.PackageName) && !_packages.Any(item => item.PackageId == moduleDefinition.PackageName))
|
||||||
|
{
|
||||||
|
var package = await PackageService.GetPackageAsync(moduleDefinition.PackageName, moduleDefinition.Version, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HideProgressIndicator();
|
||||||
|
AddModuleMessage(Localizer["Success.Module.Synchronize"], MessageType.Success);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Synchronizing Modules {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Synchronize"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,24 +5,57 @@
|
|||||||
@inject IStringLocalizer<Export> Localizer
|
@inject IStringLocalizer<Export> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="container">
|
<TabStrip>
|
||||||
<div class="row mb-1 align-items-center">
|
<TabPanel Name="Content" Heading="Content" ResourceKey="Content">
|
||||||
<Label Class="col-sm-3" For="content" HelpText="The Exported Module Content" ResourceKey="Content">Content: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<textarea id="content" class="form-control" @bind="@_content" rows="5" readonly></textarea>
|
<Label Class="col-sm-3" For="content" HelpText="Select the Export option and you will be able to view the module content" ResourceKey="Content">Content: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="content" class="form-control" @bind="@_content" rows="5" readonly></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<br />
|
||||||
</div>
|
<button type="button" class="btn btn-success" @onclick="ExportText">@Localizer["Export"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="File" Heading="File" ResourceKey="File">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="folder" HelpText="Select a folder where you wish to save the exported content" ResourceKey="Folder">Folder: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<FileManager ShowFiles="false" ShowUpload="false" @ref="_filemanager" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="filename" HelpText="Specify a name for the file (without an extension)" ResourceKey="Filename">Filename: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="content" type="text" class="form-control" @bind="@_filename" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="ExportFile">@Localizer["Export"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _content = string.Empty;
|
private string _content = string.Empty;
|
||||||
|
private FileManager _filemanager;
|
||||||
|
private string _filename = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Export Content";
|
public override string Title => "Export Content";
|
||||||
|
|
||||||
private async Task ExportModule()
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_filename = Utilities.GetFriendlyUrl(ModuleState.Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExportText()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -35,4 +68,34 @@
|
|||||||
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ExportFile()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var folderid = _filemanager.GetFolderId();
|
||||||
|
if (folderid != -1 && !string.IsNullOrEmpty(_filename))
|
||||||
|
{
|
||||||
|
var fileid = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, folderid, _filename);
|
||||||
|
if (fileid != -1)
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Content.Export"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Exporting Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,29 @@
|
|||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IModuleService ModuleService
|
@inject IModuleService ModuleService
|
||||||
|
@inject IFileService FileService
|
||||||
@inject IStringLocalizer<Import> Localizer
|
@inject IStringLocalizer<Import> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="content" HelpText="Enter The Module Content To Import" ResourceKey="Content">Content: </Label>
|
<Label Class="col-sm-3" For="file" HelpText="Optionally upload or select a file to import for this module" ResourceKey="File">File: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<FileManager Filter="json" OnSelectFile="OnSelectFile" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="content" HelpText="Provide the module content to import" ResourceKey="Content">Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="content" class="form-control" @bind="@_content" rows="5" required></textarea>
|
<textarea id="content" class="form-control" @bind="@_content" rows="5" required></textarea>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
|
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -28,6 +35,12 @@
|
|||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Import Content";
|
public override string Title => "Import Content";
|
||||||
|
|
||||||
|
private async Task OnSelectFile(int fileId)
|
||||||
|
{
|
||||||
|
var bytes = await FileService.DownloadFileAsync(fileId);
|
||||||
|
_content = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ImportModule()
|
private async Task ImportModule()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
|
|||||||
@@ -3,121 +3,156 @@
|
|||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
|
@inject IPageService PageService
|
||||||
@inject IModuleService ModuleService
|
@inject IModuleService ModuleService
|
||||||
@inject IPageModuleService PageModuleService
|
@inject IPageModuleService PageModuleService
|
||||||
@inject IStringLocalizer<Settings> Localizer
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
@if (_initialized)
|
||||||
<TabStrip>
|
{
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
@if (_containers != null)
|
<TabStrip ActiveTab="@_activetab">
|
||||||
{
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
<div class="container">
|
@if (_containers != null)
|
||||||
<div class="row mb-1 align-items-center">
|
{
|
||||||
<Label Class="col-sm-3" For="module" HelpText="The name of the module" ResourceKey="Module">Module: </Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="module" type="text" class="form-control" @bind="@_module" disabled />
|
<Label Class="col-sm-3" For="module" HelpText="The name of the module" ResourceKey="Module">Module: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="module" type="text" class="form-control" @bind="@_module" disabled />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="row mb-1 align-items-center">
|
<Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
|
||||||
<Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
|
<div class="col-sm-9">
|
||||||
<div class="col-sm-9">
|
<input id="title" type="text" class="form-control" @bind="@_title" required />
|
||||||
<input id="title" type="text" class="form-control" @bind="@_title" required />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="row mb-1 align-items-center">
|
<Label Class="col-sm-3" For="pane" HelpText="The pane where the module will be displayed" ResourceKey="Pane">Pane: </Label>
|
||||||
<Label Class="col-sm-3" For="pane" HelpText="The pane where the module will be displayed" ResourceKey="Pane">Pane: </Label>
|
<div class="col-sm-9">
|
||||||
<div class="col-sm-9">
|
<select class="form-select" @bind="@_pane">
|
||||||
<select class="form-select" @bind="@_pane">
|
@foreach (string pane in PageState.Page.Panes)
|
||||||
@foreach (string pane in PageState.Page.Panes)
|
|
||||||
{
|
|
||||||
<option value="@pane">@pane Pane</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="container" class="form-select" @bind="@_containerType" required>
|
|
||||||
@foreach (var container in _containers)
|
|
||||||
{
|
|
||||||
<option value="@container.TypeName">@container.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="allpages" class="form-select" @bind="@_allPages" required>
|
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="page" class="form-select" @bind="@_pageId" required>
|
|
||||||
@if (PageState.Page.UserId != null)
|
|
||||||
{
|
|
||||||
<option value="@PageState.Page.PageId">@(PageState.Page.Name)</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (Page p in PageState.Pages)
|
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, p.PermissionList))
|
<option value="@pane">@pane Pane</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="container" class="form-select" @bind="@_containerType" required>
|
||||||
|
@foreach (var container in _containers)
|
||||||
|
{
|
||||||
|
<option value="@container.TypeName">@container.Name</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this module is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="effectiveDate" class="form-control" @bind="@_effectivedate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this module expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="allpages" class="form-select" @bind="@_allPages" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="page" class="form-select" @bind="@_pageId" required>
|
||||||
|
@if (PageState.Page.UserId != null)
|
||||||
|
{
|
||||||
|
<option value="@PageState.Page.PageId">@(PageState.Page.Name)</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_pages != null)
|
||||||
{
|
{
|
||||||
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
|
foreach (Page p in _pages)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, p.PermissionList))
|
||||||
|
{
|
||||||
|
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</select>
|
||||||
}
|
</div>
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<br />
|
||||||
}
|
<Section Name="ModuleContent" Heading="Content" ResourceKey="ModuleContent">
|
||||||
</TabPanel>
|
<div class="container">
|
||||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
<div class="row mb-1 align-items-center">
|
||||||
@if (_permissions != null)
|
<Label Class="col-sm-3" For="moduleheader" HelpText="Optionally provide content to be injected above the module instance" ResourceKey="Header">Header: </Label>
|
||||||
{
|
<div class="col-sm-9">
|
||||||
<div class="container">
|
<textarea id="moduleheader" class="form-control" @bind="@_header" rows="3" maxlength="4000"></textarea>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="modulefooter" HelpText="Optionally provide content to be injected below the module instance" ResourceKey="Footer">Footer: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="modulefooter" class="form-control" @bind="@_footer" rows="3" maxlength="4000"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
|
||||||
|
@if (_permissions != null)
|
||||||
|
{
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
@if (_moduleSettingsType != null)
|
||||||
|
{
|
||||||
|
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
|
||||||
|
@ModuleSettingsComponent
|
||||||
|
</TabPanel>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
@if (_containerSettingsType != null)
|
||||||
@if (_moduleSettingsType != null)
|
{
|
||||||
{
|
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
|
||||||
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
|
@ContainerSettingsComponent
|
||||||
@ModuleSettingsComponent
|
</TabPanel>
|
||||||
</TabPanel>
|
}
|
||||||
}
|
</TabStrip>
|
||||||
@if (_containerSettingsType != null)
|
<br />
|
||||||
{
|
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
|
||||||
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
@ContainerSettingsComponent
|
<br />
|
||||||
</TabPanel>
|
<br />
|
||||||
}
|
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||||
</TabStrip>
|
</form>
|
||||||
<br />
|
}
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Module Settings";
|
|
||||||
|
|
||||||
|
private bool _initialized = false;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
@@ -126,13 +161,15 @@
|
|||||||
private string _pane;
|
private string _pane;
|
||||||
private string _containerType;
|
private string _containerType;
|
||||||
private string _allPages = "false";
|
private string _allPages = "false";
|
||||||
|
private string _header = "";
|
||||||
|
private string _footer = "";
|
||||||
private string _permissionNames = "";
|
private string _permissionNames = "";
|
||||||
private List<Permission> _permissions = null;
|
private List<Permission> _permissions = null;
|
||||||
private string _pageId;
|
private string _pageId;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _moduleSettingsType;
|
private Type _moduleSettingsType;
|
||||||
private object _moduleSettings;
|
private object _moduleSettings;
|
||||||
private string _moduleSettingsTitle = "Module Settings";
|
private string _moduleSettingsTitle;
|
||||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||||
private Type _containerSettingsType;
|
private Type _containerSettingsType;
|
||||||
private object _containerSettings;
|
private object _containerSettings;
|
||||||
@@ -141,35 +178,55 @@
|
|||||||
private DateTime createdon;
|
private DateTime createdon;
|
||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
|
private DateTime? _effectivedate = null;
|
||||||
|
private DateTime? _expirydate = null;
|
||||||
|
private List<Page> _pages;
|
||||||
|
private string _activetab = "";
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_module = ModuleState.ModuleDefinition.Name;
|
SetModuleTitle(Localizer["ModuleSettings.Title"]);
|
||||||
_title = ModuleState.Title;
|
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
|
||||||
_pane = ModuleState.Pane;
|
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
||||||
_containerType = ModuleState.ContainerType;
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
_allPages = ModuleState.AllPages.ToString();
|
|
||||||
_permissions = ModuleState.PermissionList;
|
|
||||||
_pageId = ModuleState.PageId.ToString();
|
|
||||||
createdby = ModuleState.CreatedBy;
|
|
||||||
createdon = ModuleState.CreatedOn;
|
|
||||||
modifiedby = ModuleState.ModifiedBy;
|
|
||||||
modifiedon = ModuleState.ModifiedOn;
|
|
||||||
|
|
||||||
if (ModuleState.ModuleDefinition != null)
|
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||||
|
|
||||||
|
_pageId = pagemodule.PageId.ToString();
|
||||||
|
_title = pagemodule.Title;
|
||||||
|
_pane = pagemodule.Pane;
|
||||||
|
_containerType = pagemodule.ContainerType;
|
||||||
|
if (string.IsNullOrEmpty(_containerType))
|
||||||
{
|
{
|
||||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
_containerType = (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType)) ? PageState.Page.DefaultContainerType : PageState.Site.DefaultContainerType;
|
||||||
|
}
|
||||||
|
_header = pagemodule.Header;
|
||||||
|
_footer = pagemodule.Footer;
|
||||||
|
_effectivedate = Utilities.UtcAsLocalDate(pagemodule.EffectiveDate);
|
||||||
|
_expirydate = Utilities.UtcAsLocalDate(pagemodule.ExpiryDate);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
_allPages = pagemodule.Module.AllPages.ToString();
|
||||||
|
createdby = pagemodule.Module.CreatedBy;
|
||||||
|
createdon = pagemodule.Module.CreatedOn;
|
||||||
|
modifiedby = pagemodule.Module.ModifiedBy;
|
||||||
|
modifiedon = pagemodule.Module.ModifiedOn;
|
||||||
|
_permissions = pagemodule.Module.PermissionList;
|
||||||
|
|
||||||
|
if (pagemodule.Module.ModuleDefinition != null)
|
||||||
|
{
|
||||||
|
_module = pagemodule.Module.ModuleDefinition.Name;
|
||||||
|
_permissionNames = pagemodule.Module.ModuleDefinition?.PermissionNames;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(pagemodule.Module.ModuleDefinition.SettingsType))
|
||||||
{
|
{
|
||||||
// module settings type explicitly declared in IModule interface
|
// module settings type explicitly declared in IModule interface
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
_moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.SettingsType);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
_moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||||
}
|
}
|
||||||
if (_moduleSettingsType != null)
|
if (_moduleSettingsType != null)
|
||||||
{
|
{
|
||||||
@@ -182,14 +239,15 @@
|
|||||||
ModuleSettingsComponent = builder =>
|
ModuleSettingsComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _moduleSettingsType);
|
builder.OpenComponent(0, _moduleSettingsType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||||
|
builder.AddComponentReferenceCapture(2, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], pagemodule.Module.ModuleDefinitionName), MessageType.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||||
@@ -201,42 +259,56 @@
|
|||||||
ContainerSettingsComponent = builder =>
|
ContainerSettingsComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _containerSettingsType);
|
builder.OpenComponent(0, _containerSettingsType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||||
|
builder.AddComponentReferenceCapture(2, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveModule()
|
private async Task SaveModule()
|
||||||
{
|
{
|
||||||
|
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_title))
|
if (!string.IsNullOrEmpty(_title))
|
||||||
{
|
{
|
||||||
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||||
pagemodule.PageId = int.Parse(_pageId);
|
pagemodule.PageId = int.Parse(_pageId);
|
||||||
pagemodule.Title = _title;
|
pagemodule.Title = _title;
|
||||||
pagemodule.Pane = _pane;
|
pagemodule.Pane = _pane;
|
||||||
|
pagemodule.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||||
|
pagemodule.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||||
{
|
{
|
||||||
pagemodule.ContainerType = string.Empty;
|
pagemodule.ContainerType = string.Empty;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
||||||
{
|
{
|
||||||
pagemodule.ContainerType = string.Empty;
|
pagemodule.ContainerType = string.Empty;
|
||||||
}
|
}
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
pagemodule.Header = _header;
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
pagemodule.Footer = _footer;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
|
||||||
var module = ModuleState;
|
var module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
||||||
module.AllPages = bool.Parse(_allPages);
|
module.AllPages = bool.Parse(_allPages);
|
||||||
module.PageModuleId = ModuleState.PageModuleId;
|
module.PageModuleId = ModuleState.PageModuleId;
|
||||||
module.PermissionList = _permissionGrid.GetPermissionList();
|
module.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
await ModuleService.UpdateModuleAsync(module);
|
await ModuleService.UpdateModuleAsync(module);
|
||||||
|
|
||||||
if (_moduleSettingsType != null)
|
if (_moduleSettingsType != null)
|
||||||
{
|
{
|
||||||
@@ -257,17 +329,18 @@
|
|||||||
await containerSettingsControl.UpdateSettings();
|
await containerSettingsControl.UpdateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_name" required />
|
<input id="name" class="form-control" @bind="@_name" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in PageState.Pages)
|
@foreach (Page page in _pages)
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
|
||||||
{
|
{
|
||||||
@@ -81,42 +81,21 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
|
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="navigation" class="form-select" @bind="@_isnavigation" required>
|
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="clickable" class="form-select" @bind="@_isclickable" required>
|
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="path" class="form-control" @bind="@_path" />
|
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="url" class="form-control" @bind="@_url" />
|
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-1">
|
|
||||||
<i class="@_icon"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -129,22 +108,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Section Name="Theme" Heading="Theme" ResourceKey="Theme">
|
||||||
<Section Name="Appearance" ResourceKey="Appearance" Heading=@Localizer["Appearance.Name"]>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="title" class="form-control" @bind="@_title" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,18 +141,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<i class="@_icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="navigation" class="form-select" @bind="@_isnavigation" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="clickable" class="form-select" @bind="@_isclickable" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this page is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="effectiveDate" class="form-control" @bind="@_effectivedate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this page expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
<Section Name="PageContent" ResourceKey="PageContent" Heading=@Localizer["PageContent.Heading"]>
|
<Section Name="PageContent" ResourceKey="PageContent" Heading=@Localizer["PageContent.Heading"]>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -186,12 +208,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@if (_themeSettingsType != null)
|
|
||||||
{
|
|
||||||
<TabPanel Name="ThemeSettings" Heading=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
|
|
||||||
@ThemeSettingsComponent
|
|
||||||
</TabPanel>
|
|
||||||
}
|
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
@@ -207,6 +223,7 @@
|
|||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
|
private List<Page> _pages;
|
||||||
private int _pageId;
|
private int _pageId;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _parentid = "-1";
|
private string _parentid = "-1";
|
||||||
@@ -226,18 +243,19 @@
|
|||||||
private string _bodycontent;
|
private string _bodycontent;
|
||||||
private string _permissions = null;
|
private string _permissions = null;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
private Type _themeSettingsType;
|
|
||||||
private object _themeSettings;
|
|
||||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
|
||||||
private bool _refresh = false;
|
private bool _refresh = false;
|
||||||
protected Page _parent = null;
|
protected Page _parent = null;
|
||||||
protected Dictionary<string, string> _icons;
|
protected Dictionary<string, string> _icons;
|
||||||
private string _iconresources = "";
|
private string _iconresources = "";
|
||||||
|
private DateTime? _effectivedate = null;
|
||||||
|
private DateTime? _expirydate = null;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("id"))
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
{
|
{
|
||||||
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
@@ -254,18 +272,27 @@
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
|
||||||
{
|
{
|
||||||
_themetype = PageState.Site.DefaultThemeType;
|
_themetype = PageState.Site.DefaultThemeType;
|
||||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
|
var themes = new List<Theme>();
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
foreach (var theme in PageState.Site.Themes)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, theme.PermissionList))
|
||||||
|
{
|
||||||
|
themes.Add(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_themes = ThemeService.GetThemeControls(themes);
|
||||||
|
_containers = ThemeService.GetContainerControls(themes, _themetype);
|
||||||
_containertype = PageState.Site.DefaultContainerType;
|
_containertype = PageState.Site.DefaultContainerType;
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
_children.Add(p);
|
_children.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThemeSettings();
|
_effectivedate = Utilities.UtcAsLocalDate(PageState.Page.EffectiveDate);
|
||||||
|
_expirydate = Utilities.UtcAsLocalDate(PageState.Page.ExpiryDate);
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -274,20 +301,20 @@
|
|||||||
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Page {Error}", ex.Message);
|
await logger.LogError(ex, "Error Loading Page {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ParentChanged(ChangeEventArgs e)
|
private async void ParentChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_parentid = (string)e.Value;
|
_parentid = (string)e.Value;
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
@@ -295,154 +322,151 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
}
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
_containertype = _containers.First().TypeName;
|
_containertype = _containers.First().TypeName;
|
||||||
ThemeSettings();
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
// if theme chosen is different than default site theme, display warning message to user
|
// if theme chosen is different than default site theme, display warning message to user
|
||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeSettings()
|
private async Task SavePage()
|
||||||
{
|
{
|
||||||
_themeSettingsType = null;
|
validated = true;
|
||||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
var interop = new Interop(JSRuntime);
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
Page page = null;
|
||||||
if (_themeSettingsType != null)
|
try
|
||||||
{
|
{
|
||||||
ThemeSettingsComponent = builder =>
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _themeSettingsType);
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
await ScrollToPageTop();
|
||||||
builder.CloseComponent();
|
return;
|
||||||
};
|
}
|
||||||
}
|
|
||||||
_refresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SavePage()
|
|
||||||
{
|
|
||||||
validated = true;
|
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
if (await interop.FormValid(form))
|
|
||||||
{
|
|
||||||
Page page = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
||||||
{
|
{
|
||||||
page = new Page();
|
page = new Page();
|
||||||
page.SiteId = PageState.Page.SiteId;
|
page.SiteId = PageState.Page.SiteId;
|
||||||
page.Name = _name;
|
page.Name = _name;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
_path = _name;
|
page.ParentId = null;
|
||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
else
|
||||||
{
|
{
|
||||||
if (_path.EndsWith("/") && _path != "/")
|
page.ParentId = Int32.Parse(_parentid);
|
||||||
{
|
}
|
||||||
_path = _path.Substring(0, _path.Length - 1);
|
|
||||||
}
|
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_parentid == "-1")
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
page.ParentId = null;
|
_path = _name;
|
||||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
page.ParentId = Int32.Parse(_parentid);
|
|
||||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
|
||||||
if (parent.Path == string.Empty)
|
|
||||||
{
|
|
||||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
if (_path.Contains("/"))
|
||||||
if (_pages.Any(item => item.Path == page.Path))
|
{
|
||||||
{
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
{
|
||||||
return;
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
}
|
}
|
||||||
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
if (_parentid == "-1")
|
||||||
|
{
|
||||||
|
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Page parent = _pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||||
|
if (parent.Path == string.Empty)
|
||||||
|
{
|
||||||
|
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
if (_pages.Any(item => item.Path == page.Path))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
return;
|
await ScrollToPageTop();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Page child;
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
switch (_insert)
|
{
|
||||||
{
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
case "<<":
|
await ScrollToPageTop();
|
||||||
page.Order = 0;
|
return;
|
||||||
break;
|
}
|
||||||
case "<":
|
|
||||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
|
||||||
page.Order = child.Order - 1;
|
|
||||||
break;
|
|
||||||
case ">":
|
|
||||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
|
||||||
page.Order = child.Order + 1;
|
|
||||||
break;
|
|
||||||
case ">>":
|
|
||||||
page.Order = int.MaxValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
Page child;
|
||||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
switch (_insert)
|
||||||
|
{
|
||||||
|
case "<<":
|
||||||
|
page.Order = 0;
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||||
|
page.Order = child.Order - 1;
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||||
|
page.Order = child.Order + 1;
|
||||||
|
break;
|
||||||
|
case ">>":
|
||||||
|
page.Order = int.MaxValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
|
||||||
|
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||||
page.Url = _url;
|
page.Url = _url;
|
||||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||||
|
page.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||||
|
page.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||||
page.UserId = null;
|
page.UserId = null;
|
||||||
|
|
||||||
// appearance
|
// appearance
|
||||||
page.Title = _title;
|
page.Title = _title;
|
||||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||||
page.ThemeType = _themetype;
|
page.ThemeType = _themetype;
|
||||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||||
{
|
{
|
||||||
page.ThemeType = string.Empty;
|
page.ThemeType = string.Empty;
|
||||||
}
|
}
|
||||||
page.DefaultContainerType = _containertype;
|
page.DefaultContainerType = _containertype;
|
||||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||||
{
|
{
|
||||||
page.DefaultContainerType = string.Empty;
|
page.DefaultContainerType = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// page content
|
// page content
|
||||||
page.HeadContent = _headcontent;
|
page.HeadContent = _headcontent;
|
||||||
page.BodyContent = _bodycontent;
|
page.BodyContent = _bodycontent;
|
||||||
|
|
||||||
// permissions
|
// permissions
|
||||||
page.PermissionList = _permissionGrid.GetPermissionList();
|
page.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
|
|
||||||
page = await PageService.AddPageAsync(page);
|
page = await PageService.AddPageAsync(page);
|
||||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||||
@@ -450,7 +474,7 @@
|
|||||||
await logger.LogInformation("Page Added {Page}", page);
|
await logger.LogInformation("Page Added {Page}", page);
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(page.Path); // redirect to new page
|
NavigationManager.NavigateTo(NavigateUrl(page.Path), true); // redirect to page added and reload
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -460,6 +484,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -467,11 +492,13 @@
|
|||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Pages
|
@namespace Oqtane.Modules.Admin.Pages
|
||||||
@using Oqtane.Interfaces
|
@using Oqtane.Interfaces
|
||||||
|
@using System.Globalization
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IPageService PageService
|
@inject IPageService PageService
|
||||||
@inject IPageModuleService PageModuleService
|
@inject IPageModuleService PageModuleService
|
||||||
@inject IThemeService ThemeService
|
@inject IModuleService ModuleService
|
||||||
|
@inject IThemeService ThemeService
|
||||||
@inject ISystemService SystemService
|
@inject ISystemService SystemService
|
||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
|
<input id="name" class="form-control" @bind="@_name" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
@@ -28,16 +30,16 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
|
<Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in PageState.Pages)
|
@foreach (Page page in _pages)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
{
|
|
||||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</select>
|
}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -96,25 +98,13 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
|
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="navigation" class="form-select" @bind="@_isnavigation" required>
|
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
|
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. Please note that spaces and punctuation will be replaced by a dash. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="clickable" class="form-select" @bind="@_isclickable" required>
|
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||||
</div>
|
</div>
|
||||||
@@ -125,15 +115,6 @@
|
|||||||
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-1">
|
|
||||||
<i class="@_icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
|
<Label Class="col-sm-3" For="personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@@ -144,21 +125,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Section Name="Appearance" ResourceKey="Appearance" Heading="Appearance">
|
<Section Name="Theme" ResourceKey="Theme" Heading="Theme">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,32 +149,81 @@
|
|||||||
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
|
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="container" class="form-select" @bind="@_containertype" required>
|
<select id="container" class="form-select" @bind="@_containertype" required>
|
||||||
@foreach (var container in _containers)
|
@if (_containers != null)
|
||||||
{
|
{
|
||||||
<option value="@container.TypeName">@container.Name</option>
|
foreach (var container in _containers)
|
||||||
|
{
|
||||||
|
<option value="@container.TypeName">@container.Name</option>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Section Name="Appearance" ResourceKey="Appearance" Heading="Appearance">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<i class="@_icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="navigation" class="form-select" @bind="@_isnavigation" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="clickable" class="form-select" @bind="@_isclickable" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this page is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="effectiveDate" class="form-control" @bind="@_effectivedate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this page expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<br />
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@@ -201,15 +232,28 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="updatemodulepermissions" HelpText="Specify if changes made to page permissions should be propagated to the modules on this page" ResourceKey="UpdateModulePermissions">Update Module Permissions? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="updatemodulepermissions" class="form-select" @bind="@_updatemodulepermissions" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
|
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
|
||||||
<Pager Items="_pageModules">
|
<Pager Items="_pageModules">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["ModuleTitle"]</th>
|
<th>@Localizer["ModuleTitle"]</th>
|
||||||
<th>@Localizer["ModuleDefinition"]</th>
|
<th>@Localizer["ModuleDefinition"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Settings" Text="Edit" Path="@_actualpath" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
|
<td><ActionLink Action="Settings" Text="Edit" Path="@_actualpath" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
|
||||||
@@ -222,9 +266,11 @@
|
|||||||
@if (_themeSettingsType != null)
|
@if (_themeSettingsType != null)
|
||||||
{
|
{
|
||||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||||
@ThemeSettingsComponent
|
@_themeSettingsComponent
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<br />
|
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
@@ -239,13 +285,26 @@
|
|||||||
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="path" HelpText="Provide a url path for your personalized page. Please note that spaces and punctuation will be replaced by a dash." ResourceKey="PersonalizedUrlPath">Url Path: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="theme" class="form-select" @bind="@_themetype" required>
|
<select id="theme" class="form-select" @bind="@_themetype" required>
|
||||||
@foreach (var theme in _themes)
|
@foreach (var theme in _themes)
|
||||||
{
|
{
|
||||||
<option value="@theme.TypeName">@theme.Name</option>
|
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@theme.TypeName">@theme.Name</option>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -262,19 +321,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@if (_themeSettingsType != null)
|
@if (_themeSettingsType != null)
|
||||||
{
|
{
|
||||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||||
@ThemeSettingsComponent
|
@_themeSettingsComponent
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<br />
|
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
<br />
|
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +347,7 @@
|
|||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
|
private List<Page> _pages;
|
||||||
private int _pageId;
|
private int _pageId;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _currentparentid;
|
private string _currentparentid;
|
||||||
@@ -305,11 +367,12 @@
|
|||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private Type _themeSettingsType;
|
private Type _themeSettingsType;
|
||||||
private object _themeSettings;
|
private object _themeSettings;
|
||||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
private RenderFragment _themeSettingsComponent { get; set; }
|
||||||
private string _headcontent;
|
private string _headcontent;
|
||||||
private string _bodycontent;
|
private string _bodycontent;
|
||||||
private List<Permission> _permissions = null;
|
private List<Permission> _permissions = null;
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
|
private string _updatemodulepermissions;
|
||||||
private List<Module> _pageModules;
|
private List<Module> _pageModules;
|
||||||
private string _createdby;
|
private string _createdby;
|
||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
@@ -322,11 +385,14 @@
|
|||||||
protected Page _parent = null;
|
protected Page _parent = null;
|
||||||
protected Dictionary<string, string> _icons;
|
protected Dictionary<string, string> _icons;
|
||||||
private string _iconresources = "";
|
private string _iconresources = "";
|
||||||
|
private DateTime? _effectivedate = null;
|
||||||
|
private DateTime? _expirydate = null;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
_page = await PageService.GetPageAsync(_pageId);
|
_page = await PageService.GetPageAsync(_pageId);
|
||||||
_icons = await SystemService.GetIconsAsync();
|
_icons = await SystemService.GetIconsAsync();
|
||||||
@@ -342,10 +408,10 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_parentid = _page.ParentId.ToString();
|
_parentid = _page.ParentId.ToString();
|
||||||
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
_parent = _pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||||
}
|
}
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid, CultureInfo.InvariantCulture))))
|
||||||
{
|
{
|
||||||
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
@@ -370,6 +436,8 @@
|
|||||||
}
|
}
|
||||||
_url = _page.Url;
|
_url = _page.Url;
|
||||||
_icon = _page.Icon;
|
_icon = _page.Icon;
|
||||||
|
_effectivedate = Utilities.UtcAsLocalDate(_page.EffectiveDate);
|
||||||
|
_expirydate = Utilities.UtcAsLocalDate(_page.ExpiryDate);
|
||||||
_ispersonalizable = _page.IsPersonalizable.ToString();
|
_ispersonalizable = _page.IsPersonalizable.ToString();
|
||||||
|
|
||||||
// appearance
|
// appearance
|
||||||
@@ -379,8 +447,16 @@
|
|||||||
{
|
{
|
||||||
_themetype = PageState.Site.DefaultThemeType;
|
_themetype = PageState.Site.DefaultThemeType;
|
||||||
}
|
}
|
||||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
|
var themes = new List<Theme>();
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
foreach (var theme in PageState.Site.Themes)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, theme.PermissionList))
|
||||||
|
{
|
||||||
|
themes.Add(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_themes = ThemeService.GetThemeControls(themes);
|
||||||
|
_containers = ThemeService.GetContainerControls(themes, _themetype);
|
||||||
_containertype = _page.DefaultContainerType;
|
_containertype = _page.DefaultContainerType;
|
||||||
if (string.IsNullOrEmpty(_containertype))
|
if (string.IsNullOrEmpty(_containertype))
|
||||||
{
|
{
|
||||||
@@ -393,9 +469,11 @@
|
|||||||
|
|
||||||
// permissions
|
// permissions
|
||||||
_permissions = _page.PermissionList;
|
_permissions = _page.PermissionList;
|
||||||
|
_updatemodulepermissions = "True";
|
||||||
|
|
||||||
// page modules
|
// page modules
|
||||||
_pageModules = PageState.Modules.Where(m => m.PageId == _page.PageId).ToList();
|
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||||
|
_pageModules = modules.Where(item => item.PageId == _page.PageId && !item.IsDeleted).ToList();
|
||||||
|
|
||||||
// audit
|
// audit
|
||||||
_createdby = _page.CreatedBy;
|
_createdby = _page.CreatedBy;
|
||||||
@@ -427,7 +505,7 @@
|
|||||||
{
|
{
|
||||||
_parentid = (string)e.Value;
|
_parentid = (string)e.Value;
|
||||||
_children = new List<Page>();
|
_children = new List<Page>();
|
||||||
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
|
||||||
{
|
{
|
||||||
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||||
{
|
{
|
||||||
@@ -441,10 +519,11 @@
|
|||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
@@ -456,22 +535,26 @@
|
|||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeSettings()
|
private void ThemeSettings()
|
||||||
{
|
{
|
||||||
_themeSettingsType = null;
|
_themeSettingsType = null;
|
||||||
|
_themeSettingsComponent = null;
|
||||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||||
{
|
{
|
||||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||||
if (_themeSettingsType != null)
|
if (_themeSettingsType != null)
|
||||||
{
|
{
|
||||||
ThemeSettingsComponent = builder =>
|
_themeSettingsComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _themeSettingsType);
|
builder.OpenComponent(0, _themeSettingsType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||||
|
builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -487,16 +570,32 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||||
{
|
{
|
||||||
string currentPath = _page.Path;
|
string currentPath = _page.Path;
|
||||||
|
|
||||||
_page.Name = _name;
|
_page.Name = _name;
|
||||||
|
|
||||||
|
if (_parentid == "-1")
|
||||||
|
{
|
||||||
|
_page.ParentId = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_page.ParentId = Int32.Parse(_parentid);
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_path))
|
if (string.IsNullOrEmpty(_path))
|
||||||
{
|
{
|
||||||
_path = _name;
|
_path = _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
if (_path.EndsWith("/") && _path != "/")
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
@@ -505,16 +604,13 @@
|
|||||||
}
|
}
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_parentid == "-1")
|
if (_parentid == "-1")
|
||||||
{
|
{
|
||||||
_page.ParentId = null;
|
|
||||||
_page.Path = Utilities.GetFriendlyUrl(_path);
|
_page.Path = Utilities.GetFriendlyUrl(_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_page.ParentId = Int32.Parse(_parentid);
|
Page parent = _pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
|
||||||
if (parent.Path == string.Empty)
|
if (parent.Path == string.Empty)
|
||||||
{
|
{
|
||||||
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||||
@@ -525,16 +621,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
|
||||||
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,11 +644,11 @@
|
|||||||
_page.Order = 0;
|
_page.Order = 0;
|
||||||
break;
|
break;
|
||||||
case "<":
|
case "<":
|
||||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
child = _pages.FirstOrDefault(item => item.PageId == _childid);
|
||||||
if (child != null) _page.Order = child.Order - 1;
|
if (child != null) _page.Order = child.Order - 1;
|
||||||
break;
|
break;
|
||||||
case ">":
|
case ">":
|
||||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
child = _pages.FirstOrDefault(item => item.PageId == _childid);
|
||||||
if (child != null) _page.Order = child.Order + 1;
|
if (child != null) _page.Order = child.Order + 1;
|
||||||
break;
|
break;
|
||||||
case ">>":
|
case ">>":
|
||||||
@@ -563,6 +660,8 @@
|
|||||||
_page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
_page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||||
_page.Url = _url;
|
_page.Url = _url;
|
||||||
_page.Icon = _icon ?? string.Empty;
|
_page.Icon = _icon ?? string.Empty;
|
||||||
|
_page.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||||
|
_page.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||||
_page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
_page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||||
|
|
||||||
// appearance
|
// appearance
|
||||||
@@ -586,6 +685,7 @@
|
|||||||
if (_page.UserId == null)
|
if (_page.UserId == null)
|
||||||
{
|
{
|
||||||
_page.PermissionList = _permissionGrid.GetPermissionList();
|
_page.PermissionList = _permissionGrid.GetPermissionList();
|
||||||
|
_page.UpdateModulePermissions = bool.Parse(_updatemodulepermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
_page = await PageService.UpdatePageAsync(_page);
|
_page = await PageService.UpdatePageAsync(_page);
|
||||||
@@ -607,27 +707,30 @@
|
|||||||
await logger.LogInformation("Page Saved {Page}", _page);
|
await logger.LogInformation("Page Saved {Page}", _page);
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
NavigationManager.NavigateTo(PageState.ReturnUrl, true); // redirect to page being edited and reload
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (PageState.Pages != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (_pages != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />
|
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />
|
||||||
|
|
||||||
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
|
<Pager Items="@_pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
||||||
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
|
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
|
||||||
@@ -28,6 +28,21 @@
|
|||||||
@code {
|
@code {
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
|
private List<Page> _pages;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Pages {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeletePage(Page page)
|
private async Task DeletePage(Page page)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IProfileService ProfileService
|
@inject IProfileService ProfileService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label>
|
<Label Class="col-sm-3" For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="order" class="form-control" @bind="@_vieworder" min="0" max="99" type="number" required />
|
<input id="order" class="form-control" @bind="@_vieworder" min="0" max="9999" type="number" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -56,9 +57,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="options" HelpText="A comma delimited list of options the user can select from" ResourceKey="Options">Options: </Label>
|
<Label Class="col-sm-3" For="options" HelpText="A comma delimited list of options. Options can contain a key and value if they are seperated by a colon (ie. key:value). You can also dynamically load your options from Settings." ResourceKey="Options">Options: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="options" class="form-control" @bind="@_options" maxlength="2000" />
|
<div class="input-group">
|
||||||
|
@if (_optiontype == "Settings")
|
||||||
|
{
|
||||||
|
<input id="options" class="form-control" @bind="@_options" maxlength="2000" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<select id="entityName" class="form-select" @bind="@_options">
|
||||||
|
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||||
|
@foreach (var entityname in _entitynames)
|
||||||
|
{
|
||||||
|
<option value="@entityname">@entityname</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleOptionType">@Localizer[_optiontype]</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@@ -76,6 +93,12 @@
|
|||||||
<input id="validation" class="form-control" @bind="@_validation" maxlength="200" />
|
<input id="validation" class="form-control" @bind="@_validation" maxlength="200" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="autocomplete" HelpText="The HTML autocomplete attribute allows you to specify browser behavior for automated user assistance in filling out form field values. Allowable values are blank (default), 'on', 'off', or any value from the standardized taxonomy defined for this attribute." ResourceKey="Autocomplete">Autocomplete: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="autocomplete" class="form-control" @bind="@_autocomplete" maxlength="30" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
|
<Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@@ -110,7 +133,10 @@
|
|||||||
private string _rows = "1";
|
private string _rows = "1";
|
||||||
private string _defaultvalue = string.Empty;
|
private string _defaultvalue = string.Empty;
|
||||||
private string _options = string.Empty;
|
private string _options = string.Empty;
|
||||||
|
private string _optiontype = "Settings";
|
||||||
|
private List<string> _entitynames;
|
||||||
private string _validation = string.Empty;
|
private string _validation = string.Empty;
|
||||||
|
private string _autocomplete = string.Empty;
|
||||||
private string _isrequired = "False";
|
private string _isrequired = "False";
|
||||||
private string _isprivate = "False";
|
private string _isprivate = "False";
|
||||||
private string createdby;
|
private string createdby;
|
||||||
@@ -126,6 +152,8 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_entitynames = await SettingService.GetEntityNamesAsync();
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("id"))
|
if (PageState.QueryString.ContainsKey("id"))
|
||||||
{
|
{
|
||||||
_profileid = Int32.Parse(PageState.QueryString["id"]);
|
_profileid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
@@ -141,7 +169,13 @@
|
|||||||
_rows = profile.Rows.ToString();
|
_rows = profile.Rows.ToString();
|
||||||
_defaultvalue = profile.DefaultValue;
|
_defaultvalue = profile.DefaultValue;
|
||||||
_options = profile.Options;
|
_options = profile.Options;
|
||||||
|
if (_options.StartsWith("EntityName:"))
|
||||||
|
{
|
||||||
|
_optiontype = "Options";
|
||||||
|
_options = _options.Substring(11);
|
||||||
|
}
|
||||||
_validation = profile.Validation;
|
_validation = profile.Validation;
|
||||||
|
_autocomplete = profile.Autocomplete;
|
||||||
_isrequired = profile.IsRequired.ToString();
|
_isrequired = profile.IsRequired.ToString();
|
||||||
_isprivate = profile.IsPrivate.ToString();
|
_isprivate = profile.IsPrivate.ToString();
|
||||||
createdby = profile.CreatedBy;
|
createdby = profile.CreatedBy;
|
||||||
@@ -158,6 +192,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleOptionType()
|
||||||
|
{
|
||||||
|
if (_optiontype == "Options")
|
||||||
|
{
|
||||||
|
_optiontype = "Settings";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_optiontype = "Options";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveProfile()
|
private async Task SaveProfile()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
@@ -185,10 +231,19 @@
|
|||||||
profile.MaxLength = int.Parse(_maxlength);
|
profile.MaxLength = int.Parse(_maxlength);
|
||||||
profile.Rows = int.Parse(_rows);
|
profile.Rows = int.Parse(_rows);
|
||||||
profile.DefaultValue = _defaultvalue;
|
profile.DefaultValue = _defaultvalue;
|
||||||
profile.Options = _options;
|
if (_optiontype == "Options" && !string.IsNullOrEmpty(_options))
|
||||||
|
{
|
||||||
|
profile.Options = "EntityName:" + _options;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
profile.Options = _options;
|
||||||
|
}
|
||||||
profile.Validation = _validation;
|
profile.Validation = _validation;
|
||||||
|
profile.Autocomplete = _autocomplete;
|
||||||
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
|
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
|
||||||
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
|
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
|
||||||
|
|
||||||
if (_profileid != -1)
|
if (_profileid != -1)
|
||||||
{
|
{
|
||||||
profile = await ProfileService.UpdateProfileAsync(profile);
|
profile = await ProfileService.UpdateProfileAsync(profile);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ else
|
|||||||
<th>@Localizer["Order"]</th>
|
<th>@Localizer["Order"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
||||||
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
|
|||||||
@@ -13,71 +13,71 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
||||||
@if (!_pages.Where(item => item.IsDeleted).Any())
|
@if (!_pages.Where(item => item.IsDeleted).Any())
|
||||||
{
|
{
|
||||||
<br />
|
<br />
|
||||||
<p>@Localizer["NoPage.Deleted"]</p>
|
<p>@Localizer["NoPage.Deleted"]</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
|
<Pager Items="@_pages.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Path"]</th>
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Path</td>
|
||||||
<td>@context.DeletedBy</td>
|
<td>@context.DeletedBy</td>
|
||||||
<td>@context.DeletedOn</td>
|
<td>@context.DeletedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
<br />
|
<br />
|
||||||
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Modules" ResourceKey="Modules" Heading="Modules">
|
<TabPanel Name="Modules" ResourceKey="Modules" Heading="Modules">
|
||||||
@if (!_modules.Where(item => item.IsDeleted).Any())
|
@if (!_modules.Where(item => item.IsDeleted).Any())
|
||||||
{
|
{
|
||||||
<br />
|
<br />
|
||||||
<p>@Localizer["NoModule.Deleted"]</p>
|
<p>@Localizer["NoModule.Deleted"]</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
|
<Pager Items="@_modules.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
|
||||||
<Header>
|
<Header>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["Page"]</th>
|
<th>@Localizer["Page"]</th>
|
||||||
<th>@Localizer["Module"]</th>
|
<th>@Localizer["Module"]</th>
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
<td>@context.DeletedBy</td>
|
<td>@context.DeletedBy</td>
|
||||||
<td>@context.DeletedOn</td>
|
<td>@context.DeletedOn</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
<br />
|
<br />
|
||||||
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
private List<Module> _modules;
|
private List<Module> _modules;
|
||||||
private int _pagePage = 1;
|
private int _pagePage = 1;
|
||||||
private int _pageModule = 1;
|
private int _pageModule = 1;
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
@@ -105,12 +105,25 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
page.IsDeleted = false;
|
var validated = true;
|
||||||
await PageService.UpdatePageAsync(page);
|
if (page.ParentId != null)
|
||||||
await logger.LogInformation("Page Restored {Page}", page);
|
{
|
||||||
await Load();
|
var parent = _pages.Find(item => item.PageId == page.ParentId);
|
||||||
StateHasChanged();
|
validated = !parent.IsDeleted;
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
}
|
||||||
|
if (validated)
|
||||||
|
{
|
||||||
|
page.IsDeleted = false;
|
||||||
|
await PageService.UpdatePageAsync(page);
|
||||||
|
await logger.LogInformation("Page Restored {Page}", page);
|
||||||
|
AddModuleMessage(Localizer["Success.Page.Restore"], MessageType.Success);
|
||||||
|
await Load();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Page.Restore"], MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -125,9 +138,9 @@ else
|
|||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
||||||
|
AddModuleMessage(Localizer["Success.Page.Delete"], MessageType.Success);
|
||||||
await Load();
|
await Load();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -140,7 +153,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ModuleInstance.ShowProgressIndicator();
|
ShowProgressIndicator();
|
||||||
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
@@ -148,16 +161,16 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
await logger.LogInformation("Pages Permanently Deleted");
|
await logger.LogInformation("Pages Permanently Deleted");
|
||||||
|
AddModuleMessage(Localizer["Success.Pages.Delete"], MessageType.Success);
|
||||||
await Load();
|
await Load();
|
||||||
ModuleInstance.HideProgressIndicator();
|
HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
ModuleInstance.HideProgressIndicator();
|
HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +182,7 @@ else
|
|||||||
pagemodule.IsDeleted = false;
|
pagemodule.IsDeleted = false;
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
await logger.LogInformation("Module Restored {Module}", module);
|
await logger.LogInformation("Module Restored {Module}", module);
|
||||||
|
AddModuleMessage(Localizer["Success.Module.Restore"], MessageType.Success);
|
||||||
await Load();
|
await Load();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@@ -185,6 +199,7 @@ else
|
|||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
await logger.LogInformation("Module Permanently Deleted {Module}", module);
|
await logger.LogInformation("Module Permanently Deleted {Module}", module);
|
||||||
|
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||||
await Load();
|
await Load();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@@ -199,21 +214,22 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ModuleInstance.ShowProgressIndicator();
|
ShowProgressIndicator();
|
||||||
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
|
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
}
|
}
|
||||||
await logger.LogInformation("Modules Permanently Deleted");
|
await logger.LogInformation("Modules Permanently Deleted");
|
||||||
|
AddModuleMessage(Localizer["Success.Modules.Delete"], MessageType.Success);
|
||||||
await Load();
|
await Load();
|
||||||
ModuleInstance.HideProgressIndicator();
|
HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||||
ModuleInstance.HideProgressIndicator();
|
HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void OnPageChangePage(int page)
|
private void OnPageChangePage(int page)
|
||||||
|
|||||||
@@ -6,70 +6,95 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject ISettingService SettingService
|
@inject ISettingService SettingService
|
||||||
|
@inject ITimeZoneService TimeZoneService
|
||||||
|
|
||||||
@if (PageState.Site.AllowRegistration)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
<AuthorizeView Roles="@RoleNames.Registered">
|
@if (PageState.Site.AllowRegistration)
|
||||||
<Authorizing>
|
{
|
||||||
<text>...</text>
|
if (!_userCreated)
|
||||||
</Authorizing>
|
{
|
||||||
<Authorized>
|
if (PageState.User != null)
|
||||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
{
|
||||||
</Authorized>
|
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||||
<NotAuthorized>
|
}
|
||||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
else
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
{
|
||||||
<div class="container">
|
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||||
<div class="row mb-1 align-items-center">
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
<div class="container">
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
|
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||||
|
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||||
|
@foreach (var timezone in _timezones)
|
||||||
|
{
|
||||||
|
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<br />
|
||||||
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
|
||||||
<div class="col-sm-9">
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
<div class="input-group">
|
@if (_allowsitelogin)
|
||||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
{
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
<br />
|
||||||
</div>
|
|
||||||
</div>
|
<br />
|
||||||
</div>
|
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
|
||||||
<div class="row mb-1 align-items-center">
|
}
|
||||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
</form>
|
||||||
<div class="col-sm-9">
|
}
|
||||||
<div class="input-group">
|
}
|
||||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
}
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
else
|
||||||
</div>
|
{
|
||||||
</div>
|
<ModuleMessage Message="@Localizer["Info.Registration.Disabled"]" Type="MessageType.Info" />
|
||||||
</div>
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
|
||||||
</form>
|
|
||||||
</NotAuthorized>
|
|
||||||
</AuthorizeView>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<ModuleMessage Message="@Localizer["Info.Registration.Disabled"]" Type="MessageType.Info" />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private bool _initialized = false;
|
||||||
|
private List<Models.TimeZone> _timezones;
|
||||||
private string _passwordrequirements;
|
private string _passwordrequirements;
|
||||||
private string _username = string.Empty;
|
private string _username = string.Empty;
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
@@ -80,12 +105,19 @@ else
|
|||||||
private string _confirm = string.Empty;
|
private string _confirm = string.Empty;
|
||||||
private string _email = string.Empty;
|
private string _email = string.Empty;
|
||||||
private string _displayname = string.Empty;
|
private string _displayname = string.Empty;
|
||||||
|
private string _timezoneid = string.Empty;
|
||||||
|
private bool _userCreated = false;
|
||||||
|
private bool _allowsitelogin = true;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||||
|
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||||
|
_timezones = TimeZoneService.GetTimeZones();
|
||||||
|
_timezoneid = PageState.Site.TimeZoneId;
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
@@ -114,6 +146,7 @@ else
|
|||||||
Password = _password,
|
Password = _password,
|
||||||
Email = _email,
|
Email = _email,
|
||||||
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
||||||
|
TimeZoneId = _timezoneid,
|
||||||
PhotoFileId = null
|
PhotoFileId = null
|
||||||
};
|
};
|
||||||
user = await UserService.AddUserAsync(user);
|
user = await UserService.AddUserAsync(user);
|
||||||
@@ -121,14 +154,8 @@ else
|
|||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
_userCreated = true;
|
||||||
{
|
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
|
||||||
}
|
|
||||||
else // legacy behavior
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -160,7 +187,7 @@ else
|
|||||||
|
|
||||||
private void Cancel()
|
private void Cancel()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
||||||
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
<td><ActionLink Action="Users" Text="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ else
|
|||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
|
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@@ -180,27 +180,28 @@ else
|
|||||||
|
|
||||||
private async Task DeleteUserRole(int UserRoleId)
|
private async Task DeleteUserRole(int UserRoleId)
|
||||||
{
|
{
|
||||||
validated = true;
|
try
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
if (await interop.FormValid(form))
|
|
||||||
{
|
{
|
||||||
try
|
var userrole = await UserRoleService.GetUserRoleAsync(UserRoleId);
|
||||||
|
if (userrole.Role.Name == RoleNames.Registered)
|
||||||
|
{
|
||||||
|
userrole.ExpiryDate = DateTime.UtcNow;
|
||||||
|
await UserRoleService.UpdateUserRoleAsync(userrole);
|
||||||
|
await logger.LogInformation("User {Username} Expired From Role {Role}", userrole.User.Username, userrole.Role.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
|
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
|
||||||
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
|
await logger.LogInformation("User {Username} Removed From Role {Role}", userrole.User.Username, userrole.Role.Name);
|
||||||
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
|
|
||||||
await GetUserRoles();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
|
|
||||||
}
|
}
|
||||||
|
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
|
||||||
|
await GetUserRoles();
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
Oqtane.Client/Modules/Admin/Search/Index.razor
Normal file
119
Oqtane.Client/Modules/Admin/Search/Index.razor
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Search
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Index> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="searchprovider" HelpText="Specify the search provider for this site" ResourceKey="SearchProvider">Search Provider: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="searchprovider" class="form-control" @bind="@_searchProvider" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="enabled" HelpText="Specify if search indexing is enabled" ResourceKey="Enabled">Indexing Enabled? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="enabled" class="form-select" @bind="@_enabled">
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="lastindexedon" HelpText="The date/time which the site was last indexed on" ResourceKey="LastIndexedOn">Last Indexed: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="lastindexedon" class="form-control" @bind="@_lastIndexedOn" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="ignorepages" HelpText="Comma delimited list of pages which should be ignored (based on their path)" ResourceKey="IgnorePages">Ignore Pages: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="ignorepages" class="form-control" @bind="@_ignorePages" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="ignoreentities" HelpText="Comma delimited list of entities which should be ignored" ResourceKey="IgnoreEntities">Ignore Entities: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="ignoreentities" class="form-control" @bind="@_ignoreEntities" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="minimumwordlength" HelpText="Minimum length of a word to be indexed" ResourceKey="MinimumWordLength">Word Length: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="minimumwordlength" class="form-control" type="number" min="0" step="1" @bind="@_minimumWordLength" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="ignorewords" HelpText="Comma delimited list of words which should be ignored" ResourceKey="IgnoreWords">Ignore Words: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="ignorewords" class="form-control" @bind="@_ignoreWords" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
||||||
|
<ActionDialog Header="Reindex" Message="Are You Sure You Wish To Reindex Search Content?" Action="Reindex" Class="btn btn-danger" OnClick="@(async () => await Reindex())" ResourceKey="Reindex" />
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
|
private string _searchProvider;
|
||||||
|
private string _enabled = "True";
|
||||||
|
private string _lastIndexedOn = "";
|
||||||
|
private string _ignorePages = "";
|
||||||
|
private string _ignoreEntities = "File";
|
||||||
|
private string _minimumWordLength = "3";
|
||||||
|
private string _ignoreWords = "the,be,to,of,and,a,i,in,that,have,it,for,not,on,with,he,as,you,do,at,this,but,his,by,from,they,we,say,her,she,or,an,will,my,one,all,would,there,their,what,so,up,out,if,about,who,get,which,go,me,when,make,can,like,time,no,just,him,know,take,people,into,year,your,good,some,could,them,see,other,than,then,now,look,only,come,its,over,think,also,back,after,use,two,how,our,work,first,well,way,even,new,want,because,any,these,give,day,most,us";
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
_searchProvider = SettingService.GetSetting(settings, "Search_SearchProvider", Constants.DefaultSearchProviderName);
|
||||||
|
_enabled = SettingService.GetSetting(settings, "Search_Enabled", _enabled);
|
||||||
|
_lastIndexedOn = SettingService.GetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn);
|
||||||
|
_ignorePages = SettingService.GetSetting(settings, "Search_IgnorePages", _ignorePages);
|
||||||
|
_ignoreEntities = SettingService.GetSetting(settings, "Search_IgnoreEntities", _ignoreEntities);
|
||||||
|
_minimumWordLength = SettingService.GetSetting(settings, "Search_MininumWordLength", _minimumWordLength);
|
||||||
|
_ignoreWords = SettingService.GetSetting(settings, "Search_IgnoreWords", _ignoreWords);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_SearchProvider", _searchProvider);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_IgnorePages", _ignorePages, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_IgnoreEntities", _ignoreEntities, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_MininumWordLength", _minimumWordLength, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "Search_IgnoreWords", _ignoreWords, true);
|
||||||
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
AddModuleMessage(Localizer["Success.Save"], MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Search Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Save"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Reindex()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_lastIndexedOn = DateTime.MinValue.ToString();
|
||||||
|
await Save();
|
||||||
|
AddModuleMessage(Localizer["Message.Reindex"], MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Search Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Save"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
153
Oqtane.Client/Modules/Admin/SearchResults/Index.razor
Normal file
153
Oqtane.Client/Modules/Admin/SearchResults/Index.razor
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
@using Oqtane.Services
|
||||||
|
@using System.Net
|
||||||
|
@namespace Oqtane.Modules.Admin.SearchResults
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ISearchResultsService SearchResultsService
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Index> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@attribute [StreamRendering] // attribute allows the progress indicator to be displayed
|
||||||
|
|
||||||
|
<div class="search-result-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form method="post" @formname="SearchResultsForm" @onsubmit="Search" data-enhance>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<span class="input-group-text">@Localizer["SearchLabel"]</span>
|
||||||
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
|
<input type="text" name="keywords" class="form-control shadow-none" maxlength="50"
|
||||||
|
aria-label="Keywords"
|
||||||
|
placeholder="@Localizer["SearchPlaceholder"]"
|
||||||
|
@bind="@_keywords">
|
||||||
|
<button class="btn btn-primary" type="submit">@SharedLocalizer["Search"]</button>
|
||||||
|
<a class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Reset"]</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 mb-3">
|
||||||
|
@if (_loading)
|
||||||
|
{
|
||||||
|
<div class="app-progress-indicator"></div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@if (_searchResults != null && _searchResults.Results != null)
|
||||||
|
{
|
||||||
|
if (_searchResults.Results.Any())
|
||||||
|
{
|
||||||
|
<Pager Items="@_searchResults?.Results"
|
||||||
|
Format="Grid"
|
||||||
|
PageSize="@_pageSize"
|
||||||
|
DisplayPages="@_displayPages"
|
||||||
|
CurrentPage="@_currentPage"
|
||||||
|
Columns="1"
|
||||||
|
Toolbar="Bottom"
|
||||||
|
Parameters="@($"q={_keywords}")">
|
||||||
|
<Row>
|
||||||
|
<div class="search-item mb-2">
|
||||||
|
<h4 class="mb-1"><a href="@context.Url">@context.Title</a></h4>
|
||||||
|
<p class="mb-0 text-body-secondary">@((MarkupString)context.Snippet)</p>
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-info show mt-3" role="alert">
|
||||||
|
@Localizer["NoResult"]
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public override string RenderMode => RenderModes.Static;
|
||||||
|
private const string SearchDefaultPageSize = "10";
|
||||||
|
|
||||||
|
private string _includeEntities;
|
||||||
|
private string _excludeEntities;
|
||||||
|
private string _fromDate;
|
||||||
|
private string _toDate;
|
||||||
|
private string _pageSize;
|
||||||
|
private string _sortField;
|
||||||
|
private string _sortOrder;
|
||||||
|
private string _bodyLength;
|
||||||
|
private string _currentPage = "0";
|
||||||
|
private string _displayPages = "7";
|
||||||
|
|
||||||
|
private string _keywords;
|
||||||
|
private SearchResults _searchResults;
|
||||||
|
private bool _loading;
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "SearchResultsForm")]
|
||||||
|
public string KeyWords { get => ""; set => _keywords = value; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_includeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_IncludeEntities", "");
|
||||||
|
_excludeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ExcludeEntities", "");
|
||||||
|
_fromDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_FromDate", DateTime.MinValue.ToString());
|
||||||
|
_toDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ToDate", DateTime.MaxValue.ToString());
|
||||||
|
_pageSize = SettingService.GetSetting(ModuleState.Settings, "SearchResults_PageSize", SearchDefaultPageSize);
|
||||||
|
_sortField = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortField", "Relevance");
|
||||||
|
_sortOrder = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortOrder", "Descending");
|
||||||
|
_bodyLength = SettingService.GetSetting(ModuleState.Settings, "SearchResults_BodyLength", "255");
|
||||||
|
|
||||||
|
if (PageState.QueryString.ContainsKey("p"))
|
||||||
|
{
|
||||||
|
_currentPage = PageState.QueryString["p"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_keywords == null && PageState.QueryString.ContainsKey("q"))
|
||||||
|
{
|
||||||
|
_keywords = WebUtility.UrlDecode(PageState.QueryString["q"]);
|
||||||
|
await PerformSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Search()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, $"page=1&q={WebUtility.UrlEncode(_keywords)}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PerformSearch()
|
||||||
|
{
|
||||||
|
_loading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_keywords))
|
||||||
|
{
|
||||||
|
var searchQuery = new SearchQuery
|
||||||
|
{
|
||||||
|
SiteId = PageState.Site.SiteId,
|
||||||
|
Alias = PageState.Alias,
|
||||||
|
Keywords = _keywords,
|
||||||
|
IncludeEntities = _includeEntities,
|
||||||
|
ExcludeEntities = _excludeEntities,
|
||||||
|
FromDate = (!string.IsNullOrEmpty(_fromDate)) ? DateTime.Parse(_fromDate) : DateTime.MinValue,
|
||||||
|
ToDate = (!string.IsNullOrEmpty(_toDate)) ? DateTime.Parse(_toDate) : DateTime.MaxValue,
|
||||||
|
PageSize = int.MaxValue,
|
||||||
|
PageIndex = 0,
|
||||||
|
SortField = (!string.IsNullOrEmpty(_sortField)) ? (SearchSortField)Enum.Parse(typeof(SearchSortField), _sortField) : SearchSortField.Relevance,
|
||||||
|
SortOrder = (!string.IsNullOrEmpty(_sortOrder)) ? (SearchSortOrder)Enum.Parse(typeof(SearchSortOrder), _sortOrder) : SearchSortOrder.Descending,
|
||||||
|
BodyLength = (!string.IsNullOrEmpty(_bodyLength)) ? int.Parse(_bodyLength) : 255
|
||||||
|
};
|
||||||
|
|
||||||
|
_searchResults = await SearchResultsService.GetSearchResultsAsync(searchQuery);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["NoCriteria"], MessageType.Info, "bottom");
|
||||||
|
}
|
||||||
|
|
||||||
|
_loading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Oqtane.Client/Modules/Admin/SearchResults/ModuleInfo.cs
Normal file
19
Oqtane.Client/Modules/Admin/SearchResults/ModuleInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.SearchResults
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Search Results",
|
||||||
|
Description = "Search Results",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
SettingsType = "Oqtane.Modules.Admin.SearchResults.Settings, Oqtane.Client"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
123
Oqtane.Client/Modules/Admin/SearchResults/Settings.razor
Normal file
123
Oqtane.Client/Modules/Admin/SearchResults/Settings.razor
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.SearchResults
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@implements Oqtane.Interfaces.ISettingsControl
|
||||||
|
@inject IStringLocalizer<Settings> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="includeentities" ResourceKey="IncludeEntities" ResourceType="@resourceType" HelpText="Comma delimited list of entities to include in the search results. By default all entities will be included.">Include Entities: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="includeentities" type="text" class="form-control" @bind="@_includeEntities" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="excludeentities" ResourceKey="ExcludeEntities" ResourceType="@resourceType" HelpText="Comma delimited list of entities to exclude from search results. By default no entities will be excluded.">Exclude Entities: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="excludeentities" class="form-control" @bind="@_excludeEntities" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="daterange" ResourceKey="DateRange" ResourceType="@resourceType" HelpText="Enter the date range for search results. The default includes all content.">Date Range: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="date" class="form-control" @bind="@_fromDate" />
|
||||||
|
<span class="input-group-text">@Localizer["To"]</span>
|
||||||
|
<input type="date" class="form-control" @bind="@_toDate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="pagesize" ResourceKey="PageSize" ResourceType="@resourceType" HelpText="The maximum number of search results to retrieve. The default is unlimited.">Page Size: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="pagesize" type="text" class="form-control" @bind="@_pageSize" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="sortfield" ResourceKey="SortField" ResourceType="@resourceType" HelpText="Specify the default sort field">Sort By: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="softfield" class="form-select" @bind="@_sortField">
|
||||||
|
<option value="Relevance">@Localizer["Relevance"]</option>
|
||||||
|
<option value="Title">@Localizer["Title"]</option>
|
||||||
|
<option value="LastModified">@Localizer["LastModified"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="sortorder" ResourceKey="SortOrder" ResourceType="@resourceType" HelpText="Specify the default sort order">Sort Order: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="softorder" class="form-select" @bind="@_sortOrder">
|
||||||
|
<option value="Ascending">@Localizer["Ascending"]</option>
|
||||||
|
<option value="Descending">@Localizer["Descending"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="bodylength" ResourceKey="BodyLength" ResourceType="@resourceType" HelpText="The number of characters displayed for each search result summary. The default is 255 characters.">Body Size: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="bodylength" type="text" class="form-control" @bind="@_bodyLength" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string resourceType = "Oqtane.Modules.Admin.SearchResults.Settings, Oqtane.Client"; // for localization
|
||||||
|
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
|
||||||
|
private string _includeEntities;
|
||||||
|
private string _excludeEntities;
|
||||||
|
private DateTime? _fromDate = null;
|
||||||
|
private DateTime? _toDate = null;
|
||||||
|
private string _pageSize;
|
||||||
|
private string _sortField;
|
||||||
|
private string _sortOrder;
|
||||||
|
private string _bodyLength;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_includeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_IncludeEntities", "");
|
||||||
|
_excludeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ExcludeEntities", "");
|
||||||
|
var fromDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_FromDate", "");
|
||||||
|
_fromDate = (string.IsNullOrEmpty(fromDate)) ? null : DateTime.Parse(fromDate);
|
||||||
|
var toDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ToDate", "");
|
||||||
|
_toDate = (string.IsNullOrEmpty(toDate)) ? null : DateTime.Parse(toDate);
|
||||||
|
_pageSize = SettingService.GetSetting(ModuleState.Settings, "SearchResults_PageSize", "");
|
||||||
|
_sortField = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortField", "Relevance");
|
||||||
|
_sortOrder = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortOrder", "Descending");
|
||||||
|
_bodyLength = SettingService.GetSetting(ModuleState.Settings, "SearchResults_BodyLength", "255");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_IncludeEntities", _includeEntities);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_ExcludeEntities", _excludeEntities);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_From", _fromDate.ToString());
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_To", _toDate.ToString());
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_PageSize", _pageSize);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_SortField", _sortField);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_SortOrder", _sortOrder);
|
||||||
|
settings = SettingService.SetSetting(settings, "SearchResults_BodyLength", _bodyLength);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
226
Oqtane.Client/Modules/Admin/Settings/Add.razor
Normal file
226
Oqtane.Client/Modules/Admin/Settings/Add.razor
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Settings
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="entityName" HelpText="Entity Name" ResourceKey="EntityName">Entity:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
@if (_entityNameElement == "input")
|
||||||
|
{
|
||||||
|
<input id="entityName" class="form-control" @bind="@_entityName" maxlength="256" required />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<select class="form-select custom-select" value="@_entityName" @onchange="(e => EntityNameChanged(e))">
|
||||||
|
<option value="-"><@Localizer["Select Entity"]></option>
|
||||||
|
@foreach (var entityName in _entityNames)
|
||||||
|
{
|
||||||
|
<option value="@entityName">@entityName</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@EntityNameClicked" tabindex="-1">@_entityNameTitle</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="entityId" HelpText="Entity Id" ResourceKey="EntityId">Id:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
@if (_entityIdElement == "input")
|
||||||
|
{
|
||||||
|
<input id="entityId" class="form-control" @bind="@_entityId" maxlength="256" required />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<select class="form-select custom-select" @bind="@_entityId">
|
||||||
|
<option value="-"><@Localizer["Select Id"]></option>
|
||||||
|
@foreach (var entityId in _entityIds)
|
||||||
|
{
|
||||||
|
<option value="@entityId">@entityId</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="@EntityIdClicked" tabindex="-1">@_entityIdTitle</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="settingName" HelpText="Setting Name" ResourceKey="SettingName">Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="settingName" class="form-control" @bind="@_settingName" maxlength="256" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="settingValue" HelpText="Setting Value" ResourceKey="SettingValue">Value:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="SettingValue" class="form-control" @bind="@_settingValue" maxlength="256" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="isPrivate" HelpText="Private" ResourceKey="IsPrivate">Private?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="isPrivate" class="form-select" @bind="@_isPrivate">
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveSetting">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
|
||||||
|
private string _entityName = "-";
|
||||||
|
private List<string> _entityNames = new List<string>();
|
||||||
|
private string _entityNameElement = "select";
|
||||||
|
private string _entityNameTitle = "";
|
||||||
|
private string _entityId = "-";
|
||||||
|
private List<int> _entityIds = new List<int>();
|
||||||
|
private string _entityIdElement = "select";
|
||||||
|
private string _entityIdTitle = "";
|
||||||
|
private string _settingName = "";
|
||||||
|
private string _settingValue = "";
|
||||||
|
private string _isPrivate = "True";
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_entityNameTitle = Localizer["Input"];
|
||||||
|
_entityIdTitle = Localizer["Input"];
|
||||||
|
|
||||||
|
// default entity names
|
||||||
|
_entityNames.Add(EntityNames.Host);
|
||||||
|
_entityNames.Add(EntityNames.Job);
|
||||||
|
_entityNames.Add(EntityNames.ModuleDefinition);
|
||||||
|
_entityNames.Add(EntityNames.Theme);
|
||||||
|
_entityNames.Add(EntityNames.Tenant);
|
||||||
|
_entityNames.Add(EntityNames.Site);
|
||||||
|
_entityNames.Add(EntityNames.Role);
|
||||||
|
_entityNames.Add(EntityNames.Page);
|
||||||
|
_entityNames.Add(EntityNames.Module);
|
||||||
|
_entityNames.Add(EntityNames.Folder);
|
||||||
|
_entityNames.Add(EntityNames.User);
|
||||||
|
_entityNames.Add(EntityNames.Visitor);
|
||||||
|
|
||||||
|
// custom entity names
|
||||||
|
var entityNames = await SettingService.GetEntityNamesAsync();
|
||||||
|
foreach (var entityName in entityNames)
|
||||||
|
{
|
||||||
|
if (!_entityNames.Contains(entityName))
|
||||||
|
{
|
||||||
|
_entityNames.Add(entityName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Setting {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.LoadSetting"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EntityNameClicked()
|
||||||
|
{
|
||||||
|
if (_entityNameElement == "select")
|
||||||
|
{
|
||||||
|
_entityName = "";
|
||||||
|
_entityNameElement = "input";
|
||||||
|
_entityNameTitle = Localizer["Select"];
|
||||||
|
_entityId = "";
|
||||||
|
_entityIdElement = "input";
|
||||||
|
_entityIdTitle = Localizer["Select"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entityName = "-";
|
||||||
|
_entityNameElement = "select";
|
||||||
|
_entityNameTitle = Localizer["Input"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EntityIdClicked()
|
||||||
|
{
|
||||||
|
if (_entityIdElement == "select")
|
||||||
|
{
|
||||||
|
_entityId = "";
|
||||||
|
_entityIdElement = "input";
|
||||||
|
_entityIdTitle = Localizer["Select"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entityId = "-";
|
||||||
|
_entityIdElement = "select";
|
||||||
|
_entityIdTitle = Localizer["Input"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EntityNameChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_entityName = e.Value.ToString();
|
||||||
|
_entityId = "-";
|
||||||
|
_entityIdElement = "select";
|
||||||
|
_entityIdTitle = Localizer["Input"];
|
||||||
|
if (_entityName != "-")
|
||||||
|
{
|
||||||
|
_entityIds = await SettingService.GetEntityIdsAsync(_entityName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entityIds = new List<int>();
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error On EntityNameChanged");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveSetting()
|
||||||
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form) && _entityName != "-" && int.TryParse(_entityId, out int entityId))
|
||||||
|
{
|
||||||
|
var setting = new Setting();
|
||||||
|
setting.EntityName = _entityName;
|
||||||
|
setting.EntityId = entityId;
|
||||||
|
setting.SettingName = _settingName;
|
||||||
|
setting.SettingValue = _settingValue;
|
||||||
|
setting.IsPrivate = (bool.Parse(_isPrivate));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
setting = await SettingService.AddSettingAsync(setting);
|
||||||
|
await logger.LogInformation("Setting Saved {Setting}", setting);
|
||||||
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Setting {Setting} {Error}", setting, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveSetting"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
Oqtane.Client/Modules/Admin/Settings/Edit.razor
Normal file
122
Oqtane.Client/Modules/Admin/Settings/Edit.razor
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Settings
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="entityName" HelpText="Entity Name" ResourceKey="EntityName">Entity:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="entityName" class="form-control" @bind="@_entityName" maxlength="256" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="entityId" HelpText="Entity Id" ResourceKey="EntityId">Id:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="entityId" class="form-control" @bind="@_entityId" maxlength="256" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="settingName" HelpText="Setting Name" ResourceKey="SettingName">Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="settingName" class="form-control" @bind="@_settingName" maxlength="256" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="settingValue" HelpText="Setting Value" ResourceKey="SettingValue">Value:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="SettingValue" class="form-control" @bind="@_settingValue" maxlength="256" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="isPrivate" HelpText="Private" ResourceKey="IsPrivate">Private?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="isPrivate" class="form-select" @bind="@_isPrivate">
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveSetting">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
<br /><br />
|
||||||
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
|
||||||
|
private int _settingId;
|
||||||
|
private string _entityName;
|
||||||
|
private string _entityId;
|
||||||
|
private string _settingName;
|
||||||
|
private string _settingValue;
|
||||||
|
private string _isPrivate;
|
||||||
|
private string _createdby;
|
||||||
|
private DateTime _createdon;
|
||||||
|
private string _modifiedby;
|
||||||
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_settingId = int.Parse(PageState.QueryString["id"]);
|
||||||
|
_entityName = PageState.QueryString["entity"];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var setting = await SettingService.GetSettingAsync(_entityName, _settingId);
|
||||||
|
if (setting != null)
|
||||||
|
{
|
||||||
|
_entityId = setting.EntityId.ToString();
|
||||||
|
_settingName = setting.SettingName;
|
||||||
|
_settingValue = setting.SettingValue;
|
||||||
|
_isPrivate = setting.IsPrivate.ToString();
|
||||||
|
_createdby = setting.CreatedBy;
|
||||||
|
_createdon = setting.CreatedOn;
|
||||||
|
_modifiedby = setting.ModifiedBy;
|
||||||
|
_modifiedon = setting.ModifiedOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Setting {SettingId} {Error}", _settingId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.LoadSetting"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveSetting()
|
||||||
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
var setting = await SettingService.GetSettingAsync(_entityName, _settingId);
|
||||||
|
setting.SettingValue = _settingValue;
|
||||||
|
setting.IsPrivate = (_isPrivate != null && Boolean.Parse(_isPrivate));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
setting = await SettingService.UpdateSettingAsync(setting);
|
||||||
|
await logger.LogInformation("Setting Saved {Setting}", setting);
|
||||||
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving Setting {Setting} {Error}", setting, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.SaveSetting"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Oqtane.Client/Modules/Admin/Settings/ImportSettings.razor
Normal file
56
Oqtane.Client/Modules/Admin/Settings/ImportSettings.razor
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Settings
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<ImportSettings> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="settings" HelpText="Provide settings in comma delimited format using the column template specified" ResourceKey="Settings">Settings:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="settings" class="form-control" @bind="@_settings" rows="5" required></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="Import">@Localizer["Import"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _settings = "Entity,Id,Name,Value,Private\n";
|
||||||
|
|
||||||
|
public override string Title => "Import Settings";
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
private async Task Import()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_settings))
|
||||||
|
{
|
||||||
|
ShowProgressIndicator();
|
||||||
|
var result = await SettingService.ImportSettingsAsync(new Result { Message = _settings });
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Import.Success"], MessageType.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Import.Failure"], MessageType.Error);
|
||||||
|
}
|
||||||
|
HideProgressIndicator();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Import.Validation"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Importing Settings {Error}", ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Import"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Oqtane.Client/Modules/Admin/Settings/Index.razor
Normal file
146
Oqtane.Client/Modules/Admin/Settings/Index.razor
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Settings
|
||||||
|
@inherits ModuleBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
|
@inject IStringLocalizer<Index> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<ActionLink Action="Add" Text="Add Setting" Security="SecurityAccessLevel.Host" ResourceKey="AddSetting" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_entityName, _entityId)))" />
|
||||||
|
<ActionLink Action="ImportSettings" Text="Import" Class="btn btn-secondary ms-1" Security="SecurityAccessLevel.Host" ResourceKey="ImportSettings" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_entityName, _entityId)))" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<select class="form-select custom-select" value="@_entityName" @onchange="(e => EntityNameChanged(e))">
|
||||||
|
<option value="-"><@Localizer["Select Entity"]></option>
|
||||||
|
@foreach (var entityName in _entityNames)
|
||||||
|
{
|
||||||
|
<option value="@entityName">@entityName</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<select class="form-select custom-select" value="@_entityId" @onchange="(e => EntityIdChanged(e))">
|
||||||
|
<option value="-"><@Localizer["Select Id"]></option>
|
||||||
|
@foreach (var entityId in _entityIds)
|
||||||
|
{
|
||||||
|
<option value="@entityId">@entityId</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Pager Items="@_settings" SearchProperties="SettingName,SettingValue">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@Localizer["Name"]</th>
|
||||||
|
<th>@Localizer["Value"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"entity={context.EntityName}&id={context.SettingId}")" Security="SecurityAccessLevel.Host" ResourceKey="EditSetting" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_entityName, _entityId)))" /></td>
|
||||||
|
<td><ActionDialog Header="Delete Setting" Message="@string.Format(Localizer["Confirm.DeleteSetting"], context.SettingName)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteSetting(context))" ResourceKey="DeleteSetting" /></td>
|
||||||
|
<td>@context.SettingName</td>
|
||||||
|
<td>@context.SettingValue</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _entityName = "-";
|
||||||
|
private List<string> _entityNames = new List<string>();
|
||||||
|
private string _entityId = "-";
|
||||||
|
private List<int> _entityIds = new List<int>();
|
||||||
|
private List<Setting> _settings = new List<Setting>();
|
||||||
|
|
||||||
|
public override string UrlParametersTemplate => "/{entityname}/{entityid}";
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
_entityNames = await SettingService.GetEntityNamesAsync();
|
||||||
|
|
||||||
|
if (UrlParameters.ContainsKey("entityname"))
|
||||||
|
{
|
||||||
|
_entityName = UrlParameters["entityname"];
|
||||||
|
await GetEntityIds();
|
||||||
|
}
|
||||||
|
if (UrlParameters.ContainsKey("entityid"))
|
||||||
|
{
|
||||||
|
_entityId = UrlParameters["entityid"];
|
||||||
|
await GetSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetEntityIds()
|
||||||
|
{
|
||||||
|
if (_entityName != "-")
|
||||||
|
{
|
||||||
|
_entityIds = await SettingService.GetEntityIdsAsync(_entityName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entityIds = new List<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetSettings()
|
||||||
|
{
|
||||||
|
if (_entityName != "-" && _entityId != "-")
|
||||||
|
{
|
||||||
|
_settings = await SettingService.GetSettingsAsync(_entityName, int.Parse(_entityId), "");
|
||||||
|
_settings = _settings.OrderBy(item => item.SettingName).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_settings = new List<Setting>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EntityNameChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_entityName = e.Value.ToString();
|
||||||
|
_entityId = "-";
|
||||||
|
await GetEntityIds();
|
||||||
|
await GetSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error On EntityNameChanged");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EntityIdChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_entityId = e.Value.ToString();
|
||||||
|
await GetSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error On EntityIdChanged");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteSetting(Setting setting)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SettingService.DeleteSettingAsync(setting.EntityName, setting.EntityId, setting.SettingName);
|
||||||
|
await logger.LogInformation("Setting Deleted {Setting}", setting);
|
||||||
|
await GetSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Setting {Setting} {Error}", setting, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.DeleteSetting"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -71,20 +71,22 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="runtime" HelpText="The runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
|
<Label Class="col-sm-3" For="rendermode" HelpText="The default render mode for the site" ResourceKey="Rendermode">Render Mode: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="rendermode" class="form-select" @bind="@_rendermode" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||||
</select>
|
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if the site should be prerendered (for search crawlers, etc...)" ResourceKey="Prerender">Prerender? </Label>
|
<Label Class="col-sm-3" For="runtime" HelpText="The render mode for UI components which require interactivity" ResourceKey="Runtime">Interactivity: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
|
<option value="@Runtimes.Server">@(SharedLocalizer["Runtime" + @Runtimes.Server])</option>
|
||||||
<option value="">@SharedLocalizer["No"]</option>
|
<option value="@Runtimes.WebAssembly">@(SharedLocalizer["Runtime" + @Runtimes.WebAssembly])</option>
|
||||||
|
<option value="@Runtimes.Auto">@(SharedLocalizer["Runtime" + @Runtimes.Auto])</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +109,7 @@ else
|
|||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
|
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database. Note that this will be the tenant name which is used within the framework to identify the database." ResourceKey="TenantName">Name: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
||||||
</div>
|
</div>
|
||||||
@@ -116,42 +118,42 @@ else
|
|||||||
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
|
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (_databases != null)
|
@if (_databases != null)
|
||||||
{
|
{
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
@foreach (var database in _databases)
|
@foreach (var database in _databases)
|
||||||
{
|
{
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
@if (!_showConnectionString)
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (!_showConnectionString)
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
if (_databaseConfigType != null)
|
if (_databaseConfigType != null)
|
||||||
{
|
{
|
||||||
@DatabaseConfigComponent
|
@DatabaseConfigComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
|
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@@ -201,8 +203,8 @@ else
|
|||||||
private string _themetype = "-";
|
private string _themetype = "-";
|
||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private string _sitetemplatetype = "-";
|
private string _sitetemplatetype = "-";
|
||||||
private string _runtime = "Server";
|
private string _rendermode = RenderModes.Static;
|
||||||
private string _prerender = "Prerendered";
|
private string _runtime = Runtimes.Server;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
@@ -214,7 +216,7 @@ else
|
|||||||
_tenantid = _tenants.First(item => item.Name == TenantNames.Master).TenantId.ToString();
|
_tenantid = _tenants.First(item => item.Name == TenantNames.Master).TenantId.ToString();
|
||||||
}
|
}
|
||||||
_urls = PageState.Alias.Name;
|
_urls = PageState.Alias.Name;
|
||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync(PageState.Site.SiteId);
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
if (_themes.Any(item => item.TypeName == Constants.DefaultTheme))
|
if (_themes.Any(item => item.TypeName == Constants.DefaultTheme))
|
||||||
{
|
{
|
||||||
@@ -228,137 +230,137 @@ else
|
|||||||
_sitetemplatetype = Constants.DefaultSiteTemplate;
|
_sitetemplatetype = Constants.DefaultSiteTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
{
|
{
|
||||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_databaseName = "LocalDB";
|
_databaseName = Constants.DefaultDBName;
|
||||||
}
|
}
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
_showConnectionString = false;
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadDatabaseConfigComponent()
|
private void LoadDatabaseConfigComponent()
|
||||||
{
|
{
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
if (database != null)
|
if (database != null)
|
||||||
{
|
{
|
||||||
_databaseConfigType = Type.GetType(database.ControlType);
|
_databaseConfigType = Type.GetType(database.ControlType);
|
||||||
DatabaseConfigComponent = builder =>
|
DatabaseConfigComponent = builder =>
|
||||||
{
|
{
|
||||||
builder.OpenComponent(0, _databaseConfigType);
|
builder.OpenComponent(0, _databaseConfigType);
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TenantChanged(ChangeEventArgs e)
|
private void TenantChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_tenantid = (string)e.Value;
|
_tenantid = (string)e.Value;
|
||||||
if (string.IsNullOrEmpty(_tenantName))
|
if (string.IsNullOrEmpty(_tenantName))
|
||||||
{
|
{
|
||||||
_tenantName = _name;
|
_tenantName = _name;
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ThemeChanged(ChangeEventArgs e)
|
private async void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
if (_themetype != "-")
|
if (_themetype != "-")
|
||||||
{
|
{
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
_containertype = _containers.First().TypeName;
|
_containertype = _containers.First().TypeName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_containers = new List<ThemeControl>();
|
_containers = new List<ThemeControl>();
|
||||||
_containertype = "-";
|
_containertype = "-";
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveSite()
|
private async Task SaveSite()
|
||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||||
{
|
{
|
||||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||||
var duplicates = new List<string>();
|
var duplicates = new List<string>();
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
if (aliases.Exists(item => item.Name == name))
|
if (aliases.Exists(item => item.Name == name))
|
||||||
{
|
{
|
||||||
duplicates.Add(name);
|
duplicates.Add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicates.Count == 0)
|
if (duplicates.Count == 0)
|
||||||
{
|
{
|
||||||
InstallConfig config = new InstallConfig();
|
InstallConfig config = new InstallConfig();
|
||||||
|
|
||||||
if (_tenantid == "+")
|
if (_tenantid == "+")
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
|
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
|
||||||
{
|
{
|
||||||
// validate host credentials
|
// validate host credentials
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _hostusername;
|
user.Username = _hostusername;
|
||||||
user.Password = _hostpassword;
|
user.Password = _hostpassword;
|
||||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
user = await UserService.LoginUserAsync(user, false, false);
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
if (_showConnectionString)
|
if (_showConnectionString)
|
||||||
{
|
{
|
||||||
connectionString = _connectionString;
|
connectionString = _connectionString;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
{
|
{
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionString != "")
|
if (connectionString != "")
|
||||||
{
|
{
|
||||||
config.TenantName = _tenantName;
|
config.TenantName = _tenantName;
|
||||||
config.DatabaseType = database.DBType;
|
config.DatabaseType = database.DBType;
|
||||||
config.ConnectionString = connectionString;
|
config.ConnectionString = connectionString;
|
||||||
config.HostUsername = _hostusername;
|
config.HostUsername = _hostusername;
|
||||||
config.HostPassword = _hostpassword;
|
config.HostPassword = _hostpassword;
|
||||||
config.HostEmail = user.Email;
|
config.HostEmail = user.Email;
|
||||||
config.HostName = user.DisplayName;
|
config.HostName = user.DisplayName;
|
||||||
@@ -388,6 +390,8 @@ else
|
|||||||
config.DatabaseType = tenant.DBType;
|
config.DatabaseType = tenant.DBType;
|
||||||
config.ConnectionString = tenant.DBConnectionString;
|
config.ConnectionString = tenant.DBConnectionString;
|
||||||
config.IsNewTenant = false;
|
config.IsNewTenant = false;
|
||||||
|
config.HostEmail = PageState.User.Email;
|
||||||
|
config.HostName = PageState.User.DisplayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,8 +403,9 @@ else
|
|||||||
config.DefaultContainer = _containertype;
|
config.DefaultContainer = _containertype;
|
||||||
config.DefaultAdminContainer = "";
|
config.DefaultAdminContainer = "";
|
||||||
config.SiteTemplate = _sitetemplatetype;
|
config.SiteTemplate = _sitetemplatetype;
|
||||||
|
config.RenderMode = _rendermode;
|
||||||
config.Runtime = _runtime;
|
config.Runtime = _runtime;
|
||||||
config.RenderMode = _runtime + _prerender;
|
config.Register = false;
|
||||||
|
|
||||||
ShowProgressIndicator();
|
ShowProgressIndicator();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user