Compare commits
1739 Commits
v5.0.0
...
revert-482
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
002f8117cb | |||
0dfdb12431 | |||
d77e898929 | |||
22e3161a9b | |||
b0c8203b24 | |||
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 | |||
611084d00c | |||
1b7c810eeb | |||
dd2a698147 | |||
e99df58bce | |||
70139221ab | |||
5d8d2b4a34 | |||
f59f322226 | |||
acca3f37b5 | |||
0de62f620d | |||
351c4a00e9 | |||
b048d81ac0 | |||
b9c9a2b75f | |||
4cea22d813 | |||
edad4e7d35 | |||
e28acd8d2d | |||
86feaff82f | |||
5f747db224 | |||
ccda41e18e | |||
2bb83c6532 | |||
ec59faba8c | |||
c5d8c44b81 | |||
7a4e4629e5 | |||
4d3dd95a60 | |||
713021359e | |||
927d2c36e8 | |||
afc6368915 | |||
3f604ad7b5 | |||
c5d4e237ad | |||
0b50b21779 | |||
cafc70f1c9 | |||
b4377c346f | |||
7c206af757 | |||
6212d0a052 | |||
8909822aea | |||
5fc0964f73 | |||
7725559a44 | |||
03414a58e9 | |||
ade0419bf6 | |||
9d646b2eed | |||
0d718a5ca2 | |||
78c8b36dfa | |||
b5ca0874fa | |||
e209becff9 | |||
652d42aa6b | |||
058a11597f | |||
dd73d6e19a | |||
3793f618a8 | |||
6621983a9c | |||
e0b0302f5a | |||
b02584bec6 | |||
c832d61409 | |||
ac701f28b5 | |||
53032140e7 | |||
3c7633564f | |||
43eae40404 | |||
e34b1b54d5 | |||
0658a7de0d | |||
9e0a4dfac8 | |||
82034ade90 | |||
b979203e12 | |||
c9a368f13a | |||
cfabf22fef | |||
61aef77916 | |||
8b6c6beceb | |||
4688e49454 | |||
76cc28b215 | |||
467e5423d2 | |||
422bf7b689 | |||
1d230bd4aa | |||
029850842b | |||
cc65555c3d | |||
3b090396a4 | |||
2e4656ae8b | |||
ebce63baa4 | |||
08f691ee0d | |||
7550a19951 | |||
daeb76df11 | |||
31315e5ba6 | |||
e4e1fa6a4c | |||
6e36312be8 | |||
9cc7ba1d82 | |||
9182001147 | |||
5d1510083e | |||
7035f4cc1f | |||
da826b2a22 | |||
a152d8664d | |||
6ad75d963a | |||
e92d34a9e3 | |||
768066db58 | |||
886b63e55b | |||
3c7ee65b7d | |||
0e060c4564 | |||
3e127dbd9c | |||
ad66d14a1b | |||
5db61b78ea | |||
85f9597f2c | |||
73aca22605 | |||
077343ca20 | |||
c76946c770 | |||
2344a49260 | |||
65f463dbbd | |||
6e100a70b9 | |||
77650ebe09 | |||
cdba2bd9e2 | |||
e84cc92fcd | |||
a2890948bb | |||
f2e1ebef45 | |||
3c33614115 | |||
fcc4b1e8ee | |||
c8ac4ec1e8 | |||
93ab8b88d4 | |||
cc12c302e3 | |||
73941ca30e | |||
f963711820 | |||
1a0a301fb6 | |||
f87a9b7aae | |||
f18f0bc762 | |||
c3b1bc6674 | |||
e9bb228528 | |||
a906957454 | |||
49d10337a1 | |||
87c6c31f1f | |||
5f3639e396 | |||
14d36ef8dc | |||
fc186f1718 | |||
70aa93fe8d | |||
901df22d39 | |||
cd0fe3d6d4 | |||
09f2c3f645 | |||
116542d8e4 | |||
ffae6e269b | |||
2194dc0463 | |||
63e3923349 | |||
da2d426137 | |||
8df7672624 | |||
b9dba61c97 | |||
96e8e9736f |
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?
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -16,16 +16,23 @@ _ReSharper.Caches
|
||||
Oqtane.Server/appsettings.json
|
||||
Oqtane.Server/Data
|
||||
|
||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||
Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||
Oqtane.Server/Content
|
||||
Oqtane.Server/Packages
|
||||
Oqtane.Server/wwwroot/Content
|
||||
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/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/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!
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2023 .NET Foundation
|
||||
Copyright (c) 2018-2024 .NET Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -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,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Oqtane.Interfaces;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
@ -7,16 +8,17 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class OqtaneServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddOqtaneAuthorization(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneAuthentication(this IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorizationCore();
|
||||
services.AddCascadingAuthenticationState();
|
||||
services.AddScoped<IdentityAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<IdentityAuthenticationStateProvider>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneClientScopedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IInstallationService, InstallationService>();
|
||||
@ -49,6 +51,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<IUrlMappingService, UrlMappingService>();
|
||||
services.AddScoped<IVisitorService, VisitorService>();
|
||||
services.AddScoped<ISyncService, SyncService>();
|
||||
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||
|
||||
// providers
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.TextAreaTextEditor>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<div class="row">
|
||||
<div class="mx-auto text-center">
|
||||
<img src="oqtane-black.png" />
|
||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 8)</div>
|
||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 9)</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
@ -69,7 +69,7 @@
|
||||
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
||||
<div class="container">
|
||||
<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">
|
||||
<input id="username" type="text" class="form-control" @bind="@_hostUsername" />
|
||||
</div>
|
||||
@ -162,7 +162,7 @@
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// 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);
|
||||
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
@ -217,7 +217,7 @@
|
||||
{
|
||||
// include JavaScript
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +238,8 @@
|
||||
|
||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||
{
|
||||
if (await UserService.ValidatePasswordAsync(_hostPassword))
|
||||
var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_loadingDisplay = "";
|
||||
StateHasChanged();
|
||||
@ -260,7 +261,9 @@
|
||||
IsNewTenant = true,
|
||||
SiteName = Constants.DefaultSite,
|
||||
Register = _register,
|
||||
SiteTemplate = _template
|
||||
SiteTemplate = _template,
|
||||
RenderMode = RenderModes.Static,
|
||||
Runtime = Runtimes.Server
|
||||
};
|
||||
|
||||
var installation = await InstallationService.Install(config);
|
||||
@ -276,7 +279,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Password.Invalid"];
|
||||
_message = string.Join("<br />", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key]));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -12,12 +12,12 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
{
|
||||
string url = NavigateUrl(p.Path);
|
||||
<p class="col-md-2 mx-auto text-center mb-3">
|
||||
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
||||
<div class="col-md-2 mx-auto text-center my-3">
|
||||
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All">
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@ -27,6 +27,7 @@
|
||||
private List<Page> _pages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
public override string RenderMode => RenderModes.Static;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IFileService FileService
|
||||
@inject IFolderService FolderService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -80,6 +81,7 @@
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_url == string.Empty || _folderId == -1)
|
||||
@ -93,7 +95,7 @@
|
||||
_name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
}
|
||||
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
||||
if (!PageState.Site.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning);
|
||||
return;
|
||||
|
@ -40,10 +40,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
@if (_name.ToLower().EndsWith(".zip"))
|
||||
{
|
||||
<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>
|
||||
</form>
|
||||
}
|
||||
@ -126,4 +130,18 @@
|
||||
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,6 +8,8 @@
|
||||
|
||||
@if (_folders != null)
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -59,16 +61,24 @@
|
||||
<input id="capacity" class="form-control" @bind="@_capacity" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
||||
}
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
|
||||
<div class="container">
|
||||
<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" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br /><br />
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
||||
<br />
|
||||
@if (!_isSystem)
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button>
|
||||
@ -80,11 +90,6 @@
|
||||
@((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" />
|
||||
}
|
||||
<br /><br />
|
||||
@if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -170,6 +175,7 @@
|
||||
try
|
||||
{
|
||||
Folder folder;
|
||||
|
||||
if (_folderId != -1)
|
||||
{
|
||||
folder = await FolderService.GetFolderAsync(_folderId);
|
||||
@ -179,8 +185,6 @@
|
||||
folder = new Folder();
|
||||
}
|
||||
|
||||
folder.SiteId = PageState.Site.SiteId;
|
||||
|
||||
if (_parentId == -1)
|
||||
{
|
||||
folder.ParentId = null;
|
||||
@ -190,6 +194,14 @@
|
||||
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.Type = _type;
|
||||
folder.ImageSizes = _imagesizes;
|
||||
|
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">
|
||||
<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">
|
||||
<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>
|
||||
<option value="m">@Localizer["Minute(s)"]</option>
|
||||
<option value="H">@Localizer["Hour(s)"]</option>
|
||||
@ -45,7 +45,7 @@
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -154,6 +154,11 @@
|
||||
|
||||
private async Task SaveJob()
|
||||
{
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_startDate, _endDate))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
|
@ -10,14 +10,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">@Localizer["Refresh.Text"]</button>
|
||||
<br />
|
||||
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh.Text"]</button>
|
||||
<br />
|
||||
|
||||
<Pager Items="@_jobs" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
@ -27,9 +24,8 @@ else
|
||||
<th style="width: 1px;"> </th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="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" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
||||
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
|
||||
<td><ActionLink Action="Log" Text="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||
@ -46,6 +42,9 @@ else
|
||||
</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
|
||||
<br />
|
||||
<ActionLink Action="Log" Class="btn btn-secondary" Text="View All Logs" ResourceKey="ViewLogs" />
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -53,15 +52,20 @@ else
|
||||
|
||||
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)
|
||||
{
|
||||
var status = string.Empty;
|
||||
@ -112,22 +116,6 @@ else
|
||||
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)
|
||||
{
|
||||
try
|
||||
@ -164,7 +152,8 @@ else
|
||||
|
||||
private async Task Refresh()
|
||||
{
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
StateHasChanged();
|
||||
ShowProgressIndicator();
|
||||
await GetJobs();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh"]</button>
|
||||
<br /><br />
|
||||
|
||||
<Pager Items="@_jobLogs">
|
||||
<Header>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
@ -35,6 +38,11 @@ else
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await GetJobLogs();
|
||||
}
|
||||
|
||||
private async Task GetJobLogs()
|
||||
{
|
||||
_jobLogs = await JobLogService.GetJobLogsAsync();
|
||||
|
||||
@ -67,4 +75,11 @@ else
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private async Task Refresh()
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
await GetJobLogs();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
@namespace Oqtane.Modules.Admin.Languages
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILocalizationService LocalizationService
|
||||
@inject ILanguageService LanguageService
|
||||
@ -94,7 +93,6 @@ else
|
||||
var language = new Language
|
||||
{
|
||||
SiteId = PageState.Page.SiteId,
|
||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
||||
Code = _code,
|
||||
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
||||
};
|
||||
@ -130,9 +128,7 @@ else
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||
}
|
||||
}
|
||||
|
||||
|
109
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
109
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
@ -0,0 +1,109 @@
|
||||
@namespace Oqtane.Modules.Admin.Languages
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILocalizationService LocalizationService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IPackageService PackageService
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (_code == null)
|
||||
{
|
||||
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Manage" ResourceKey="Manage" Heading="Manage">
|
||||
<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="Name Of The Language" ResourceKey="Name">Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="code" class="form-control" @bind="@_code" readonly/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="default" class="form-select" @bind="@_default" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _languageId = -1;
|
||||
private string _code = string.Empty;
|
||||
private string _cultureName = string.Empty;
|
||||
private string _default = "False";
|
||||
private List<Language> _languages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_languageId = Int32.Parse(PageState.QueryString["id"]);
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||
if (language != null)
|
||||
{
|
||||
_code = language.Code;
|
||||
_cultureName = language.Name;
|
||||
_default = language.IsDefault.ToString();
|
||||
if (language.SiteId == null)
|
||||
{
|
||||
language.SiteId = PageState.Site.SiteId;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SaveLanguage()
|
||||
{
|
||||
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||
if (language != null)
|
||||
{
|
||||
language.IsDefault = Boolean.Parse(_default);
|
||||
try
|
||||
{
|
||||
await LanguageService.EditLanguageAsync(language);
|
||||
|
||||
if (language.IsDefault)
|
||||
{
|
||||
await SetCultureAsync(language.Code);
|
||||
}
|
||||
|
||||
await logger.LogInformation("Language Edited {Language}", language);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Editing Language {Language} {Error}", language, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Language.Edit"], MessageType.Error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SetCultureAsync(string culture)
|
||||
{
|
||||
if (culture != CultureInfo.CurrentUICulture.Name)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@ else
|
||||
|
||||
<Pager Items="@_languages" SearchProperties="Name,Code">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["Code"]</th>
|
||||
@ -27,6 +28,12 @@ else
|
||||
}
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
@if (!context.IsDefault)
|
||||
{
|
||||
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.LanguageId.ToString())" ResourceKey="EditLanguage" />
|
||||
}
|
||||
</td>
|
||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Code</td>
|
||||
|
@ -8,11 +8,12 @@
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<NotAuthorized>
|
||||
@if (PageState.User != null)
|
||||
{
|
||||
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (!twofactor)
|
||||
{
|
||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
@ -20,7 +21,9 @@
|
||||
@if (_allowexternallogin)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||
<br /><br />
|
||||
<br />
|
||||
|
||||
<br />
|
||||
}
|
||||
@if (_allowsitelogin)
|
||||
{
|
||||
@ -46,8 +49,17 @@
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
<br /><br />
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
||||
@if (PageState.Site.AllowRegistration)
|
||||
{
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
@ -66,8 +78,7 @@
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _allowsitelogin = true;
|
||||
@ -84,9 +95,8 @@
|
||||
private bool _alwaysremember = false;
|
||||
private string _code = string.Empty;
|
||||
|
||||
private string _returnUrl = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
public override bool? Prerender => true;
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
@ -99,14 +109,10 @@
|
||||
{
|
||||
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
_alwaysremember = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AlwaysRemember", "false"));
|
||||
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
_returnUrl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("name"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
@ -155,10 +161,6 @@
|
||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||
}
|
||||
}
|
||||
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
||||
{
|
||||
_alwaysremember = Convert.ToBoolean(alwaysRememberStr);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -170,12 +172,15 @@
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender && PageState.User == null && _allowsitelogin)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(username.Id)) // ensure username is visible in UI
|
||||
{
|
||||
await username.FocusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// redirect logged in user to specified page
|
||||
if (PageState.User != null)
|
||||
if (PageState.User != null && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
@ -194,13 +199,7 @@
|
||||
|
||||
if (!twofactor)
|
||||
{
|
||||
bool alwaysRemember = false;
|
||||
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
||||
{
|
||||
alwaysRemember = Convert.ToBoolean(alwaysRememberStr);
|
||||
}
|
||||
bool remember = alwaysRemember || _remember;
|
||||
_remember = remember;
|
||||
_remember = _alwaysremember || _remember;
|
||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||
}
|
||||
else
|
||||
@ -208,28 +207,31 @@
|
||||
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);
|
||||
|
||||
// return url is not specified if user navigated directly to login page
|
||||
var returnurl = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : PageState.Alias.Path;
|
||||
|
||||
if (hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive login
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
|
||||
NavigationManager.NavigateTo(NavigateUrl(returnurl, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
}
|
||||
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;
|
||||
validated = false;
|
||||
@ -264,7 +266,7 @@
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(_returnUrl);
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
||||
private async Task Forgot()
|
||||
@ -332,7 +334,7 @@
|
||||
|
||||
private void ExternalLogin()
|
||||
{
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + WebUtility.UrlEncode(PageState.ReturnUrl)), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ else
|
||||
<th>@Localizer["Function"]</th>
|
||||
</Header>
|
||||
<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)">@context.Level</td>
|
||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||
@ -81,12 +81,13 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" type="number" min="0" step="1" class="form-control" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<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>
|
||||
</TabStrip>
|
||||
}
|
||||
@ -97,7 +98,7 @@ else
|
||||
private string _rows = "10";
|
||||
private int _page = 1;
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
private int _retention = 30;
|
||||
|
||||
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
@ -126,7 +127,7 @@ else
|
||||
await GetLogs();
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -218,7 +219,7 @@ else
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
@ -230,6 +231,21 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_page = page;
|
||||
|
@ -125,7 +125,7 @@
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Heading="Upload">
|
||||
<div class="container">
|
||||
<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">
|
||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
|
||||
</div>
|
||||
|
@ -13,13 +13,13 @@
|
||||
<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="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">
|
||||
<input id="owner" class="form-control" @bind="@_owner" required />
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<input id="module" class="form-control" @bind="@_module" required />
|
||||
</div>
|
||||
@ -27,7 +27,7 @@
|
||||
<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>
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -118,6 +118,7 @@
|
||||
{
|
||||
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||
{
|
||||
if (string.IsNullOrEmpty(_description)) _description = _module;
|
||||
if (IsValidXML(_description))
|
||||
{
|
||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||
|
@ -1,13 +1,15 @@
|
||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IPackageService PackageService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IPageService PageService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -30,7 +32,7 @@
|
||||
<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>
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -131,6 +133,18 @@
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
||||
<Pager Items="@_pagesWithModules" RowClass="align-middle">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
||||
<td>@(string.IsNullOrEmpty(context.Title) ? @context.Name : @context.Title )</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
|
||||
@if (_languages != null && _languages.Count > 0)
|
||||
{
|
||||
@ -240,6 +254,7 @@
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private List<Page> _pagesWithModules;
|
||||
|
||||
#pragma warning disable 649
|
||||
private PermissionGrid _permissionGrid;
|
||||
@ -291,6 +306,19 @@
|
||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
// get distinct pages where module exists
|
||||
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||
var distinctPageIds = modules
|
||||
.Where(md => md.ModuleDefinition?.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
||||
.Select(md => md.PageId)
|
||||
.Distinct();
|
||||
|
||||
// retrieve the pages which contain the module
|
||||
var pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
_pagesWithModules = pages
|
||||
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
|
||||
.ToList();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
@ -439,5 +467,5 @@
|
||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IModuleService ModuleService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IPackageService PackageService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@ -50,7 +51,7 @@ else
|
||||
<th style="width: 1px;"> </th>
|
||||
</Header>
|
||||
<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>
|
||||
@if (context.AssemblyName != Constants.ClientId)
|
||||
{
|
||||
@ -70,7 +71,7 @@ else
|
||||
}
|
||||
</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>
|
||||
}
|
||||
@ -99,6 +100,7 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Module> _modules;
|
||||
private List<ModuleDefinition> _allModuleDefinitions;
|
||||
private List<ModuleDefinition> _moduleDefinitions;
|
||||
private List<Package> _packages;
|
||||
@ -111,6 +113,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
await LoadModuleDefinitions();
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private string _content = string.Empty;
|
||||
|
@ -17,7 +17,7 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
@code {
|
||||
|
@ -3,13 +3,16 @@
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IThemeService ThemeService
|
||||
@inject IPageService PageService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject IStringLocalizer<Settings> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<TabStrip>
|
||||
@if (_initialized)
|
||||
{
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<TabStrip ActiveTab="@_activetab">
|
||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||
@if (_containers != null)
|
||||
{
|
||||
@ -48,6 +51,18 @@
|
||||
</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">
|
||||
@ -67,14 +82,16 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Page p in PageState.Pages)
|
||||
if (_pages != null)
|
||||
{
|
||||
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>
|
||||
@ -82,7 +99,7 @@
|
||||
</div>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
|
||||
@if (_permissions != null)
|
||||
{
|
||||
<div class="container">
|
||||
@ -108,16 +125,17 @@
|
||||
</TabStrip>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||
</form>
|
||||
</form>
|
||||
}
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Module Settings";
|
||||
|
||||
private bool _initialized = false;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
@ -132,7 +150,7 @@
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _moduleSettingsType;
|
||||
private object _moduleSettings;
|
||||
private string _moduleSettingsTitle = "Module Settings";
|
||||
private string _moduleSettingsTitle;
|
||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||
private Type _containerSettingsType;
|
||||
private object _containerSettings;
|
||||
@ -141,11 +159,17 @@
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
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);
|
||||
_containerType = ModuleState.ContainerType;
|
||||
@ -156,9 +180,13 @@
|
||||
createdon = ModuleState.CreatedOn;
|
||||
modifiedby = ModuleState.ModifiedBy;
|
||||
modifiedon = ModuleState.ModifiedOn;
|
||||
_effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
|
||||
_expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
||||
if (ModuleState.ModuleDefinition != null)
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
@ -182,7 +210,8 @@
|
||||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
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();
|
||||
};
|
||||
}
|
||||
@ -201,25 +230,37 @@
|
||||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
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();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private async Task SaveModule()
|
||||
{
|
||||
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
|
||||
if (!string.IsNullOrEmpty(_title))
|
||||
{
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
pagemodule.PageId = int.Parse(_pageId);
|
||||
pagemodule.Title = _title;
|
||||
pagemodule.Pane = _pane;
|
||||
pagemodule.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||
pagemodule.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||
{
|
||||
@ -257,17 +298,18 @@
|
||||
await containerSettingsControl.UpdateSettings();
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_activetab = "Settings";
|
||||
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_activetab = "Settings";
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||
<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))
|
||||
{
|
||||
@ -119,6 +119,18 @@
|
||||
<i class="@_icon"></i>
|
||||
</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 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>
|
||||
<div class="col-sm-9">
|
||||
@ -143,9 +155,16 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -186,12 +205,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
@ -207,6 +220,7 @@
|
||||
private bool validated = false;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pages;
|
||||
private int _pageId;
|
||||
private string _name;
|
||||
private string _parentid = "-1";
|
||||
@ -226,18 +240,19 @@
|
||||
private string _bodycontent;
|
||||
private string _permissions = null;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _themeSettingsType;
|
||||
private object _themeSettings;
|
||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
||||
private bool _refresh = false;
|
||||
protected Page _parent = null;
|
||||
protected Dictionary<string, string> _icons;
|
||||
private string _iconresources = "";
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
||||
@ -258,14 +273,15 @@
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = PageState.Site.DefaultContainerType;
|
||||
_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))
|
||||
{
|
||||
_children.Add(p);
|
||||
}
|
||||
}
|
||||
ThemeSettings();
|
||||
_effectivedate = Utilities.UtcAsLocalDate(PageState.Page.EffectiveDate);
|
||||
_expirydate = Utilities.UtcAsLocalDate(PageState.Page.ExpiryDate);
|
||||
_initialized = true;
|
||||
}
|
||||
else
|
||||
@ -287,7 +303,7 @@
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_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))
|
||||
{
|
||||
@ -306,33 +322,14 @@
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
StateHasChanged();
|
||||
|
||||
// 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)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
_refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,16 +342,31 @@
|
||||
Page page = null;
|
||||
try
|
||||
{
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
||||
{
|
||||
page = new Page();
|
||||
page.SiteId = PageState.Page.SiteId;
|
||||
page.Name = _name;
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
@ -363,16 +375,13 @@
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
||||
Page parent = _pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
@ -383,7 +392,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
if (_pages.Any(item => item.Path == page.Path))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||
@ -403,11 +411,11 @@
|
||||
page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
|
||||
page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
@ -419,6 +427,8 @@
|
||||
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
page.Url = _url;
|
||||
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
|
||||
page.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||
page.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||
page.UserId = null;
|
||||
|
||||
// appearance
|
||||
@ -448,7 +458,7 @@
|
||||
await logger.LogInformation("Page Added {Page}", page);
|
||||
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
|
||||
{
|
||||
|
@ -1,9 +1,11 @@
|
||||
@namespace Oqtane.Modules.Admin.Pages
|
||||
@using Oqtane.Interfaces
|
||||
@using System.Globalization
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IPageService PageService
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IThemeService ThemeService
|
||||
@inject ISystemService SystemService
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@ -30,7 +32,7 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||
<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)
|
||||
{
|
||||
@ -134,6 +136,18 @@
|
||||
<i class="@_icon"></i>
|
||||
</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 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>
|
||||
<div class="col-sm-9">
|
||||
@ -157,9 +171,16 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -167,10 +188,13 @@
|
||||
<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">
|
||||
<select id="container" class="form-select" @bind="@_containertype" required>
|
||||
@foreach (var container in _containers)
|
||||
@if (_containers != null)
|
||||
{
|
||||
foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -222,7 +246,7 @@
|
||||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
}
|
||||
@ -244,9 +268,16 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="theme" class="form-select" @bind="@_themetype" required>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
@if (theme.TypeName == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
<option value="@theme.TypeName">*@theme.Name*</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -266,7 +297,7 @@
|
||||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
}
|
||||
@ -286,6 +317,7 @@
|
||||
private bool validated = false;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pages;
|
||||
private int _pageId;
|
||||
private string _name;
|
||||
private string _currentparentid;
|
||||
@ -305,7 +337,7 @@
|
||||
private string _containertype = "-";
|
||||
private Type _themeSettingsType;
|
||||
private object _themeSettings;
|
||||
private RenderFragment ThemeSettingsComponent { get; set; }
|
||||
private RenderFragment _themeSettingsComponent { get; set; }
|
||||
private string _headcontent;
|
||||
private string _bodycontent;
|
||||
private List<Permission> _permissions = null;
|
||||
@ -322,11 +354,14 @@
|
||||
protected Page _parent = null;
|
||||
protected Dictionary<string, string> _icons;
|
||||
private string _iconresources = "";
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
_pageId = Int32.Parse(PageState.QueryString["id"]);
|
||||
_page = await PageService.GetPageAsync(_pageId);
|
||||
_icons = await SystemService.GetIconsAsync();
|
||||
@ -342,10 +377,10 @@
|
||||
else
|
||||
{
|
||||
_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>();
|
||||
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))
|
||||
{
|
||||
@ -370,6 +405,8 @@
|
||||
}
|
||||
_url = _page.Url;
|
||||
_icon = _page.Icon;
|
||||
_effectivedate = Utilities.UtcAsLocalDate(_page.EffectiveDate);
|
||||
_expirydate = Utilities.UtcAsLocalDate(_page.ExpiryDate);
|
||||
_ispersonalizable = _page.IsPersonalizable.ToString();
|
||||
|
||||
// appearance
|
||||
@ -395,7 +432,8 @@
|
||||
_permissions = _page.PermissionList;
|
||||
|
||||
// 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
|
||||
_createdby = _page.CreatedBy;
|
||||
@ -427,7 +465,7 @@
|
||||
{
|
||||
_parentid = (string)e.Value;
|
||||
_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))
|
||||
{
|
||||
@ -447,29 +485,34 @@
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
|
||||
// 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)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
_themeSettingsComponent = null;
|
||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
|
||||
{
|
||||
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
|
||||
if (_themeSettingsType != null)
|
||||
{
|
||||
ThemeSettingsComponent = builder =>
|
||||
_themeSettingsComponent = builder =>
|
||||
{
|
||||
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();
|
||||
};
|
||||
}
|
||||
@ -485,16 +528,31 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
{
|
||||
string currentPath = _page.Path;
|
||||
|
||||
_page.Name = _name;
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
_page.ParentId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.ParentId = Int32.Parse(_parentid);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
@ -503,16 +561,13 @@
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
_page.ParentId = null;
|
||||
_page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.ParentId = Int32.Parse(_parentid);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||
Page parent = _pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
@ -523,7 +578,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||
@ -545,11 +599,11 @@
|
||||
_page.Order = 0;
|
||||
break;
|
||||
case "<":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
child = _pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) _page.Order = child.Order - 1;
|
||||
break;
|
||||
case ">":
|
||||
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
child = _pages.FirstOrDefault(item => item.PageId == _childid);
|
||||
if (child != null) _page.Order = child.Order + 1;
|
||||
break;
|
||||
case ">>":
|
||||
@ -561,6 +615,8 @@
|
||||
_page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
|
||||
_page.Url = _url;
|
||||
_page.Icon = _icon ?? string.Empty;
|
||||
_page.EffectiveDate = Utilities.LocalDateAndTimeAsUtc(_effectivedate);
|
||||
_page.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
|
||||
_page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
|
||||
|
||||
// appearance
|
||||
@ -605,11 +661,11 @@
|
||||
await logger.LogInformation("Page Saved {Page}", _page);
|
||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl, true); // redirect to page being edited and reload
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5,11 +5,11 @@
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@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" />
|
||||
|
||||
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
|
||||
<Pager Items="@_pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -17,7 +17,7 @@
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
</Header>
|
||||
<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><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>
|
||||
@ -28,6 +28,21 @@
|
||||
@code {
|
||||
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)
|
||||
{
|
||||
try
|
||||
|
@ -34,7 +34,7 @@
|
||||
<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>
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -76,6 +76,12 @@
|
||||
<input id="validation" class="form-control" @bind="@_validation" maxlength="200" />
|
||||
</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">
|
||||
<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">
|
||||
@ -111,6 +117,7 @@
|
||||
private string _defaultvalue = string.Empty;
|
||||
private string _options = string.Empty;
|
||||
private string _validation = string.Empty;
|
||||
private string _autocomplete = string.Empty;
|
||||
private string _isrequired = "False";
|
||||
private string _isprivate = "False";
|
||||
private string createdby;
|
||||
@ -142,6 +149,7 @@
|
||||
_defaultvalue = profile.DefaultValue;
|
||||
_options = profile.Options;
|
||||
_validation = profile.Validation;
|
||||
_autocomplete = profile.Autocomplete;
|
||||
_isrequired = profile.IsRequired.ToString();
|
||||
_isprivate = profile.IsPrivate.ToString();
|
||||
createdby = profile.CreatedBy;
|
||||
@ -187,8 +195,10 @@
|
||||
profile.DefaultValue = _defaultvalue;
|
||||
profile.Options = _options;
|
||||
profile.Validation = _validation;
|
||||
profile.Autocomplete = _autocomplete;
|
||||
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
|
||||
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
|
||||
|
||||
if (_profileid != -1)
|
||||
{
|
||||
profile = await ProfileService.UpdateProfileAsync(profile);
|
||||
|
@ -22,7 +22,7 @@ else
|
||||
<th>@Localizer["Order"]</th>
|
||||
</Header>
|
||||
<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>@context.Name</td>
|
||||
<td>@context.Title</td>
|
||||
|
@ -22,7 +22,7 @@ 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>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -50,7 +50,7 @@ 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>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -140,7 +140,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
@ -149,7 +149,7 @@ else
|
||||
|
||||
await logger.LogInformation("Pages Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -157,7 +157,7 @@ else
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,21 +199,21 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
|
||||
{
|
||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||
}
|
||||
await logger.LogInformation("Modules Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
private void OnPageChangePage(int page)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.Register
|
||||
@using System.Net
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@ -8,14 +9,14 @@
|
||||
|
||||
@if (PageState.Site.AllowRegistration)
|
||||
{
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
if (!_userCreated)
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
@ -59,9 +60,16 @@
|
||||
<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>
|
||||
@if (_allowsitelogin)
|
||||
{
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
|
||||
}
|
||||
</form>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -79,12 +87,15 @@ else
|
||||
private string _confirm = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private bool _userCreated = false;
|
||||
private bool _allowsitelogin = true;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
@ -120,6 +131,7 @@ else
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||
_userCreated = true;
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
@ -152,7 +164,7 @@ else
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
|
@ -20,9 +20,9 @@ else
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
</Header>
|
||||
<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><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>
|
||||
</Row>
|
||||
</Pager>
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
142
Oqtane.Client/Modules/Admin/SearchResults/Index.razor
Normal file
142
Oqtane.Client/Modules/Admin/SearchResults/Index.razor
Normal file
@ -0,0 +1,142 @@
|
||||
@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"
|
||||
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-muted">@((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 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 _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", int.MaxValue.ToString());
|
||||
_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 (_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 = (!string.IsNullOrEmpty(_pageSize)) ? int.Parse(_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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
@namespace Oqtane.Modules.Admin.Site
|
||||
@inherits ModuleBase
|
||||
@using System.Text.RegularExpressions
|
||||
@using Microsoft.Extensions.DependencyInjection
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISiteService SiteService
|
||||
@inject IPageService PageService
|
||||
@inject ITenantService TenantService
|
||||
@inject IDatabaseService DatabaseService
|
||||
@inject IAliasService AliasService
|
||||
@inject IThemeService ThemeService
|
||||
@inject ISettingService SettingService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@ -27,7 +30,7 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="homepage" class="form-select" @bind="@_homepageid" required>
|
||||
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (Page page in PageState.Pages)
|
||||
@foreach (Page page in _pages)
|
||||
{
|
||||
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||
{
|
||||
@ -74,7 +77,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -98,10 +101,13 @@
|
||||
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
|
||||
@if (_containers != null)
|
||||
{
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -110,15 +116,48 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
|
||||
<option value="@Constants.DefaultAdminContainer"><@Localizer["DefaultAdminContainer"]></option>
|
||||
@if (_containers != null)
|
||||
{
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="textEditor" HelpText="Select the text editor for the site" ResourceKey="TextEditor">Text Editor: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="textEditor" class="form-select" @bind="@_textEditor" required>
|
||||
@if (_textEditors != null)
|
||||
{
|
||||
@foreach (var textEditor in _textEditors)
|
||||
{
|
||||
<option value="@textEditor.Value">@textEditor.Key</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</Section>
|
||||
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -168,14 +207,14 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@_smtpusername" />
|
||||
<input id="username" class="form-control" @bind="@_smtpusername" autocomplete="off"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" />
|
||||
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" autocomplete="off"/>
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword" tabindex="-1">@_togglesmtppassword</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -207,7 +246,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
||||
@ -270,7 +309,7 @@
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.IsDefault</td>
|
||||
<td>@((context.IsDefault) ? SharedLocalizer["Yes"] : SharedLocalizer["No"])</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -295,21 +334,40 @@
|
||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor 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">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
<select id="rendermode" class="form-select" value="@_rendermode" @onchange="(e => RenderModeChanged(e))" required>
|
||||
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="@Runtimes.Server">@(SharedLocalizer["Runtime" + @Runtimes.Server])</option>
|
||||
<option value="@Runtimes.WebAssembly">@(SharedLocalizer["Runtime" + @Runtimes.WebAssembly])</option>
|
||||
<option value="@Runtimes.Auto">@(SharedLocalizer["Runtime" + @Runtimes.Auto])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if interactive components should prerender their output on the server" ResourceKey="Prerender">Prerender: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
||||
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
|
||||
<option value="">@SharedLocalizer["No"]</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="hybrid" HelpText="Specifies if the site can be integrated with an external .NET MAUI hybrid application" ResourceKey="Hybrid">Hybrid? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="hybrid" class="form-select" @bind="@_hybrid" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -318,7 +376,7 @@
|
||||
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
|
||||
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site. Note that this is not the physical database name but rather the tenant name which is used within the framework to identify a database." ResourceKey="Tenant">Database: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||
</div>
|
||||
@ -330,7 +388,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The name of the connection string in appsettings.json which will be used to connect to the database" ResourceKey="ConnectionString">Connection: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
|
||||
</div>
|
||||
@ -353,12 +411,15 @@
|
||||
private bool _initialized = false;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pages;
|
||||
|
||||
private string _name = string.Empty;
|
||||
private string _homepageid = "-";
|
||||
private string _isdeleted;
|
||||
private string _sitemap = "";
|
||||
private string _siteguid = "";
|
||||
private string _version = "";
|
||||
|
||||
private int _logofileid = -1;
|
||||
private FileManager _logofilemanager;
|
||||
private int _faviconfileid = -1;
|
||||
@ -366,8 +427,15 @@
|
||||
private string _themetype = "";
|
||||
private string _containertype = "";
|
||||
private string _admincontainertype = "";
|
||||
|
||||
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
||||
private string _textEditor = "";
|
||||
private string _imageFiles = string.Empty;
|
||||
private string _uploadableFiles = string.Empty;
|
||||
|
||||
private string _headcontent = string.Empty;
|
||||
private string _bodycontent = string.Empty;
|
||||
|
||||
private string _smtphost = string.Empty;
|
||||
private string _smtpport = string.Empty;
|
||||
private string _smtpssl = "False";
|
||||
@ -378,21 +446,28 @@
|
||||
private string _smtpsender = string.Empty;
|
||||
private string _smtprelay = "False";
|
||||
private string _smtpenabled = "True";
|
||||
private string _retention = string.Empty;
|
||||
private int _retention = 30;
|
||||
|
||||
private string _pwaisenabled;
|
||||
private int _pwaappiconfileid = -1;
|
||||
private FileManager _pwaappiconfilemanager;
|
||||
private int _pwasplashiconfileid = -1;
|
||||
private FileManager _pwasplashiconfilemanager;
|
||||
|
||||
private List<Alias> _aliases;
|
||||
private int _aliasid = -1;
|
||||
private string _aliasname;
|
||||
private string _defaultalias;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
|
||||
private string _rendermode = RenderModes.Interactive;
|
||||
private string _runtime = Runtimes.Server;
|
||||
private string _prerender = "True";
|
||||
private string _hybrid = "False";
|
||||
|
||||
private string _tenant = string.Empty;
|
||||
private string _database = string.Empty;
|
||||
private string _connectionstring = string.Empty;
|
||||
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
@ -406,16 +481,25 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("updated"))
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
}
|
||||
|
||||
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
||||
_name = site.Name;
|
||||
if (site.HomePageId != null)
|
||||
{
|
||||
_homepageid = site.HomePageId.Value.ToString();
|
||||
}
|
||||
_isdeleted = site.IsDeleted.ToString();
|
||||
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
|
||||
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/sitemap.xml";
|
||||
_siteguid = site.SiteGuid;
|
||||
_version = site.Version;
|
||||
|
||||
@ -435,10 +519,34 @@
|
||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
|
||||
// functionality
|
||||
var textEditors = ServiceProvider.GetServices<ITextEditor>();
|
||||
foreach (var textEditor in textEditors)
|
||||
{
|
||||
_textEditors.Add(textEditor.Name, Utilities.GetFullTypeName(textEditor.GetType().AssemblyQualifiedName));
|
||||
}
|
||||
_textEditor = SettingService.GetSetting(settings, "TextEditor", Constants.DefaultTextEditor);
|
||||
_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;
|
||||
|
||||
// page content
|
||||
_headcontent = site.HeadContent;
|
||||
_bodycontent = site.BodyContent;
|
||||
|
||||
// SMTP
|
||||
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
|
||||
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
|
||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
||||
_retention = int.Parse(SettingService.GetSetting(settings, "NotificationRetention", "30"));
|
||||
|
||||
// PWA
|
||||
_pwaisenabled = site.PwaIsEnabled.ToString();
|
||||
if (site.PwaAppIconFileId != null)
|
||||
@ -450,25 +558,14 @@
|
||||
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
|
||||
}
|
||||
|
||||
// SMTP
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
|
||||
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
|
||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||
|
||||
// aliases
|
||||
await GetAliases();
|
||||
|
||||
// hosting model
|
||||
_rendermode = site.RenderMode;
|
||||
_runtime = site.Runtime;
|
||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||
_prerender = site.Prerender.ToString();
|
||||
_hybrid = site.Hybrid.ToString();
|
||||
|
||||
// database
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -479,7 +576,7 @@
|
||||
if (tenant != null)
|
||||
{
|
||||
_tenant = tenant.Name;
|
||||
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
|
||||
_database = _databases.Find(item => item.DBType == tenant.DBType && item.Name != "LocalDB")?.Name;
|
||||
_connectionstring = tenant.DBConnectionString;
|
||||
}
|
||||
}
|
||||
@ -519,6 +616,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderModeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_rendermode = (string)e.Value;
|
||||
switch (_rendermode)
|
||||
{
|
||||
case RenderModes.Interactive:
|
||||
_prerender = "True";
|
||||
break;
|
||||
case RenderModes.Static:
|
||||
_prerender = "False";
|
||||
break;
|
||||
case RenderModes.Headless:
|
||||
_prerender = "False";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveSite()
|
||||
{
|
||||
validated = true;
|
||||
@ -532,9 +646,6 @@
|
||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
{
|
||||
bool refresh = false;
|
||||
bool reload = false;
|
||||
|
||||
site.Name = _name;
|
||||
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
@ -545,23 +656,24 @@
|
||||
if (logofileid != -1)
|
||||
{
|
||||
site.LogoFileId = logofileid;
|
||||
if (logofileid != _logofileid)
|
||||
{
|
||||
_logofileid = logofileid;
|
||||
}
|
||||
}
|
||||
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
||||
if (faviconFieldId == -1) faviconFieldId = null;
|
||||
if (site.FaviconFileId != faviconFieldId)
|
||||
{
|
||||
site.FaviconFileId = faviconFieldId;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
if (site.DefaultThemeType != _themetype)
|
||||
{
|
||||
site.DefaultThemeType = _themetype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
if (site.DefaultContainerType != _containertype)
|
||||
{
|
||||
site.DefaultContainerType = _containertype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
|
||||
@ -569,43 +681,39 @@
|
||||
if (site.HeadContent != _headcontent)
|
||||
{
|
||||
site.HeadContent = _headcontent;
|
||||
reload = true;
|
||||
}
|
||||
if (site.BodyContent != _bodycontent)
|
||||
{
|
||||
site.BodyContent = _bodycontent;
|
||||
reload = true;
|
||||
}
|
||||
|
||||
// PWA
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||
{
|
||||
site.PwaAppIconFileId = pwaappiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||
{
|
||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
|
||||
// hosting model
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
||||
if (site.RenderMode != _rendermode || site.Runtime != _runtime || site.Prerender != bool.Parse(_prerender) || site.Hybrid != bool.Parse(_hybrid))
|
||||
{
|
||||
site.RenderMode = _rendermode;
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
reload = true; // needs to be reloaded on server
|
||||
site.Prerender = bool.Parse(_prerender);
|
||||
site.Hybrid = bool.Parse(_hybrid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,20 +730,18 @@
|
||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||
|
||||
// functionality
|
||||
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
|
||||
settings = SettingService.SetSetting(settings, "ImageFiles", (_imageFiles != Constants.ImageFiles) ? _imageFiles.Replace(" ", "") : "", false);
|
||||
settings = SettingService.SetSetting(settings, "UploadableFiles", (_uploadableFiles != Constants.UploadableFiles) ? _uploadableFiles.Replace(" ", "") : "", false);
|
||||
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
if (refresh || reload)
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "updated=true"), true); // reload
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -774,8 +880,17 @@
|
||||
if (!string.IsNullOrEmpty(_aliasname))
|
||||
{
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
var alias = aliases.Where(item => item.Name == _aliasname).FirstOrDefault();
|
||||
|
||||
int protocolIndex = _aliasname.IndexOf("://", StringComparison.OrdinalIgnoreCase);
|
||||
if (protocolIndex != -1)
|
||||
{
|
||||
_aliasname = _aliasname.Substring(protocolIndex + 3);
|
||||
}
|
||||
|
||||
var alias = aliases.FirstOrDefault(item => item.Name == _aliasname);
|
||||
|
||||
bool unique = (alias == null || alias.AliasId == _aliasid);
|
||||
|
||||
if (unique)
|
||||
{
|
||||
if (_aliasid == 0)
|
||||
@ -785,22 +900,27 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
alias = _aliases.Single(item => item.AliasId == _aliasid);
|
||||
alias = _aliases.SingleOrDefault(item => item.AliasId == _aliasid);
|
||||
if (alias != null)
|
||||
{
|
||||
alias.Name = _aliasname;
|
||||
alias.IsDefault = bool.Parse(_defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
else // duplicate alias
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
else // Duplicate alias
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CancelAlias()
|
||||
|
@ -71,21 +71,22 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
<select id="rendermode" class="form-select" @bind="@_rendermode" required>
|
||||
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
||||
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
|
||||
<option value="">@SharedLocalizer["No"]</option>
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="@Runtimes.Server">@(SharedLocalizer["Runtime" + @Runtimes.Server])</option>
|
||||
<option value="@Runtimes.WebAssembly">@(SharedLocalizer["Runtime" + @Runtimes.WebAssembly])</option>
|
||||
<option value="@Runtimes.Auto">@(SharedLocalizer["Runtime" + @Runtimes.Auto])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -108,7 +109,7 @@ else
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<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">
|
||||
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
|
||||
</div>
|
||||
@ -202,8 +203,8 @@ else
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
private string _rendermode = RenderModes.Static;
|
||||
private string _runtime = Runtimes.Server;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -400,8 +401,8 @@ else
|
||||
config.DefaultContainer = _containertype;
|
||||
config.DefaultAdminContainer = "";
|
||||
config.SiteTemplate = _sitetemplatetype;
|
||||
config.RenderMode = _rendermode;
|
||||
config.Runtime = _runtime;
|
||||
config.RenderMode = _runtime + _prerender;
|
||||
|
||||
ShowProgressIndicator();
|
||||
|
||||
|
@ -47,12 +47,26 @@ else
|
||||
}
|
||||
|
||||
private void Edit(string name)
|
||||
{
|
||||
if (PageState.Alias.Name == name)
|
||||
{
|
||||
NavigationManager.NavigateTo("/admin/site");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name + "/admin/site", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Browse(string name)
|
||||
{
|
||||
if (PageState.Alias.Name == name)
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.Alias.Path + "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,23 +83,14 @@ else
|
||||
{
|
||||
@if (_connection != "-")
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(_tenant))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
|
||||
<div class="col-sm-9">
|
||||
@if (_databases != null)
|
||||
{
|
||||
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
|
||||
<option value="-"><@Localizer["Type.Select"]></option>
|
||||
@foreach (var database in _databases)
|
||||
{
|
||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
<input id="databasetype" class="form-control" @bind="@_databasetype" readonly />
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_tenant))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -204,12 +195,12 @@ else
|
||||
{
|
||||
_connectionstring = _connections[_connection].ToString();
|
||||
_tenant = "";
|
||||
_databasetype = "-";
|
||||
_databasetype = "";
|
||||
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
|
||||
if (tenant != null)
|
||||
{
|
||||
_tenant = tenant.Name;
|
||||
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
|
||||
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType && item.Name != "LocalDB").Name;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -26,6 +26,12 @@
|
||||
<input id="osversion" class="form-control" @bind="@_osversion" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="process" HelpText="Indicates if the current process is 32 bit or 64 bit" ResourceKey="Process">Process: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="process" class="form-control" @bind="@_process" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="machinename" HelpText="Machine Name" ResourceKey="MachineName">Machine Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -62,12 +68,6 @@
|
||||
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tickcount" HelpText="Amount Of Time The Service Has Been Available And Operational" ResourceKey="TickCount">Service Uptime: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="tickcount" class="form-control" @bind="@_tickcount" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="workingset" HelpText="Memory Allocation Of Service (in MB)" ResourceKey="WorkingSet">Memory Allocation: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -165,13 +165,13 @@
|
||||
private string _version = string.Empty;
|
||||
private string _clrversion = string.Empty;
|
||||
private string _osversion = string.Empty;
|
||||
private string _process = string.Empty;
|
||||
private string _machinename = string.Empty;
|
||||
private string _ipaddress = string.Empty;
|
||||
private string _environment = string.Empty;
|
||||
private string _contentrootpath = string.Empty;
|
||||
private string _webrootpath = string.Empty;
|
||||
private string _servertime = string.Empty;
|
||||
private string _tickcount = string.Empty;
|
||||
private string _workingset = string.Empty;
|
||||
private string _installationid = string.Empty;
|
||||
|
||||
@ -192,13 +192,13 @@
|
||||
{
|
||||
_clrversion = systeminfo["CLRVersion"].ToString();
|
||||
_osversion = systeminfo["OSVersion"].ToString();
|
||||
_process = systeminfo["Process"].ToString();
|
||||
_machinename = systeminfo["MachineName"].ToString();
|
||||
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||
_environment = systeminfo["Environment"].ToString();
|
||||
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||
_tickcount = TimeSpan.FromMilliseconds(Convert.ToInt64(systeminfo["TickCount"].ToString())).ToString();
|
||||
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@
|
||||
<TabPanel Name="Upload" ResourceKey="Upload">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label>
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more theme packages." ResourceKey="Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@ else
|
||||
<th> </th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
|
||||
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
|
||||
<td>
|
||||
@if (context.AssemblyName != Constants.ClientId)
|
||||
{
|
||||
@ -63,7 +63,6 @@ else
|
||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||
}
|
||||
</td>
|
||||
<td></td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
|
@ -54,6 +54,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Disclaimer.Text"], MessageType.Warning);
|
||||
|
||||
List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
|
||||
if (packages != null)
|
||||
{
|
||||
@ -97,13 +99,16 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
await PackageService.DownloadPackageAsync(packageid, version);
|
||||
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
|
||||
HideProgressIndicator();
|
||||
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Framework Package {Error}", ex.Message);
|
||||
HideProgressIndicator();
|
||||
AddModuleMessage(Localizer["Error.Framework.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ else
|
||||
<th>@Localizer["Requested"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
||||
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
||||
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
||||
<td>
|
||||
<a href="@Utilities.TenantUrl(PageState.Alias, context.Url)">@context.Url</a>
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.UserProfile
|
||||
@using System.Net
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@ -8,6 +9,8 @@
|
||||
@inject INotificationService NotificationService
|
||||
@inject IFileService FileService
|
||||
@inject IFolderService FolderService
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -76,13 +79,14 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@photofileid" Filter="@Constants.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||
<FileManager FileId="@photofileid" Filter="@PageState.Site.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
<button type="button" class="btn btn-danger" @onclick="Logout">@Localizer["Logout Everywhere"]</button>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
@ -104,7 +108,9 @@
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@if (!string.IsNullOrEmpty(p.Autocomplete))
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
@if (GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
|
||||
@ -119,8 +125,38 @@
|
||||
</select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
@if (GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
|
||||
{
|
||||
<option value="@option" selected>@option</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@option">@option</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (p.Rows == 1)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(p.Autocomplete))
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
{
|
||||
@ -131,6 +167,20 @@
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(p.Autocomplete))
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
{
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
@ -143,6 +193,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -310,7 +361,7 @@
|
||||
private int folderid = -1;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
|
||||
private string _ImageFiles = string.Empty;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
@ -337,6 +388,11 @@
|
||||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
|
||||
}
|
||||
|
||||
// get user folder
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
@ -356,6 +412,9 @@
|
||||
}
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
|
||||
@ -427,9 +486,16 @@
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
@ -455,6 +521,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Logout()
|
||||
{
|
||||
await logger.LogInformation("User Logout Everywhere For Username {Username}", PageState.User?.Username);
|
||||
|
||||
var url = NavigateUrl(""); // home page
|
||||
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
// hybrid apps utilize an interactive logout
|
||||
await UserService.LogoutUserEverywhereAsync(PageState.User);
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = true };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
foreach (Profile profile in profiles)
|
||||
@ -488,7 +580,7 @@
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
@ -533,7 +625,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach(var Notification in notifications)
|
||||
{
|
||||
if (!Notification.IsDeleted)
|
||||
@ -549,7 +641,7 @@
|
||||
}
|
||||
await logger.LogInformation("Notifications Permanently Deleted");
|
||||
await LoadNotificationsAsync();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -557,9 +649,8 @@
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
@ -575,5 +666,4 @@
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null)
|
||||
{
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||
@ -22,24 +21,6 @@
|
||||
<input id="username" class="form-control" @bind="@_username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." 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="Please enter the password again to confirm it matches with the value 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="The email address where the user will receive notifications" ResourceKey="Email"></Label>
|
||||
<div class="col-sm-9">
|
||||
@ -123,12 +104,7 @@
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private string _username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _confirm = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private string _notify = "True";
|
||||
@ -142,8 +118,6 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = new Dictionary<string, string>();
|
||||
_initialized = true;
|
||||
@ -169,16 +143,14 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty)
|
||||
{
|
||||
if (_password == _confirm)
|
||||
if (_username != string.Empty && _email != string.Empty)
|
||||
{
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
user.Password = _password;
|
||||
user.Password = ""; // will be auto generated
|
||||
user.Email = _email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
|
||||
user.PhotoFileId = null;
|
||||
@ -200,11 +172,6 @@
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning);
|
||||
}
|
||||
@ -252,18 +219,4 @@
|
||||
var value = (string)e.Value;
|
||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,11 +226,6 @@
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
|
||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||
|
||||
|
@ -32,13 +32,13 @@ else
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||
</td>
|
||||
<td>
|
||||
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
||||
</td>
|
||||
<td>
|
||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||
<ActionLink Action="Roles" Text="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||
</td>
|
||||
<td>@context.User.Username</td>
|
||||
<td>@context.User.DisplayName</td>
|
||||
@ -182,20 +182,38 @@ else
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="provider" HelpText="Select the external login provider" ResourceKey="Provider">Provider:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<select id="provider" class="form-select" value="@_provider" @onchange="(e => ProviderChanged(e))">
|
||||
@foreach (var provider in Shared.ExternalLoginProviders.Providers)
|
||||
{
|
||||
<option value="@provider.Name">@Localizer[provider.Name]</option>
|
||||
}
|
||||
</select>
|
||||
@if (!string.IsNullOrEmpty(_providerurl))
|
||||
{
|
||||
<a href="@_providerurl" class="btn btn-secondary" target="_new">@Localizer["Info"]</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||
<option value="" selected>@Localizer["Not Specified"]</option>
|
||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OpenID Connect"]</option>
|
||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth 2.0"]</option>
|
||||
<option value="" selected><@Localizer["Not Specified"]></option>
|
||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="providername" class="form-control" @bind="@_providername" />
|
||||
</div>
|
||||
@ -254,20 +272,10 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authresponsetype" HelpText="Specify the authorization response type" ResourceKey="AuthResponseType">Authorization Response Type</Label>
|
||||
<Label Class="col-sm-3" For="authresponsetype" HelpText="Specify the authorization response type. The default is Authorization Code which is considered to be the most secure option based on the latest OAuth specification." ResourceKey="AuthResponseType">Authorization Response Type:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="authresponsetype" class="form-select" @bind="@_authresponsetype" required>
|
||||
<option value="code">@Localizer["AuthFlow.Code"]</option>
|
||||
@ -281,6 +289,19 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -297,32 +318,67 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<select id="reviewclaims" class="form-select" @bind="@_reviewclaims" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
@if (_reviewclaims == "true")
|
||||
{
|
||||
<a href="@_externalloginurl" target="_blank" class="btn btn-secondary">@SharedLocalizer["Test"]</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="nameclaimtype" class="form-control" @bind="@_nameclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimmappings" class="form-control" @bind="@_roleclaimmappings" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="synchronizeroles" HelpText="This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider" ResourceKey="SynchronizeRoles">Synchronize Roles?</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<select id="synchronizeroles" class="form-select" @bind="@_synchronizeroles" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -351,10 +407,10 @@ else
|
||||
</Section>
|
||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="secret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_secrettype" id="secret" class="form-control" @bind="@_secret" />
|
||||
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -414,6 +470,8 @@ else
|
||||
private string _maximumfailures;
|
||||
private string _lockoutduration;
|
||||
|
||||
private string _provider;
|
||||
private string _providerurl;
|
||||
private string _providertype;
|
||||
private string _providername;
|
||||
private string _authority;
|
||||
@ -425,14 +483,19 @@ else
|
||||
private string _clientsecret;
|
||||
private string _clientsecrettype = "password";
|
||||
private string _toggleclientsecret = string.Empty;
|
||||
private string _authresponsetype;
|
||||
private string _scopes;
|
||||
private string _parameters;
|
||||
private string _pkce;
|
||||
private string _authresponsetype;
|
||||
private string _redirecturl;
|
||||
private string _reviewclaims;
|
||||
private string _externalloginurl;
|
||||
private string _identifierclaimtype;
|
||||
private string _nameclaimtype;
|
||||
private string _emailclaimtype;
|
||||
private string _roleclaimtype;
|
||||
private string _roleclaimmappings;
|
||||
private string _synchronizeroles;
|
||||
private string _profileclaimtypes;
|
||||
private string _domainfilter;
|
||||
private string _createusers;
|
||||
@ -476,6 +539,20 @@ else
|
||||
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
||||
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
||||
|
||||
LoadExternalLoginSettings(settings);
|
||||
|
||||
_secret = SettingService.GetSetting(settings, "JwtOptions:Secret", "");
|
||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
||||
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
||||
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20");
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadExternalLoginSettings(Dictionary<string, string> settings)
|
||||
{
|
||||
_provider = SettingService.GetSetting(settings, "ExternalLogin:Provider", "<Custom>");
|
||||
_providerurl = SettingService.GetSetting(settings, "ExternalLogin:ProviderUrl", "");
|
||||
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
||||
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
||||
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
||||
@ -486,25 +563,23 @@ else
|
||||
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||
_reviewclaims = SettingService.GetSetting(settings, "ExternalLogin:ReviewClaims", "false");
|
||||
_externalloginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external");
|
||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||
_nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name");
|
||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||
_roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", "");
|
||||
_synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false");
|
||||
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
|
||||
|
||||
_secret = SettingService.GetSetting(settings, "JwtOptions:Secret", "");
|
||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
||||
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
||||
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadUsersAsync(bool load)
|
||||
@ -569,6 +644,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
||||
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Provider", _provider, false);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
||||
@ -578,19 +654,22 @@ else
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ReviewClaims", _reviewclaims, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimMappings", _roleclaimmappings, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:SynchronizeRoles", _synchronizeroles, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
|
||||
|
||||
if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||
@ -612,6 +691,21 @@ else
|
||||
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ProviderChanged(ChangeEventArgs e)
|
||||
{
|
||||
_provider = (string)e.Value;
|
||||
var provider = Shared.ExternalLoginProviders.Providers.FirstOrDefault(item => item.Name == _provider);
|
||||
if (provider != null)
|
||||
{
|
||||
LoadExternalLoginSettings(provider.Settings);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void ProviderTypeChanged(ChangeEventArgs e)
|
||||
|
@ -34,13 +34,13 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
|
||||
<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 role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
|
||||
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -60,8 +60,8 @@ else
|
||||
</Header>
|
||||
<Row>
|
||||
<td>@context.Role.Name</td>
|
||||
<td>@context.EffectiveDate</td>
|
||||
<td>@context.ExpiryDate</td>
|
||||
<td>@Utilities.UtcAsLocalDate(context.EffectiveDate)</td>
|
||||
<td>@Utilities.UtcAsLocalDate(context.ExpiryDate)</td>
|
||||
<td>
|
||||
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
|
||||
</td>
|
||||
@ -75,8 +75,8 @@ else
|
||||
private string name = string.Empty;
|
||||
private List<Role> roles;
|
||||
private int roleid = -1;
|
||||
private DateTime? effectivedate = null;
|
||||
private DateTime? expirydate = null;
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
private List<UserRole> userroles;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
@ -113,6 +113,7 @@ else
|
||||
try
|
||||
{
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, userid);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -127,11 +128,16 @@ else
|
||||
{
|
||||
if (roleid != -1)
|
||||
{
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
||||
if (userrole != null)
|
||||
{
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
userrole.EffectiveDate = _effectivedate;
|
||||
userrole.ExpiryDate = _expirydate;
|
||||
await UserRoleService.UpdateUserRoleAsync(userrole);
|
||||
}
|
||||
else
|
||||
@ -139,8 +145,8 @@ else
|
||||
userrole = new UserRole();
|
||||
userrole.UserId = userid;
|
||||
userrole.RoleId = roleid;
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
userrole.EffectiveDate = Utilities.UtcAsLocalDate(_effectivedate);
|
||||
userrole.ExpiryDate = Utilities.UtcAsLocalDate(_expirydate);
|
||||
await UserRoleService.AddUserRoleAsync(userrole);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ else
|
||||
<th>@Localizer["Created"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
||||
<td><ActionLink Action="Detail" Text="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
||||
<td>@context.IPAddress</td>
|
||||
<td>
|
||||
@if (context.UserId != null)
|
||||
@ -69,6 +69,12 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="duration" HelpText="The duration of a browsing session considered to be a distinct visit (in minutes)" ResourceKey="Duration">Session Duration: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="duration" class="form-control" type="number" min="0" step="1" @bind="@_duration" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages identifying visitors which should not be tracked" ResourceKey="Filter">Filter: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -76,9 +82,9 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -103,8 +109,9 @@ else
|
||||
private int _page = 1;
|
||||
private List<Visitor> _visitors;
|
||||
private string _tracking;
|
||||
private int _duration = 5;
|
||||
private string _filter = "";
|
||||
private string _retention = "";
|
||||
private int _retention = 30;
|
||||
private string _correlation = "true";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
@ -128,8 +135,9 @@ else
|
||||
|
||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_duration = int.Parse(SettingService.GetSetting(settings, "VisitorDuration", "5"));
|
||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||
}
|
||||
|
||||
@ -179,8 +187,9 @@ else
|
||||
await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "VisitorDuration", _duration.ToString(), true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
|
@ -2,9 +2,12 @@
|
||||
@using System.Text.Json
|
||||
@inherits LocalizableComponent
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (_visible)
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
@if (_visible)
|
||||
{
|
||||
<div class="app-actiondialog">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
@ -27,16 +30,68 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_authorized)
|
||||
{
|
||||
}
|
||||
@if (_authorized)
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_openIconSpan) @_openText</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button>
|
||||
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_openIconSpan) @_openText</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (_visible)
|
||||
{
|
||||
<div class="app-actiondialog">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="app-form-inline" method="post" @formname="@($"ActionDialogCloseForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Header</h5>
|
||||
<button type="submit" class="btn-close" aria-label="Close"></button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="modal-body">
|
||||
<p>@Message</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!string.IsNullOrEmpty(Action))
|
||||
{
|
||||
<form method="post" @formname="@($"ActionDialogConfirmForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="Confirm" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="@Class">@((MarkupString)_iconSpan) @Text</button>
|
||||
</form>
|
||||
}
|
||||
<form method="post" @formname="@($"ActionDialogCancelForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="btn btn-secondary">@SharedLocalizer["Cancel"]</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_authorized)
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_openIconSpan) @_openText</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" class="app-form-inline" @formname="@($"ActionDialogActionForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="@Class">@((MarkupString)_openIconSpan) @_openText</button>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +101,8 @@
|
||||
private bool _editmode = false;
|
||||
private bool _authorized = false;
|
||||
private string _iconSpan = string.Empty;
|
||||
private string _openIconSpan = string.Empty;
|
||||
private string _openText = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Header { get; set; } // required
|
||||
@ -83,6 +140,12 @@
|
||||
[Parameter]
|
||||
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
||||
|
||||
[Parameter]
|
||||
public bool IconOnly { get; set; } // optional - specifies only icon in opening link
|
||||
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - specifies a unique id for the compoment - required when there are multiple component instances on a page in static rendering
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Permissions))
|
||||
@ -99,6 +162,7 @@
|
||||
{
|
||||
Text = Action;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Class))
|
||||
{
|
||||
Class = "btn btn-success";
|
||||
@ -109,21 +173,39 @@
|
||||
_editmode = bool.Parse(EditMode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(IconName))
|
||||
{
|
||||
if (!IconName.Contains(" "))
|
||||
{
|
||||
IconName = "oi oi-" + IconName;
|
||||
}
|
||||
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
||||
}
|
||||
|
||||
Text = Localize(nameof(Text), Text);
|
||||
Header = Localize(nameof(Header), Header);
|
||||
Message = Localize(nameof(Message), Message);
|
||||
|
||||
_openText = Text;
|
||||
|
||||
if (!string.IsNullOrEmpty(IconName))
|
||||
{
|
||||
if (IconOnly)
|
||||
{
|
||||
_openText = string.Empty;
|
||||
}
|
||||
|
||||
// Check if IconName starts with "oi oi-"
|
||||
bool startsWithOiOi = IconName.StartsWith("oi oi-");
|
||||
|
||||
if (!startsWithOiOi && !IconName.Contains(" "))
|
||||
{
|
||||
IconName = "oi oi-" + IconName;
|
||||
}
|
||||
_openIconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : " ")}";
|
||||
_iconSpan = $"<span class=\"{IconName}\"></span> ";
|
||||
}
|
||||
|
||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||
_authorized = IsAuthorized();
|
||||
|
||||
if (string.IsNullOrEmpty(Id)) Id = "1";
|
||||
|
||||
if (PageState.QueryString.ContainsKey("dialog"))
|
||||
{
|
||||
_visible = (PageState.QueryString["dialog"] == Id);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAuthorized()
|
||||
@ -175,12 +257,22 @@
|
||||
private void DisplayModal()
|
||||
{
|
||||
_visible = !_visible;
|
||||
if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = new Dictionary<string, string>(PageState.QueryString);
|
||||
if (parameters.ContainsKey("dialog")) parameters.Remove("dialog");
|
||||
if (_visible) parameters.Add("dialog", Id);
|
||||
NavigationManager.NavigateTo(PageState.Route.AbsolutePath + Utilities.CreateQueryString(parameters));
|
||||
}
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
DisplayModal();
|
||||
OnClick();
|
||||
DisplayModal();
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@_classname" style="@_style" disabled>@((MarkupString)_iconSpan) @_text</button>
|
||||
<NavLink class="@($"{_classname} disabled")" href="@_url" style="@_style">@((MarkupString)_iconSpan) @_text</NavLink>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -97,10 +97,13 @@
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
_text = Action;
|
||||
if (!string.IsNullOrEmpty(Text))
|
||||
{
|
||||
_text = Text;
|
||||
_text = Localize(nameof(Text), Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
_text = Localize(nameof(Action), Action);
|
||||
}
|
||||
|
||||
if (IconOnly && !string.IsNullOrEmpty(IconName))
|
||||
@ -142,7 +145,10 @@
|
||||
|
||||
if (!string.IsNullOrEmpty(IconName))
|
||||
{
|
||||
if (!IconName.Contains(" "))
|
||||
// Check if IconName starts with "oi oi-"
|
||||
bool startsWithOiOi = IconName.StartsWith("oi oi-");
|
||||
|
||||
if (!startsWithOiOi && !IconName.Contains(" "))
|
||||
{
|
||||
IconName = "oi oi-" + IconName;
|
||||
}
|
||||
@ -150,7 +156,6 @@
|
||||
}
|
||||
|
||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||
_text = Localize(nameof(Text), _text);
|
||||
|
||||
_url = EditUrl(_path, _moduleId, Action, _parameters);
|
||||
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||
|
@ -3,6 +3,8 @@
|
||||
@inherits ModuleControlBase
|
||||
@inject IFolderService FolderService
|
||||
@inject IFileService FileService
|
||||
@inject ISettingService SettingService
|
||||
@inject IUserService UserService
|
||||
@inject IStringLocalizer<FileManager> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -83,6 +85,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
<div class="row mt-1">
|
||||
<div class="col">
|
||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (_image != string.Empty)
|
||||
@ -92,14 +102,6 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
<div class="row mt-1">
|
||||
<div class="col">
|
||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -194,7 +196,7 @@
|
||||
else
|
||||
{
|
||||
FolderId = -1;
|
||||
_message = "Folder Path " + Folder + "Does Not Exist";
|
||||
_message = "Folder Path " + Folder + " Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
@ -224,9 +226,9 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
FileId = -1; // file does not exist
|
||||
_message = "FileId " + FileId.ToString() + "Does Not Exist";
|
||||
_message = "FileId " + FileId.ToString() + " Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
FileId = -1; // file does not exist
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,7 +314,6 @@
|
||||
await SetImage();
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
StateHasChanged();
|
||||
|
||||
}
|
||||
|
||||
private async Task SetImage()
|
||||
@ -343,6 +344,7 @@
|
||||
_message = string.Empty;
|
||||
var interop = new Interop(JSRuntime);
|
||||
var uploads = await interop.GetFiles(_fileinputid);
|
||||
|
||||
if (uploads.Length > 0)
|
||||
{
|
||||
string restricted = "";
|
||||
@ -350,25 +352,38 @@
|
||||
{
|
||||
var filename = upload.Split(':')[0];
|
||||
var extension = (filename.LastIndexOf(".") != -1) ? filename.Substring(filename.LastIndexOf(".") + 1) : "";
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
if (!PageState.Site.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
{
|
||||
restricted += (restricted == "" ? "" : ",") + extension;
|
||||
}
|
||||
}
|
||||
if (restricted == "")
|
||||
{
|
||||
try
|
||||
{
|
||||
// upload the files
|
||||
var posturl = Utilities.TenantUrl(PageState.Alias, "/api/file/upload");
|
||||
var folder = (Folder == Constants.PackagesFolder) ? Folder : FolderId.ToString();
|
||||
var jwt = "";
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
{
|
||||
jwt = await UserService.GetTokenAsync();
|
||||
if (string.IsNullOrEmpty(jwt))
|
||||
{
|
||||
await logger.LogInformation("File Upload Failed From .NET MAUI Due To Missing Security Token. Token Options Must Be Set In User Settings.");
|
||||
_message = "Security Token Not Specified";
|
||||
_messagetype = MessageType.Error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShowProgress)
|
||||
{
|
||||
_uploading = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// upload the files
|
||||
var posturl = Utilities.TenantUrl(PageState.Alias, "/api/file/upload");
|
||||
var folder = (Folder == Constants.PackagesFolder) ? Folder : FolderId.ToString();
|
||||
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken);
|
||||
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt);
|
||||
|
||||
// uploading is asynchronous so we need to poll to determine if uploads are completed
|
||||
var success = true;
|
||||
@ -380,7 +395,7 @@
|
||||
|
||||
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
||||
var megabits = (size / 1048576.0) * 8; // binary conversion
|
||||
var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps)
|
||||
var uploadspeed = (PageState.Alias.Name.Contains("localhost")) ? 100 : 3; // 3 Mbps is FCC minimum for broadband upload
|
||||
var uploadtime = (megabits / uploadspeed); // seconds
|
||||
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
||||
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
||||
|
@ -37,8 +37,6 @@
|
||||
}
|
||||
|
||||
protected void OnChange(ChangeEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Value.ToString()))
|
||||
{
|
||||
Value = e.Value.ToString();
|
||||
if (ValueChanged.HasDelegate)
|
||||
@ -46,5 +44,4 @@
|
||||
ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,15 +2,25 @@
|
||||
@inherits ModuleControlBase
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
@if (!string.IsNullOrEmpty(Message))
|
||||
{
|
||||
<div class="@_classname alert-dismissible fade show mb-3" role="alert">
|
||||
@((MarkupString)_message)
|
||||
@((MarkupString)Message)
|
||||
@if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
@((MarkupString)" ")<NavLink href="@NavigateUrl("admin/log")">View Details</NavLink>
|
||||
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
|
||||
}
|
||||
@if (ModuleState != null)
|
||||
{
|
||||
@if (ModuleState.RenderMode == RenderModes.Static)
|
||||
{
|
||||
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
|
||||
}
|
||||
}
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="DismissModal"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -24,6 +34,9 @@
|
||||
[Parameter]
|
||||
public MessageType Type { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderModeBoundary Parent { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_message = Message;
|
||||
@ -54,10 +67,15 @@
|
||||
|
||||
return classname;
|
||||
}
|
||||
|
||||
private void DismissModal()
|
||||
private void CloseMessage(MouseEventArgs e)
|
||||
{
|
||||
_message = "";
|
||||
StateHasChanged();
|
||||
if(Parent != null)
|
||||
{
|
||||
Parent.DismissMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,29 +6,33 @@
|
||||
|
||||
@if (ItemList != null)
|
||||
{
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
<div class="input-group my-3">
|
||||
<input id="search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<form autocomplete="off">
|
||||
<div class="input-group my-3 @SearchBoxClass">
|
||||
<input type="text" id="pagersearch" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
@ -36,33 +40,98 @@
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
<form method="post" autocomplete="off" @formname="PagerForm" @onsubmit="Search" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<div class="input-group my-3 @SearchBoxClass">
|
||||
<input type="text" id="pagersearch" name="_search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="submit" class="btn btn-primary">@SharedLocalizer["Search"]</button>
|
||||
<a class="btn btn-secondary" href="@PageUrl(1, "")">@SharedLocalizer["Reset"]</a>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
|
||||
@if (Format == "Table" && Row != null)
|
||||
{
|
||||
<div class="table-responsive">
|
||||
@ -126,20 +195,23 @@
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
@ -147,33 +219,83 @@
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link shadow-none" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link shadow-none" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link shadow-none" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -237,6 +359,21 @@
|
||||
[Parameter]
|
||||
public string SearchProperties { get; set; } // comma delimited list of property names to include in search
|
||||
|
||||
[Parameter]
|
||||
public string SearchBoxClass { get; set; } // class for Search box
|
||||
|
||||
[Parameter]
|
||||
public string Parameters { get; set; } // optional - querystring parameters in the form of "id=x&name=y" used in static render mode
|
||||
|
||||
[SupplyParameterFromForm(FormName = "PagerForm")]
|
||||
public string _Search { get => ""; set => _search = value; }
|
||||
|
||||
/// <summary>
|
||||
/// Accepted values are Start or Center or End. The default value is Center
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PaginationAlignment { get; set; } = "center"; // Alignment of the Page Numbering start, center, end
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
@ -292,6 +429,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("search"))
|
||||
{
|
||||
_search = PageState.QueryString["search"];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
AllItems = Items; // only used in search
|
||||
@ -316,6 +458,12 @@
|
||||
_displayPages = int.Parse(DisplayPages);
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
||||
{
|
||||
_page = page;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(CurrentPage))
|
||||
{
|
||||
_page = int.Parse(CurrentPage);
|
||||
@ -324,6 +472,7 @@
|
||||
{
|
||||
_page = 1;
|
||||
}
|
||||
}
|
||||
if (_page < 1) _page = 1;
|
||||
|
||||
_startPage = 0;
|
||||
@ -465,4 +614,25 @@
|
||||
}
|
||||
return string.Join(",", properties);
|
||||
}
|
||||
|
||||
private string PageUrl(int page, string search)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>(PageState.QueryString);
|
||||
if (parameters.ContainsKey("page")) parameters.Remove("page");
|
||||
parameters.Add("page", page.ToString());
|
||||
if (parameters.ContainsKey("search")) parameters.Remove("search");
|
||||
if (!string.IsNullOrEmpty(search))
|
||||
{
|
||||
parameters.Add("search", search);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Parameters))
|
||||
{
|
||||
foreach (var parameter in Utilities.ParseQueryString(Parameters))
|
||||
{
|
||||
if (parameters.ContainsKey(parameter.Key)) parameters.Remove(parameter.Key);
|
||||
parameters.Add(parameter.Key, parameter.Value);
|
||||
}
|
||||
}
|
||||
return PageState.Route.AbsolutePath + Utilities.CreateQueryString(parameters);
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Modules.Controls
|
||||
{
|
||||
public class RichTextEditorInterop
|
||||
public class QuillEditorInterop
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public RichTextEditorInterop(IJSRuntime jsRuntime)
|
||||
public QuillEditorInterop(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
@ -105,13 +105,25 @@ namespace Oqtane.Modules.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public Task InsertImage(ElementReference quillElement, string imageUrl, string altText)
|
||||
public ValueTask<int> GetCurrentCursor(ElementReference quillElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _jsRuntime.InvokeAsync<int>("Oqtane.RichTextEditor.getCurrentCursor", quillElement);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new ValueTask<int>(Task.FromResult(0));
|
||||
}
|
||||
}
|
||||
|
||||
public Task InsertImage(ElementReference quillElement, string imageUrl, string altText, int editorIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeAsync<object>(
|
||||
"Oqtane.RichTextEditor.insertQuillImage",
|
||||
quillElement, imageUrl, altText);
|
||||
quillElement, imageUrl, altText, editorIndex);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
578
Oqtane.Client/Modules/Controls/QuillJSTextEditor.razor
Normal file
578
Oqtane.Client/Modules/Controls/QuillJSTextEditor.razor
Normal file
@ -0,0 +1,578 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@implements ITextEditor
|
||||
@inject ISettingService SettingService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<QuillJSTextEditor> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="quill-text-editor">
|
||||
<TabStrip ActiveTab="@_activetab">
|
||||
@if (_allowRichText)
|
||||
{
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (_allowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (!string.IsNullOrEmpty(_toolbarContent))
|
||||
{
|
||||
@((MarkupString)_toolbarContent)
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement"></div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
}
|
||||
@if (_allowRawHtml)
|
||||
{
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (_allowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
@if (ReadOnly)
|
||||
{
|
||||
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||
}
|
||||
</TabPanel>
|
||||
}
|
||||
@if (_allowSettings)
|
||||
{
|
||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||
<div class="quill-text-editor-settings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="Scope" ResourceKey="Scope" ResourceType="@resourceType" HelpText="Specify if settings are scoped to the module or site">Scope: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="Scope" class="form-select" value="@_scopeSetting" @onchange="(e => ScopeChanged(e))">
|
||||
<option value="Module">@SharedLocalizer["Module"]</option>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
<option value="Site">@SharedLocalizer["Site"]</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="AllowRichText" ResourceKey="AllowRichText" ResourceType="@resourceType" HelpText="Specify if editors can use the Rich Text Editor">Rich Text Editor? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="AllowRichText" class="form-select" @bind="@_allowRichTextSetting" 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="AllowRawHtml" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify if editors can use the Raw HTML Editor">Raw HTML Editor? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="AllowRawHtml" class="form-select" @bind="@_allowRawHtmlSetting" 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="AllowFileManagement" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify if editors can upload and insert images">Insert Images? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="AllowFileManagement" class="form-select" @bind="@_allowFileManagementSetting" 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="Theme" ResourceKey="Theme" ResourceType="@resourceType" HelpText="Specify the Rich Text Editor's theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="Theme" class="form-control" @bind="_themeSetting" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="DebugLevel" ResourceKey="DebugLevel" ResourceType="@resourceType" HelpText="Specify the Debug Level">Debug Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="DebugLevel" class="form-select" @bind="_debugLevelSetting">
|
||||
@foreach (var level in _debugLevels)
|
||||
{
|
||||
<option value="@level">@level</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ToolbarContent" ResourceKey="ToolbarContent" ResourceType="@resourceType" HelpText="Specify any toolbar content to customize the Rich Text Editor">Toolbar Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="ToolbarContent" class="form-control" @bind="_toolbarContentSetting" rows="3" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
<button type="button" class="btn btn-success" @onclick="@(async () => await UpdateSettings())">@Localizer["SaveSettings"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public string Name => "QuillJS";
|
||||
|
||||
private string resourceType = "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client";
|
||||
|
||||
private bool _settingsLoaded;
|
||||
private bool _initialized = false;
|
||||
|
||||
private QuillEditorInterop _interop;
|
||||
private FileManager _fileManager;
|
||||
private string _activetab = "Rich";
|
||||
private bool _allowSettings = false;
|
||||
|
||||
private bool _allowFileManagement = false;
|
||||
private bool _allowRawHtml = false;
|
||||
private bool _allowRichText = false;
|
||||
private string _theme = "snow";
|
||||
private string _debugLevel = "info";
|
||||
private string _toolbarContent = string.Empty;
|
||||
|
||||
private string _scopeSetting = "Module";
|
||||
private string _allowFileManagementSetting = "False";
|
||||
private string _allowRawHtmlSetting = "False";
|
||||
private string _allowRichTextSetting = "False";
|
||||
private string _themeSetting = "snow";
|
||||
private string _debugLevelSetting = "info";
|
||||
private string _toolbarContentSetting = string.Empty;
|
||||
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _richfilemanager = false;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
|
||||
private bool _rawfilemanager = false;
|
||||
private string _rawhtmlid = "RawHtmlEditor_" + Guid.NewGuid().ToString("N");
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
|
||||
private string _message = string.Empty;
|
||||
private bool _contentchanged = false;
|
||||
private int _editorIndex;
|
||||
|
||||
private List<string> _debugLevels = new List<string> { "info", "log", "warn", "error" };
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
// the following parameters were supported by the original RichTextEditor and can be passed as optional static parameters
|
||||
|
||||
[Parameter]
|
||||
public bool? AllowFileManagement { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool? AllowRichText { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool? AllowRawHtml { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; }
|
||||
|
||||
public override List<Resource> Resources { get; set; } = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js", Location = ResourceLocation.Body },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js", Location = ResourceLocation.Body },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js", Location = ResourceLocation.Body }
|
||||
};
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_interop = new QuillEditorInterop(JSRuntime);
|
||||
|
||||
if (string.IsNullOrEmpty(Placeholder))
|
||||
{
|
||||
Placeholder = Localizer["Placeholder"];
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
LoadSettings();
|
||||
|
||||
if (!_allowRichText)
|
||||
{
|
||||
_activetab = "Raw";
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
// include CSS theme
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.IncludeLink("", "stylesheet", $"{PageState?.Alias.BaseUrl}/css/quill/quill.{_theme}.css", "text/css", "", "", "");
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (_allowRichText)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await _interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
_theme,
|
||||
_debugLevel);
|
||||
|
||||
await _interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
|
||||
// preserve a copy of the content (Quill sanitizes content so we need to retrieve it from the editor as it may have been modified)
|
||||
_originalrichhtml = await _interop.GetHtml(_editorElement);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
if (_contentchanged)
|
||||
{
|
||||
// reload editor if Content passed to component has changed
|
||||
await _interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
_originalrichhtml = await _interop.GetHtml(_editorElement);
|
||||
|
||||
_contentchanged = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// preserve changed content on re-render event
|
||||
var richhtml = await _interop.GetHtml(_editorElement);
|
||||
if (richhtml != _richhtml)
|
||||
{
|
||||
_richhtml = richhtml;
|
||||
await _interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(string content)
|
||||
{
|
||||
_richhtml = content;
|
||||
_rawhtml = content;
|
||||
_originalrichhtml = "";
|
||||
_richhtml = content;
|
||||
if (!_contentchanged)
|
||||
{
|
||||
_contentchanged = content != _originalrawhtml;
|
||||
}
|
||||
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetContent()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
if (_rawhtml != _originalrawhtml)
|
||||
{
|
||||
return _rawhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
var richhtml = "";
|
||||
|
||||
if (_allowRichText)
|
||||
{
|
||||
richhtml = await _interop.GetHtml(_editorElement);
|
||||
}
|
||||
|
||||
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml))
|
||||
{
|
||||
// convert Quill's empty content to empty string
|
||||
if (richhtml == "<p><br></p>")
|
||||
{
|
||||
richhtml = string.Empty;
|
||||
}
|
||||
return richhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return original raw html content
|
||||
return _originalrawhtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseRichFileManager()
|
||||
{
|
||||
_richfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void CloseRawFileManager()
|
||||
{
|
||||
_rawfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
if (_rawhtml != _originalrawhtml)
|
||||
{
|
||||
return _rawhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
var richhtml = "";
|
||||
|
||||
if (_allowRichText)
|
||||
{
|
||||
richhtml = await _interop.GetHtml(_editorElement);
|
||||
}
|
||||
|
||||
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml))
|
||||
{
|
||||
// convert Quill's empty content to empty string
|
||||
if (richhtml == "<p><br></p>")
|
||||
{
|
||||
richhtml = string.Empty;
|
||||
}
|
||||
return richhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return original raw html content
|
||||
return _originalrawhtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertRichImage()
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_richfilemanager)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
await _interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name), _editorIndex);
|
||||
_richhtml = await _interop.GetHtml(_editorElement);
|
||||
_richfilemanager = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_editorIndex = await _interop.GetCurrentCursor(_editorElement);
|
||||
_richfilemanager = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task InsertRawImage()
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_rawfilemanager)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
int pos = await interop.GetCaretPosition(_rawhtmlid);
|
||||
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
|
||||
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
||||
_rawfilemanager = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_rawfilemanager = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void ScopeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_scopeSetting = (string)e.Value;
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private void LoadSettings(bool reload = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_settingsLoaded || reload)
|
||||
{
|
||||
_allowFileManagement = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowFileManagement", "True"));
|
||||
_allowRawHtml = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowRawHtml", "True"));
|
||||
_allowRichText = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowRichText", "True"));
|
||||
_theme = GetSetting("Component", "QuillTextEditor_Theme", "snow");
|
||||
_debugLevel = GetSetting("Component", "QuillTextEditor_DebugLevel", "info");
|
||||
_toolbarContent = GetSetting("Component", "QuillTextEditor_ToolbarContent", string.Empty);
|
||||
|
||||
// optional static parameter overrides
|
||||
if (AllowFileManagement != null) _allowFileManagement = AllowFileManagement.Value;
|
||||
if (AllowRichText != null) _allowRichText = AllowRichText.Value;
|
||||
if (AllowRawHtml != null) _allowRawHtml = AllowRawHtml.Value;
|
||||
if (!string.IsNullOrEmpty(Theme)) _theme = Theme;
|
||||
if (!string.IsNullOrEmpty(DebugLevel)) _debugLevel = DebugLevel;
|
||||
}
|
||||
|
||||
_allowSettings = PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList);
|
||||
if (_allowSettings)
|
||||
{
|
||||
_allowFileManagementSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowFileManagement", "True");
|
||||
_allowRawHtmlSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowRawHtml", "True");
|
||||
_allowRichTextSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowRichText", "True");
|
||||
_themeSetting = GetSetting(_scopeSetting, "QuillTextEditor_Theme", "snow");
|
||||
_debugLevelSetting = GetSetting(_scopeSetting, "QuillTextEditor_DebugLevel", "info");
|
||||
_toolbarContentSetting = GetSetting(_scopeSetting, "QuillTextEditor_ToolbarContent", string.Empty);
|
||||
}
|
||||
|
||||
_settingsLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSetting(string scope, string settingName, string defaultValue)
|
||||
{
|
||||
var settingValue = "";
|
||||
switch (scope)
|
||||
{
|
||||
case "Component":
|
||||
settingValue = SettingService.GetSetting(PageState.Site.Settings, settingName, defaultValue);
|
||||
settingValue = SettingService.GetSetting(ModuleState.Settings, settingName, settingValue);
|
||||
break;
|
||||
case "Site":
|
||||
settingValue = SettingService.GetSetting(PageState.Site.Settings, settingName, defaultValue);
|
||||
break;
|
||||
case "Module":
|
||||
settingValue = SettingService.GetSetting(ModuleState.Settings, settingName, defaultValue);
|
||||
break;
|
||||
}
|
||||
return settingValue;
|
||||
}
|
||||
|
||||
private async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_scopeSetting == "Site" && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowFileManagement", _allowFileManagementSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRawHtml", _allowRawHtmlSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRichText", _allowRichTextSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_Theme", _themeSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_DebugLevel", _debugLevelSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_ToolbarContent", _toolbarContentSetting);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
}
|
||||
else if (_scopeSetting == "Module")
|
||||
{
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowFileManagement", _allowFileManagementSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRawHtml", _allowRawHtmlSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRichText", _allowRichTextSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_Theme", _themeSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_DebugLevel", _debugLevelSetting);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_ToolbarContent", _toolbarContentSetting);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings,ModuleState.ModuleId);
|
||||
}
|
||||
LoadSettings(true);
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +1,22 @@
|
||||
@using System.Text.RegularExpressions
|
||||
@using Microsoft.AspNetCore.Components.Rendering
|
||||
@using Microsoft.Extensions.DependencyInjection
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<RichTextEditor> Localizer
|
||||
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
<div class="col">
|
||||
<TabStrip>
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||
}
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
@if (ReadOnly)
|
||||
{
|
||||
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||
}
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
@_textEditorComponent
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _richfilemanager = false;
|
||||
private FileManager _fileManager;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
private bool _rawfilemanager = false;
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _textEditorType;
|
||||
private RenderFragment _textEditorComponent;
|
||||
private ITextEditor _textEditor;
|
||||
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
@ -122,159 +25,103 @@
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
public string Provider { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRawHtml { get; set; } = true;
|
||||
[Parameter(CaptureUnmatchedValues = true)]
|
||||
public Dictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||
};
|
||||
_textEditorType = GetTextEditorType();
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_richhtml = Content;
|
||||
_rawhtml = Content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
_textEditorComponent = (builder) =>
|
||||
{
|
||||
CreateTextEditor(builder);
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if(_textEditor != null)
|
||||
{
|
||||
_textEditor.Initialize(Content);
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
|
||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseRichFileManager()
|
||||
{
|
||||
_richfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void CloseRawFileManager()
|
||||
{
|
||||
_rawfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void RefreshRichText()
|
||||
{
|
||||
_richhtml = _rawhtml;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
_rawhtml = await interop.GetHtml(_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
if (_rawhtml != _originalrawhtml)
|
||||
return await _textEditor.GetContent();
|
||||
}
|
||||
|
||||
private void CreateTextEditor(RenderTreeBuilder builder)
|
||||
{
|
||||
return _rawhtml;
|
||||
if(!string.IsNullOrEmpty(_textEditorType))
|
||||
{
|
||||
var editorType = Type.GetType(_textEditorType);
|
||||
if (editorType != null)
|
||||
{
|
||||
builder.OpenComponent(0, editorType);
|
||||
|
||||
var attributes = new Dictionary<string, object>
|
||||
{
|
||||
{ "Placeholder", Placeholder },
|
||||
{ "ReadOnly", ReadOnly }
|
||||
};
|
||||
|
||||
if (AdditionalAttributes != null)
|
||||
{
|
||||
foreach(var key in AdditionalAttributes.Keys)
|
||||
{
|
||||
if(!attributes.ContainsKey(key))
|
||||
{
|
||||
attributes.Add(key, AdditionalAttributes[key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// return rich text content if it has changed
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
var richhtml = await interop.GetHtml(_editorElement);
|
||||
if (richhtml != _originalrichhtml)
|
||||
{
|
||||
return richhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return original raw html content
|
||||
return _originalrawhtml;
|
||||
attributes[key] = AdditionalAttributes[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertRichImage()
|
||||
var index = 1;
|
||||
foreach(var name in attributes.Keys)
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_richfilemanager)
|
||||
if (editorType.GetProperty(name) != null)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
||||
_richfilemanager = false;
|
||||
builder.AddAttribute(index++, name, attributes[name]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_richfilemanager = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task InsertRawImage()
|
||||
builder.AddComponentReferenceCapture(index, (c) =>
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_rawfilemanager)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
||||
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
|
||||
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
||||
_rawfilemanager = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
_textEditor = (ITextEditor)c;
|
||||
});
|
||||
builder.CloseComponent();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_rawfilemanager = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
|
||||
private string GetTextEditorType()
|
||||
{
|
||||
const string EditorSettingName = "TextEditor";
|
||||
|
||||
if(!string.IsNullOrEmpty(Provider))
|
||||
{
|
||||
var provider = ServiceProvider.GetServices<ITextEditor>().FirstOrDefault(i => i.Name.Equals(Provider, StringComparison.OrdinalIgnoreCase));
|
||||
if(provider != null)
|
||||
{
|
||||
return Utilities.GetFullTypeName(provider.GetType().AssemblyQualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
return SettingService.GetSetting(PageState.Site.Settings, EditorSettingName, Constants.DefaultTextEditor);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits LocalizableComponent
|
||||
|
||||
<div class="d-flex mt-2">
|
||||
@if (IsVisible)
|
||||
{
|
||||
<div class="d-flex mt-2">
|
||||
<div>
|
||||
<a data-bs-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||
<h5>@_heading</h5>
|
||||
@ -12,16 +14,17 @@
|
||||
<i class="oi oi-chevron-bottom"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="collapse @_show" id="@Name">
|
||||
</div>
|
||||
<div class="collapse @_show" id="@Name">
|
||||
@if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _heading = string.Empty;
|
||||
@ -40,6 +43,9 @@
|
||||
[Parameter]
|
||||
public string Expanded { get; set; } // optional - will default to false if not provided
|
||||
|
||||
[Parameter]
|
||||
public bool IsVisible { get; set; } = true;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet(); // must be included to call method in LocalizableComponent
|
||||
|
@ -36,14 +36,7 @@ else
|
||||
|
||||
Parent.AddTabPanel((TabPanel)this);
|
||||
|
||||
if (string.IsNullOrEmpty(Heading))
|
||||
{
|
||||
Heading = Localize(nameof(Name), Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Heading = Localize(nameof(Heading), Heading);
|
||||
}
|
||||
Heading = string.IsNullOrEmpty(Heading) ? Localize(nameof(Name), Name) : Localize(nameof(Heading), Heading);
|
||||
}
|
||||
|
||||
public string DisplayHeading()
|
||||
|
@ -23,7 +23,7 @@
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-content @TabContentClass">
|
||||
<br />
|
||||
@ChildContent
|
||||
</div>
|
||||
@ -47,6 +47,9 @@
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - used to uniquely identify an instance of a tab strip component (will be set automatically if no value provided)
|
||||
|
||||
[Parameter]
|
||||
public string TabContentClass { get; set; } // optional - to extend the TabContent div.
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Id))
|
||||
|
33
Oqtane.Client/Modules/Controls/TextAreaTextEditor.razor
Normal file
33
Oqtane.Client/Modules/Controls/TextAreaTextEditor.razor
Normal file
@ -0,0 +1,33 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@implements ITextEditor
|
||||
|
||||
<div class="text-area-editor">
|
||||
<textarea @bind="_content" @ref="_editor" placeholder="@Placeholder" readonly="@ReadOnly" />
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public string Name => "TextArea";
|
||||
|
||||
private ElementReference _editor;
|
||||
private string _content;
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
public void Initialize(string content)
|
||||
{
|
||||
_content = content;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetContent()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
return _content;
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@ -51,15 +51,7 @@
|
||||
|
||||
public override string Title => "Edit Html/Text";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||
};
|
||||
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private bool _allowfilemanagement;
|
||||
private bool _allowrawhtml;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
@ -72,8 +64,6 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
|
||||
await LoadContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -4,22 +4,32 @@
|
||||
@inject IHtmlTextService HtmlTextService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@((MarkupString)content)
|
||||
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<br />
|
||||
<div class="text-center mb-2">
|
||||
<ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
|
||||
@((MarkupString)content)
|
||||
|
||||
@if (PageState.EditMode && content.Length > 3000)
|
||||
{
|
||||
<div class="text-center mt-2">
|
||||
<ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string content = "";
|
||||
|
||||
public override string RenderMode => RenderModes.Static;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ShouldRender())
|
||||
{
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
@ -27,6 +37,11 @@
|
||||
content = htmltext.Content;
|
||||
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
||||
}
|
||||
else
|
||||
{
|
||||
content = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace Oqtane.Modules.HtmlText
|
||||
Version = "1.0.1",
|
||||
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
||||
ReleaseVersions = "1.0.0,1.0.1",
|
||||
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||
SettingsType = string.Empty,
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
@ -9,7 +8,7 @@ using Oqtane.Shared;
|
||||
namespace Oqtane.Modules.HtmlText.Services
|
||||
{
|
||||
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService, IService
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService, IClientService
|
||||
{
|
||||
public HtmlTextService(HttpClient http, SiteState siteState) : base(http, siteState) {}
|
||||
|
||||
@ -30,9 +29,9 @@ namespace Oqtane.Modules.HtmlText.Services
|
||||
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||
}
|
||||
|
||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||
public async Task<Models.HtmlText> AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||
{
|
||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||
return await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||
}
|
||||
|
||||
public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId)
|
||||
|
@ -13,7 +13,7 @@ namespace Oqtane.Modules.HtmlText.Services
|
||||
|
||||
Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId);
|
||||
|
||||
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||
Task<Models.HtmlText> AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||
|
||||
Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
@implements Oqtane.Interfaces.ISettingsControl
|
||||
@inject IStringLocalizer<Settings> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="files" class="form-select" @bind="@_allowfilemanagement">
|
||||
<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="files" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="files" class="form-select" @bind="@_allowrawhtml">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||
private string _allowfilemanagement;
|
||||
private string _allowrawhtml;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
||||
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ namespace Oqtane.Modules
|
||||
protected Module ModuleState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ModuleInstance ModuleInstance { get; set; }
|
||||
public RenderModeBoundary RenderModeBoundary { get; set; }
|
||||
|
||||
// optional interface properties
|
||||
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
|
||||
@ -50,6 +50,10 @@ namespace Oqtane.Modules
|
||||
|
||||
public virtual List<Resource> Resources { get; set; }
|
||||
|
||||
public virtual string RenderMode { get { return RenderModes.Interactive; } } // interactive by default
|
||||
|
||||
public virtual bool? Prerender { get { return null; } } // allows the Site Prerender property to be overridden
|
||||
|
||||
// url parameters
|
||||
public virtual string UrlParametersTemplate { get; set; }
|
||||
|
||||
@ -77,7 +81,7 @@ namespace Oqtane.Modules
|
||||
{
|
||||
if (PageState.Page.Resources != null)
|
||||
{
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site && item.Namespace == type.Namespace).ToList();
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Module && item.Namespace == type.Namespace).ToList();
|
||||
}
|
||||
}
|
||||
else // modulecontrolbase
|
||||
@ -87,12 +91,14 @@ namespace Oqtane.Modules
|
||||
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
|
||||
}
|
||||
}
|
||||
if (resources != null &&resources.Any())
|
||||
if (resources != null && resources.Any())
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var scripts = new List<object>();
|
||||
var inline = 0;
|
||||
foreach (Resource resource in resources)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
@ -105,6 +111,7 @@ namespace Oqtane.Modules
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scripts.Any())
|
||||
{
|
||||
await interop.IncludeScripts(scripts.ToArray());
|
||||
@ -113,6 +120,11 @@ namespace Oqtane.Modules
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShouldRender()
|
||||
{
|
||||
return PageState?.RenderId == ModuleState?.RenderId;
|
||||
}
|
||||
|
||||
// path method
|
||||
|
||||
public string ModulePath()
|
||||
@ -122,6 +134,7 @@ namespace Oqtane.Modules
|
||||
|
||||
// url methods
|
||||
|
||||
// navigate url
|
||||
public string NavigateUrl()
|
||||
{
|
||||
return NavigateUrl(PageState.Page.Path);
|
||||
@ -137,24 +150,65 @@ namespace Oqtane.Modules
|
||||
return NavigateUrl(PageState.Page.Path, refresh);
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, string parameters)
|
||||
public string NavigateUrl(string path, string querystring)
|
||||
{
|
||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
|
||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring);
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, Dictionary<string, string> querystring)
|
||||
{
|
||||
return NavigateUrl(path, Utilities.CreateQueryString(querystring));
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, bool refresh)
|
||||
{
|
||||
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
|
||||
return NavigateUrl(path, refresh ? "refresh" : "");
|
||||
}
|
||||
|
||||
public string NavigateUrl(int moduleId, string action)
|
||||
{
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, "");
|
||||
}
|
||||
|
||||
public string NavigateUrl(int moduleId, string action, string querystring)
|
||||
{
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
public string NavigateUrl(int moduleId, string action, Dictionary<string, string> querystring)
|
||||
{
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, int moduleId, string action)
|
||||
{
|
||||
return EditUrl(path, moduleId, action, "");
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, int moduleId, string action, string querystring)
|
||||
{
|
||||
return EditUrl(path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
public string NavigateUrl(string path, int moduleId, string action, Dictionary<string, string> querystring)
|
||||
{
|
||||
return EditUrl(path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
// edit url
|
||||
public string EditUrl(string action)
|
||||
{
|
||||
return EditUrl(ModuleState.ModuleId, action);
|
||||
}
|
||||
|
||||
public string EditUrl(string action, string parameters)
|
||||
public string EditUrl(string action, string querystring)
|
||||
{
|
||||
return EditUrl(ModuleState.ModuleId, action, parameters);
|
||||
return EditUrl(ModuleState.ModuleId, action, querystring);
|
||||
}
|
||||
|
||||
public string EditUrl(string action, Dictionary<string, string> querystring)
|
||||
{
|
||||
return EditUrl(ModuleState.ModuleId, action, querystring);
|
||||
}
|
||||
|
||||
public string EditUrl(int moduleId, string action)
|
||||
@ -162,16 +216,27 @@ namespace Oqtane.Modules
|
||||
return EditUrl(moduleId, action, "");
|
||||
}
|
||||
|
||||
public string EditUrl(int moduleId, string action, string parameters)
|
||||
public string EditUrl(int moduleId, string action, string querystring)
|
||||
{
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, parameters);
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
public string EditUrl(string path, int moduleid, string action, string parameters)
|
||||
public string EditUrl(int moduleId, string action, Dictionary<string, string> querystring)
|
||||
{
|
||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
||||
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
|
||||
}
|
||||
|
||||
public string EditUrl(string path, int moduleid, string action, string querystring)
|
||||
{
|
||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring);
|
||||
}
|
||||
|
||||
public string EditUrl(string path, int moduleid, string action, Dictionary<string, string> querystring)
|
||||
{
|
||||
return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring));
|
||||
}
|
||||
|
||||
// file url
|
||||
public string FileUrl(string folderpath, string filename)
|
||||
{
|
||||
return FileUrl(folderpath, filename, false);
|
||||
@ -191,6 +256,8 @@ namespace Oqtane.Modules
|
||||
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||
}
|
||||
|
||||
// image url
|
||||
|
||||
public string ImageUrl(int fileid, int width, int height)
|
||||
{
|
||||
return ImageUrl(fileid, width, height, "");
|
||||
@ -261,22 +328,27 @@ namespace Oqtane.Modules
|
||||
// UI methods
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(message, type);
|
||||
AddModuleMessage(message, type, "top");
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
RenderModeBoundary.AddModuleMessage(message, type, position);
|
||||
}
|
||||
|
||||
public void ClearModuleMessage()
|
||||
{
|
||||
ModuleInstance.AddModuleMessage("", MessageType.Undefined);
|
||||
RenderModeBoundary.AddModuleMessage("", MessageType.Undefined);
|
||||
}
|
||||
|
||||
public void ShowProgressIndicator()
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
RenderModeBoundary.ShowProgressIndicator();
|
||||
}
|
||||
|
||||
public void HideProgressIndicator()
|
||||
{
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
RenderModeBoundary.HideProgressIndicator();
|
||||
}
|
||||
|
||||
public void SetModuleTitle(string title)
|
||||
@ -476,5 +548,8 @@ namespace Oqtane.Modules
|
||||
{
|
||||
return Utilities.FileUrl(PageState.Alias, fileid, asAttachment);
|
||||
}
|
||||
|
||||
// Referencing ModuleInstance methods from ModuleBase is deprecated. Use the ModuleBase methods instead
|
||||
public ModuleInstance ModuleInstance { get { return new ModuleInstance(); } }
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>5.0.0</Version>
|
||||
<Version>6.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -12,22 +12,20 @@
|
||||
<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/v5.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
@ -13,13 +12,13 @@ using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
|
||||
namespace Oqtane.Client
|
||||
@ -41,10 +40,10 @@ namespace Oqtane.Client
|
||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
// register auth services
|
||||
builder.Services.AddOqtaneAuthorization();
|
||||
builder.Services.AddOqtaneAuthentication();
|
||||
|
||||
// register scoped core services
|
||||
builder.Services.AddOqtaneScopedServices();
|
||||
builder.Services.AddOqtaneClientScopedServices();
|
||||
|
||||
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||
|
||||
@ -220,6 +219,16 @@ namespace Oqtane.Client
|
||||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
implementationTypes = assembly.GetInterfaces<IClientService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
if (implementationType.AssemblyQualifiedName != null)
|
||||
{
|
||||
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -248,7 +257,7 @@ namespace Oqtane.Client
|
||||
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||
var interop = new Interop(jsRuntime);
|
||||
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
||||
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
||||
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICulture.Name;
|
||||
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
||||
var cultures = await localizationService.GetCulturesAsync(false);
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Models;
|
||||
@ -16,12 +14,10 @@ namespace Oqtane.Providers
|
||||
public class IdentityAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly NavigationManager _navigationManager;
|
||||
|
||||
public IdentityAuthenticationStateProvider(IServiceProvider serviceProvider, NavigationManager navigationManager)
|
||||
public IdentityAuthenticationStateProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_navigationManager = navigationManager;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -153,7 +153,7 @@
|
||||
<data name="Integrated" xml:space="preserve">
|
||||
<value>Integrated</value>
|
||||
</data>
|
||||
<data name="Encryption,Text" xml:space="preserve">
|
||||
<data name="Encryption.Text" xml:space="preserve">
|
||||
<value>Encryption:</value>
|
||||
</data>
|
||||
<data name="Encryption.HelpText" xml:space="preserve">
|
||||
|
@ -183,4 +183,7 @@
|
||||
<data name="Template" xml:space="preserve">
|
||||
<value>Select a site template</value>
|
||||
</data>
|
||||
<data name="Message.Username.Invalid" xml:space="preserve">
|
||||
<value>The Username Provided Does Not Meet The System Requirement, It Can Only Contains Letters Or Digits.</value>
|
||||
</data>
|
||||
</root>
|
@ -150,14 +150,11 @@
|
||||
<data name="Name.HelpText" xml:space="preserve">
|
||||
<value>Enter the folder name</value>
|
||||
</data>
|
||||
<data name="Permissions.HelpText" xml:space="preserve">
|
||||
<value>Select the permissions you want for the folder</value>
|
||||
</data>
|
||||
<data name="Parent.Text" xml:space="preserve">
|
||||
<value>Parent: </value>
|
||||
</data>
|
||||
<data name="Permissions.Text" xml:space="preserve">
|
||||
<value>Permissions: </value>
|
||||
<data name="Permissions.Heading" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
</data>
|
||||
<data name="DeleteFolder.Header" xml:space="preserve">
|
||||
<value>Delete Folder</value>
|
||||
@ -195,4 +192,10 @@
|
||||
<data name="Folder Management" xml:space="preserve">
|
||||
<value>Folder Management</value>
|
||||
</data>
|
||||
<data name="Message.Folder.Duplicate" xml:space="preserve">
|
||||
<value>Folder Name Specified Already Exists In Parent</value>
|
||||
</data>
|
||||
<data name="Settings.Heading" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
</root>
|
@ -192,4 +192,7 @@
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</data>
|
||||
<data name="Message.StartEndDateError" xml:space="preserve">
|
||||
<value>Start Date cannot be after End Date.</value>
|
||||
</data>
|
||||
</root>
|
@ -144,17 +144,8 @@
|
||||
<data name="Month" xml:space="preserve">
|
||||
<value>Month(s)</value>
|
||||
</data>
|
||||
<data name="Error.Job.Delete" xml:space="preserve">
|
||||
<value>Error Deleting Job</value>
|
||||
</data>
|
||||
<data name="ViewJobs.Text" xml:space="preserve">
|
||||
<value>View Logs</value>
|
||||
</data>
|
||||
<data name="DeleteJob.Header" xml:space="preserve">
|
||||
<value>Delete Job</value>
|
||||
</data>
|
||||
<data name="DeleteJob.Message" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete This Job?</value>
|
||||
<data name="ViewLogs.Text" xml:space="preserve">
|
||||
<value>View All Logs</value>
|
||||
</data>
|
||||
<data name="Frequency" xml:space="preserve">
|
||||
<value>Frequency</value>
|
||||
@ -165,9 +156,6 @@
|
||||
<data name="Stop" xml:space="preserve">
|
||||
<value>Stop</value>
|
||||
</data>
|
||||
<data name="DeleteJob.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="EditJob.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
|
@ -132,4 +132,7 @@
|
||||
<data name="Failed" xml:space="preserve">
|
||||
<value>Failed</value>
|
||||
</data>
|
||||
<data name="Refresh" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
</root>
|
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
@ -0,0 +1,153 @@
|
||||
<?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="Error.Language.Edit" xml:space="preserve">
|
||||
<value>Error Updating Language</value>
|
||||
</data>
|
||||
<data name="Name.HelpText" xml:space="preserve">
|
||||
<value>Name Of The Langauage</value>
|
||||
</data>
|
||||
<data name="IsDefault.HelpText" xml:space="preserve">
|
||||
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
|
||||
</data>
|
||||
<data name="Name.Text" xml:space="preserve">
|
||||
<value>Name:</value>
|
||||
</data>
|
||||
<data name="IsDefault.Text" xml:space="preserve">
|
||||
<value>Default?</value>
|
||||
</data>
|
||||
<data name="Success.Language.Download" xml:space="preserve">
|
||||
<value>Translation Package Saved Successfully. You Must <a href={0}>Restart</a> To Complete The Installation.</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||
<value>Upload one or more translation packages.</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||
<value>Translation</value>
|
||||
</data>
|
||||
<data name="Manage.Heading" xml:space="preserve">
|
||||
<value>Manage</value>
|
||||
</data>
|
||||
<data name="Upload.Heading" xml:space="preserve">
|
||||
<value>Upload</value>
|
||||
</data>
|
||||
<data name="Error.Language.Load" xml:space="preserve">
|
||||
<value>Error Loading Language</value>
|
||||
</data>
|
||||
</root>
|
@ -204,8 +204,8 @@
|
||||
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||
<data name="ExternalLoginStatus.MissingClaims" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide All Of The Required Information. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
@ -225,4 +225,10 @@
|
||||
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
||||
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.ReviewClaims" xml:space="preserve">
|
||||
<value>The Review Claims Option Was Enabled In External Login Settings. Please Visit The Event Log To View The Claims Returned By The Provider.</value>
|
||||
</data>
|
||||
<data name="Register" xml:space="preserve">
|
||||
<value>Register as new user?</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -210,4 +210,16 @@
|
||||
<data name="Success.SaveSiteSettings" xml:space="preserve">
|
||||
<value>Settings Saved Successfully</value>
|
||||
</data>
|
||||
<data name="DeleteLogs.Header" xml:space="preserve">
|
||||
<value>Clear Events</value>
|
||||
</data>
|
||||
<data name="DeleteLogs.Message" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Remove All Log Events?</value>
|
||||
</data>
|
||||
<data name="DeleteLogs.Text" xml:space="preserve">
|
||||
<value>Clear Events</value>
|
||||
</data>
|
||||
<data name="Error.DeleteLogs" xml:space="preserve">
|
||||
<value>Error Deleting Log Events</value>
|
||||
</data>
|
||||
</root>
|
@ -130,16 +130,16 @@
|
||||
<value>Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment</value>
|
||||
</data>
|
||||
<data name="Message.Require.ValidName" xml:space="preserve">
|
||||
<value>You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template</value>
|
||||
<value>You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same Or Contain The Word "Oqtane" ) And Choose A Template</value>
|
||||
</data>
|
||||
<data name="Message.Require.ValidDescription" xml:space="preserve">
|
||||
<value>You Must Provide A Valid Description (ie. No Punctuation)</value>
|
||||
</data>
|
||||
<data name="OwnerName.HelpText" xml:space="preserve">
|
||||
<value>Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.</value>
|
||||
<value>Enter the name of the organization who is developing this module. It should not contain spaces or punctuation or contain the word "oqtane".</value>
|
||||
</data>
|
||||
<data name="ModuleName.HelpText" xml:space="preserve">
|
||||
<value>Enter a name for this module. It should not contain spaces or punctuation.</value>
|
||||
<value>Enter a name for this module. It should not contain spaces or punctuation or contain the word "oqtane".</value>
|
||||
</data>
|
||||
<data name="Description.HelpText" xml:space="preserve">
|
||||
<value>Enter a short description for the module</value>
|
||||
|
@ -240,4 +240,10 @@
|
||||
<data name="Validate" xml:space="preserve">
|
||||
<value>Validate</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="Pages.Heading" xml:space="preserve">
|
||||
<value>Pages</value>
|
||||
</data>
|
||||
</root>
|
@ -127,7 +127,7 @@
|
||||
<value>Error Downloading Module</value>
|
||||
</data>
|
||||
<data name="Confirm.Module.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete The {0} Module?</value>
|
||||
<value>Are You Sure You Wish To Uninstall The {0} Module?</value>
|
||||
</data>
|
||||
<data name="Error.Module.Load" xml:space="preserve">
|
||||
<value>Error Loading Modules</value>
|
||||
@ -142,10 +142,10 @@
|
||||
<value>Install Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Header" xml:space="preserve">
|
||||
<value>Delete Module</value>
|
||||
<value>Uninstall Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
<value>Uninstall</value>
|
||||
</data>
|
||||
<data name="InUse" xml:space="preserve">
|
||||
<value>In Use?</value>
|
||||
|
@ -156,7 +156,7 @@
|
||||
<data name="Module.Text" xml:space="preserve">
|
||||
<value>Module:</value>
|
||||
</data>
|
||||
<data name="Module Settings" xml:space="preserve">
|
||||
<data name="ModuleSettings.Heading" xml:space="preserve">
|
||||
<value>Module Settings</value>
|
||||
</data>
|
||||
<data name="Pane.HelpText" xml:space="preserve">
|
||||
@ -165,4 +165,28 @@
|
||||
<data name="Pane.Text" xml:space="preserve">
|
||||
<value>Pane:</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this module is active</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.Text" xml:space="preserve">
|
||||
<value>Effective Date: </value>
|
||||
</data>
|
||||
<data name="ExpiryDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this module expires</value>
|
||||
</data>
|
||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||
<value>Expiry Date: </value>
|
||||
</data>
|
||||
<data name="Permissions.Text" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
</data>
|
||||
<data name="Permissions.Heading" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
</data>
|
||||
<data name="ContainerSettings.Heading" xml:space="preserve">
|
||||
<value>Container Settings</value>
|
||||
</data>
|
||||
<data name="ModuleSettings.Title" xml:space="preserve">
|
||||
<value>Module Settings</value>
|
||||
</data>
|
||||
</root>
|
@ -255,4 +255,16 @@
|
||||
<data name="Theme.Heading" xml:space="preserve">
|
||||
<value>Theme Settings</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this page is active</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.Text" xml:space="preserve">
|
||||
<value>Effective Date: </value>
|
||||
</data>
|
||||
<data name="ExpiryDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this page expires</value>
|
||||
</data>
|
||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||
<value>Expiry Date: </value>
|
||||
</data>
|
||||
</root>
|
@ -285,4 +285,16 @@
|
||||
<data name="ThemeChanged.Message" xml:space="preserve">
|
||||
<value>Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this page is active</value>
|
||||
</data>
|
||||
<data name="EffectiveDate.Text" xml:space="preserve">
|
||||
<value>Effective Date: </value>
|
||||
</data>
|
||||
<data name="ExpiryDate.HelpText" xml:space="preserve">
|
||||
<value>The date that this page expires</value>
|
||||
</data>
|
||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||
<value>Expiry Date: </value>
|
||||
</data>
|
||||
</root>
|
@ -138,4 +138,7 @@
|
||||
<data name="EditPage.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="Error.Page.Load" xml:space="preserve">
|
||||
<value>Error Loading Pages</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -195,4 +195,10 @@
|
||||
<data name="Rows.Text" xml:space="preserve">
|
||||
<value>Rows: </value>
|
||||
</data>
|
||||
<data name="Autocomplete.HelpText" xml:space="preserve">
|
||||
<value>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.</value>
|
||||
</data>
|
||||
<data name="Autocomplete.Text" xml:space="preserve">
|
||||
<value>Autocomplete: </value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -147,4 +147,7 @@
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="Detail.Text" xml:space="preserve">
|
||||
<value>Detail</value>
|
||||
</data>
|
||||
</root>
|
@ -177,4 +177,7 @@
|
||||
<data name="Username.Text" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="Login" xml:space="preserve">
|
||||
<value>Already have account? Login now.</value>
|
||||
</data>
|
||||
</root>
|
180
Oqtane.Client/Resources/Modules/Admin/Search/Index.resx
Normal file
180
Oqtane.Client/Resources/Modules/Admin/Search/Index.resx
Normal file
@ -0,0 +1,180 @@
|
||||
<?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="Enabled.Text" xml:space="preserve">
|
||||
<value>Enabled? </value>
|
||||
</data>
|
||||
<data name="Enabled.HelpText" xml:space="preserve">
|
||||
<value>Specify if search indexing is enabled</value>
|
||||
</data>
|
||||
<data name="LastIndexedOn.Text" xml:space="preserve">
|
||||
<value>Last Indexed: </value>
|
||||
</data>
|
||||
<data name="LastIndexedOn.HelpText" xml:space="preserve">
|
||||
<value>The date/time which the site was last indexed on</value>
|
||||
</data>
|
||||
<data name="IgnorePages.Text" xml:space="preserve">
|
||||
<value>Ignore Pages: </value>
|
||||
</data>
|
||||
<data name="IgnorePages.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of pages which should be ignored (based on page path)</value>
|
||||
</data>
|
||||
<data name="IgnoreEntities.Text" xml:space="preserve">
|
||||
<value>Ignore Entities: </value>
|
||||
</data>
|
||||
<data name="IgnoreEntities.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of entities which should be ignored. By default File entities are ignored.</value>
|
||||
</data>
|
||||
<data name="MinimumWordLength.Text" xml:space="preserve">
|
||||
<value>Word Length: </value>
|
||||
</data>
|
||||
<data name="MinimumWordLength.HelpText" xml:space="preserve">
|
||||
<value>Minimum length of a word to be indexed</value>
|
||||
</data>
|
||||
<data name="IgnoreWords.Text" xml:space="preserve">
|
||||
<value>Ignore Words: </value>
|
||||
</data>
|
||||
<data name="IgnoreWords.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of words which should be ignored</value>
|
||||
</data>
|
||||
<data name="Success.Save" xml:space="preserve">
|
||||
<value>Search Settings Saved Successfully. You Will Need Reindex For Your Changes To Be Reflected In The Search Results.</value>
|
||||
</data>
|
||||
<data name="Error.Save" xml:space="preserve">
|
||||
<value>Error Saving Search Settings</value>
|
||||
</data>
|
||||
<data name="SearchProvider.HelpText" xml:space="preserve">
|
||||
<value>Specify the search provider for this site</value>
|
||||
</data>
|
||||
<data name="SearchProvider.Text" xml:space="preserve">
|
||||
<value>Search Provider:</value>
|
||||
</data>
|
||||
<data name="Message.Reindex" xml:space="preserve">
|
||||
<value>The search index will be rebuilt for this site. Please be patient during the reindexing process.</value>
|
||||
</data>
|
||||
<data name="Reindex.Text" xml:space="preserve">
|
||||
<value>Reindex</value>
|
||||
</data>
|
||||
<data name="Reindex.Header" xml:space="preserve">
|
||||
<value>Reindex</value>
|
||||
</data>
|
||||
<data name="Reindex.Message" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Reindex Search Content?</value>
|
||||
</data>
|
||||
</root>
|
132
Oqtane.Client/Resources/Modules/Admin/SearchResults/Index.resx
Normal file
132
Oqtane.Client/Resources/Modules/Admin/SearchResults/Index.resx
Normal file
@ -0,0 +1,132 @@
|
||||
<?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="NoCriteria" xml:space="preserve">
|
||||
<value>You Must Provide Some Search Criteria</value>
|
||||
</data>
|
||||
<data name="NoResult" xml:space="preserve">
|
||||
<value>No Content Matches The Criteria Provided</value>
|
||||
</data>
|
||||
<data name="SearchLabel" xml:space="preserve">
|
||||
<value>Search:</value>
|
||||
</data>
|
||||
<data name="SearchPlaceholder" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
</root>
|
@ -0,0 +1,180 @@
|
||||
<?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="Ascending" xml:space="preserve">
|
||||
<value>Ascending</value>
|
||||
</data>
|
||||
<data name="BodyLength.HelpText" xml:space="preserve">
|
||||
<value>The number of characters displayed for each search result summary. The default is 255 characters.</value>
|
||||
</data>
|
||||
<data name="BodyLength.Text" xml:space="preserve">
|
||||
<value>Body Size:</value>
|
||||
</data>
|
||||
<data name="DateRange.HelpText" xml:space="preserve">
|
||||
<value>Enter the date range for search results. The default includes all content.</value>
|
||||
</data>
|
||||
<data name="DateRange.Text" xml:space="preserve">
|
||||
<value>Date Range:</value>
|
||||
</data>
|
||||
<data name="Descending" xml:space="preserve">
|
||||
<value>Descending</value>
|
||||
</data>
|
||||
<data name="ExcludeEntities.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of entities to exclude from search results. By default no entities will be excluded.</value>
|
||||
</data>
|
||||
<data name="ExcludeEntities.Text" xml:space="preserve">
|
||||
<value>Exlude Entities:</value>
|
||||
</data>
|
||||
<data name="IncludeEntities.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of entities to include in the search results. By default all entities will be included.</value>
|
||||
</data>
|
||||
<data name="IncludeEntities.Text" xml:space="preserve">
|
||||
<value>Include Entities:</value>
|
||||
</data>
|
||||
<data name="LastModified" xml:space="preserve">
|
||||
<value>LastModified</value>
|
||||
</data>
|
||||
<data name="PageSize.HelpText" xml:space="preserve">
|
||||
<value>The maximum number of search results to retrieve. The default is unlimited.</value>
|
||||
</data>
|
||||
<data name="PageSize.Text" xml:space="preserve">
|
||||
<value>Page Size:</value>
|
||||
</data>
|
||||
<data name="Relevance" xml:space="preserve">
|
||||
<value>Relevance</value>
|
||||
</data>
|
||||
<data name="SortField.HelpText" xml:space="preserve">
|
||||
<value>Specify the default sort field</value>
|
||||
</data>
|
||||
<data name="SortField.Text" xml:space="preserve">
|
||||
<value>Sort By:</value>
|
||||
</data>
|
||||
<data name="SortOrder.HelpText" xml:space="preserve">
|
||||
<value>Specify the default sort order</value>
|
||||
</data>
|
||||
<data name="SortOrder.Text" xml:space="preserve">
|
||||
<value>Sort Order:</value>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="To" xml:space="preserve">
|
||||
<value>To</value>
|
||||
</data>
|
||||
</root>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user