mirror of https://github.com/itflow-org/itflow
Compare commits
1150 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
163aa3062e | |
|
|
32f996d034 | |
|
|
312eb4dffc | |
|
|
1916456c84 | |
|
|
9b8d37b577 | |
|
|
05018e5f17 | |
|
|
72ef918452 | |
|
|
27fde82aff | |
|
|
b27ffe6635 | |
|
|
84cc4a094a | |
|
|
e75600ee05 | |
|
|
871ad2ea7e | |
|
|
8b5f2e0f3f | |
|
|
58d6ab7342 | |
|
|
03570ecd04 | |
|
|
ca5fb2e010 | |
|
|
da561b296e | |
|
|
523da0dea0 | |
|
|
0e4cc76a84 | |
|
|
7e39a7ed89 | |
|
|
4a26ea7ed9 | |
|
|
7c83ba15b9 | |
|
|
10bfbed4bb | |
|
|
81550bd7a8 | |
|
|
a430bb917e | |
|
|
e1a579387f | |
|
|
fe8df66c67 | |
|
|
5bb410f80c | |
|
|
29b79b9d4e | |
|
|
0f8a8d1464 | |
|
|
cc92a4b7ee | |
|
|
3ffef6df51 | |
|
|
78e4787b99 | |
|
|
540512a156 | |
|
|
7737dbc65d | |
|
|
faa94d888d | |
|
|
99e2487d2b | |
|
|
f09d8ffe05 | |
|
|
c486e3fe62 | |
|
|
ba2d6b6709 | |
|
|
a388a279bc | |
|
|
5a64b19a06 | |
|
|
53178b8d20 | |
|
|
0347382a34 | |
|
|
840460afe7 | |
|
|
c851e54e1d | |
|
|
5ef53b569c | |
|
|
698b4166e8 | |
|
|
1a9a36829b | |
|
|
155b8598ff | |
|
|
4153c91f84 | |
|
|
a99b19a1b5 | |
|
|
18429fda2c | |
|
|
435da991ec | |
|
|
ebd9aae924 | |
|
|
414a84d5ec | |
|
|
a3b2517603 | |
|
|
43535082f6 | |
|
|
e73af9980e | |
|
|
0bdd5784ee | |
|
|
48719ce29c | |
|
|
29839d3b23 | |
|
|
185ea7d6ac | |
|
|
ac7623d4f5 | |
|
|
3d119261cc | |
|
|
169619c9b9 | |
|
|
b991f787a2 | |
|
|
215fc6803e | |
|
|
a79c1c8246 | |
|
|
1aa6419b1b | |
|
|
c7ef3627ce | |
|
|
02694f6720 | |
|
|
f50aabb570 | |
|
|
19b8d09bfd | |
|
|
66fb999a8c | |
|
|
0c5883b61b | |
|
|
ef66d5172c | |
|
|
118f9a34d8 | |
|
|
b61dfac569 | |
|
|
79160f9b5c | |
|
|
d2523cff4a | |
|
|
1839599769 | |
|
|
29e1b56e78 | |
|
|
47e647c712 | |
|
|
a87b0b0447 | |
|
|
96b8fcad3a | |
|
|
cf0fa0024c | |
|
|
aba5ed9271 | |
|
|
63141f3578 | |
|
|
612041635d | |
|
|
efcc0fd5cb | |
|
|
b0724f5b66 | |
|
|
66a2b4b6d2 | |
|
|
77b4dfa50a | |
|
|
1e6e7fd6d8 | |
|
|
46a1b673ba | |
|
|
7230325e62 | |
|
|
af8e733cfb | |
|
|
26ab43c57f | |
|
|
15ed4ef1ce | |
|
|
0ac76766bd | |
|
|
abb97ad99f | |
|
|
6cdc26b55b | |
|
|
d1dcc5fb7e | |
|
|
9f19fd3c75 | |
|
|
61dedb7e7b | |
|
|
65d2b8b2cb | |
|
|
1d3f206660 | |
|
|
ab46899e72 | |
|
|
723a423b06 | |
|
|
a837b97870 | |
|
|
8be0789f25 | |
|
|
99d017144d | |
|
|
891f71006b | |
|
|
d25017216a | |
|
|
83b7c7b054 | |
|
|
283c2a17df | |
|
|
44de049f3b | |
|
|
920d08f039 | |
|
|
0cf1e338c2 | |
|
|
293a2b800e | |
|
|
650a099e19 | |
|
|
46c2c8616e | |
|
|
6295a5c878 | |
|
|
39d8e19e16 | |
|
|
9d3a44d110 | |
|
|
54d46719c2 | |
|
|
dbed2c17db | |
|
|
f772ef2efd | |
|
|
2f28f96f8d | |
|
|
1f2bcf7c34 | |
|
|
a9a5850fd4 | |
|
|
09f3bfd8f4 | |
|
|
3813fbf8f2 | |
|
|
16001f8d4e | |
|
|
49d3dbad9a | |
|
|
56f32a4da2 | |
|
|
a297b8d6d8 | |
|
|
d365f48192 | |
|
|
df6d955261 | |
|
|
9fcaf9f5cc | |
|
|
43a7b7faa5 | |
|
|
69253385c5 | |
|
|
cea7d61481 | |
|
|
41f9a2e6e2 | |
|
|
31d3659098 | |
|
|
c12bfb157e | |
|
|
a55dabb1cd | |
|
|
06fec3c280 | |
|
|
f733a27ad7 | |
|
|
7ea39eb545 | |
|
|
a85f898ef5 | |
|
|
519975f3cf | |
|
|
0e9a071e96 | |
|
|
3917e66fd8 | |
|
|
9f48e2d9f0 | |
|
|
215eadcf2b | |
|
|
b09e4938b7 | |
|
|
d3d706ea68 | |
|
|
8268761ef4 | |
|
|
2850c35bdc | |
|
|
24d8635dac | |
|
|
8314a115bb | |
|
|
b8e2423dbd | |
|
|
52c67f4139 | |
|
|
e895156d03 | |
|
|
89abc18465 | |
|
|
355dfbbb25 | |
|
|
6d15640ae4 | |
|
|
ad4ab5a54c | |
|
|
3c5c86c4c5 | |
|
|
09b91c8826 | |
|
|
13ea48bff8 | |
|
|
26bb430d6e | |
|
|
82da54740f | |
|
|
e02b10d12a | |
|
|
1573045157 | |
|
|
bf31c333a6 | |
|
|
4229bca978 | |
|
|
13bd929755 | |
|
|
7f6c0346af | |
|
|
0387e66066 | |
|
|
04bae8dc37 | |
|
|
559506fc90 | |
|
|
f2b6d481a1 | |
|
|
c66aa92365 | |
|
|
e24ef68d8d | |
|
|
0cacf83ae5 | |
|
|
2dc66b329b | |
|
|
10dc8ea2bf | |
|
|
303f9174c9 | |
|
|
c5dd5f2b6f | |
|
|
ab77705ca2 | |
|
|
10c89ebf73 | |
|
|
ecce994921 | |
|
|
5dd4f5ea62 | |
|
|
93bb5db019 | |
|
|
65ff008ccf | |
|
|
f0c48d23fe | |
|
|
975b52a43d | |
|
|
079b0d5024 | |
|
|
99ccb12b8c | |
|
|
0bb7d24e07 | |
|
|
b7a9f9ea38 | |
|
|
21aee98f9f | |
|
|
9a5a4be64a | |
|
|
db7f8501d0 | |
|
|
61d15cbf9e | |
|
|
39c9c695f1 | |
|
|
d97654581b | |
|
|
2ee70fd3a8 | |
|
|
b336ec4188 | |
|
|
c77e1be1c3 | |
|
|
986f688468 | |
|
|
1d9429b762 | |
|
|
d122d90a47 | |
|
|
2c534d4d20 | |
|
|
b7e0e5c5eb | |
|
|
2915b12181 | |
|
|
ed589ef65b | |
|
|
0d5bfdafdf | |
|
|
fbf3346052 | |
|
|
3ff206f84d | |
|
|
a3b0fce961 | |
|
|
8130280b35 | |
|
|
fea3020d9a | |
|
|
1eb9d163fa | |
|
|
e3e7c2e38b | |
|
|
27e1d6a9cd | |
|
|
2ec4cdc4fb | |
|
|
35a7506c26 | |
|
|
16242be74e | |
|
|
3fcbe440d3 | |
|
|
4ef0755039 | |
|
|
a4ed906dd1 | |
|
|
416a8d9a94 | |
|
|
d8803aaac2 | |
|
|
01f6615ca0 | |
|
|
fd93ee3263 | |
|
|
32bfd298a1 | |
|
|
5de2e7a3bd | |
|
|
6e8c133a99 | |
|
|
956f18430b | |
|
|
76c9933baf | |
|
|
6c6a988c2b | |
|
|
d829e39b66 | |
|
|
0e401df3c0 | |
|
|
9072c37e95 | |
|
|
2eff11efbf | |
|
|
6a7a02d220 | |
|
|
d6349bbc5c | |
|
|
9a2f887db1 | |
|
|
71d30ff95f | |
|
|
3135247936 | |
|
|
0d629221fe | |
|
|
181ea4b487 | |
|
|
fa769665df | |
|
|
00f5198bed | |
|
|
785a291614 | |
|
|
92209c7125 | |
|
|
690007be5c | |
|
|
e6bcf0e12f | |
|
|
ca6a903b8f | |
|
|
50f790dd6c | |
|
|
ed6aa843b7 | |
|
|
52a27699f1 | |
|
|
dba08714bf | |
|
|
edabc5c33f | |
|
|
6b6c70f1df | |
|
|
93061eb695 | |
|
|
1f9133c188 | |
|
|
e7dcc6df3c | |
|
|
fbd58b4723 | |
|
|
e992138456 | |
|
|
058f79d0a1 | |
|
|
5c448c05a9 | |
|
|
e966cd3068 | |
|
|
6d3351b2f7 | |
|
|
61a1d61901 | |
|
|
4ff3231451 | |
|
|
ce832d2805 | |
|
|
b11730303e | |
|
|
565f9ab314 | |
|
|
9435434cf9 | |
|
|
a58ca6f66d | |
|
|
c769bbc405 | |
|
|
0379143829 | |
|
|
ee235cf231 | |
|
|
04b29d43df | |
|
|
dc0715da57 | |
|
|
902323a75b | |
|
|
3a5b18f3dd | |
|
|
ce7d84aa2f | |
|
|
981fb9585d | |
|
|
23b2dcba70 | |
|
|
e4a437f54c | |
|
|
d4167f9595 | |
|
|
88475a2b76 | |
|
|
c26ce4b7dc | |
|
|
5960e7cbd9 | |
|
|
68872ab9fb | |
|
|
64f12b42b8 | |
|
|
8c0d542d7d | |
|
|
c016b67c3a | |
|
|
49d127e957 | |
|
|
e7353c4757 | |
|
|
3106685972 | |
|
|
2b7017fae2 | |
|
|
da0b01e23f | |
|
|
d450ea4beb | |
|
|
9210734911 | |
|
|
ebae80bb7e | |
|
|
dcade3a5c7 | |
|
|
f51c3e9e3f | |
|
|
b34298e45b | |
|
|
9fa78897bc | |
|
|
9642babb7e | |
|
|
d2d1aed393 | |
|
|
58d43063ec | |
|
|
6a5ce1dce3 | |
|
|
f3c7c0761b | |
|
|
27b3124f77 | |
|
|
aa516529e7 | |
|
|
2283ad0eb2 | |
|
|
cece9ad46c | |
|
|
d37da2e8ff | |
|
|
9d9bfc65d5 | |
|
|
d7d6e4ac6f | |
|
|
46d93f986e | |
|
|
2549a97a7e | |
|
|
fc2cca5fdb | |
|
|
ef1ec56270 | |
|
|
cf63a2e7d1 | |
|
|
ed9b99e2f0 | |
|
|
33340b80f9 | |
|
|
ec94bb3d2a | |
|
|
bc2fe2bec8 | |
|
|
a87a206c04 | |
|
|
45083e19b0 | |
|
|
de627c19c5 | |
|
|
595e57dcdd | |
|
|
dadcc69900 | |
|
|
2c160d1ac9 | |
|
|
292b074d8c | |
|
|
2beff45811 | |
|
|
469c36da46 | |
|
|
8afe2d52a0 | |
|
|
51273e9151 | |
|
|
1517c96841 | |
|
|
b7afbd3500 | |
|
|
39d6c42c71 | |
|
|
9f50c9355a | |
|
|
fd73f132bc | |
|
|
1f99e592b7 | |
|
|
a90c4eded8 | |
|
|
1638538fb4 | |
|
|
da0892f9e3 | |
|
|
66cab6c1ab | |
|
|
535078b0ab | |
|
|
d584c6b1e8 | |
|
|
530a76a9a2 | |
|
|
2a0181fd92 | |
|
|
86c5b9a55d | |
|
|
0a6b890250 | |
|
|
af7f1f30ad | |
|
|
f6c0f840fe | |
|
|
0e54359db8 | |
|
|
373f1574bd | |
|
|
6f5ffef22f | |
|
|
451206525e | |
|
|
41f957ea3b | |
|
|
91b0080428 | |
|
|
170d6dd7e5 | |
|
|
15ba385398 | |
|
|
0b93d58a32 | |
|
|
67078c4552 | |
|
|
ab17de7efd | |
|
|
1145260619 | |
|
|
066952ed04 | |
|
|
6ccf7a0fb8 | |
|
|
7f49ecefb1 | |
|
|
43324391ff | |
|
|
89af8216b8 | |
|
|
be9e30e6d8 | |
|
|
85cfd6fb7c | |
|
|
e694e20025 | |
|
|
7618dd335a | |
|
|
eca09253d3 | |
|
|
96c32b3275 | |
|
|
94a4fec3c6 | |
|
|
a8386d3937 | |
|
|
4b7aa4bece | |
|
|
4c604f4009 | |
|
|
98cf2f7a5a | |
|
|
804467b1ba | |
|
|
9fbf775299 | |
|
|
5c93753b7e | |
|
|
27016eaa28 | |
|
|
859281784a | |
|
|
7ae7b8904c | |
|
|
559e0c6350 | |
|
|
d942badec8 | |
|
|
f443ae9203 | |
|
|
d37dc37c2d | |
|
|
236d895498 | |
|
|
3bb0ef6f39 | |
|
|
a0ced96d1d | |
|
|
4ef1e91c78 | |
|
|
2333fa2d9b | |
|
|
37bef2d824 | |
|
|
6427235aec | |
|
|
535c8e9f7b | |
|
|
890e166d1c | |
|
|
337ceb7477 | |
|
|
f82f3e9b8c | |
|
|
3c709955e3 | |
|
|
595e6090c7 | |
|
|
1d98eb5160 | |
|
|
7172c92f02 | |
|
|
874d9ddbc3 | |
|
|
73b84a9690 | |
|
|
c78f50ab8c | |
|
|
8407cd3ea5 | |
|
|
9caa42975d | |
|
|
44fdb6c24f | |
|
|
c8984d1bc9 | |
|
|
be66a1690f | |
|
|
13838ec8d6 | |
|
|
2d72a6fd12 | |
|
|
e24ec04a75 | |
|
|
9a0aa9ce97 | |
|
|
643a4ce666 | |
|
|
189098541e | |
|
|
e8a1d4c66c | |
|
|
08dd6147f2 | |
|
|
7e55808a05 | |
|
|
6b39c1a280 | |
|
|
a38d0054df | |
|
|
e39072d3bc | |
|
|
b4e5e3fda7 | |
|
|
e8af173b4f | |
|
|
fef0d8761f | |
|
|
76b48378ab | |
|
|
a7e042b5f4 | |
|
|
ab8f51622f | |
|
|
fae7e76721 | |
|
|
012b0f1a77 | |
|
|
cc5b1c8040 | |
|
|
2cf932600e | |
|
|
d2dac1915d | |
|
|
5d52b6c4ec | |
|
|
7cfba7f2ef | |
|
|
41a8e41463 | |
|
|
07e2c41eb7 | |
|
|
9fe0d8417b | |
|
|
905908bcf1 | |
|
|
8e87c25de1 | |
|
|
5a76780cd9 | |
|
|
10fd911dcc | |
|
|
2333d81276 | |
|
|
9ee76213e1 | |
|
|
4ec1441189 | |
|
|
38e2e50a29 | |
|
|
95950700d8 | |
|
|
0494bfc1cf | |
|
|
4906e06bf1 | |
|
|
699546f531 | |
|
|
d5ec601d75 | |
|
|
5f26f68a04 | |
|
|
219fe59aa2 | |
|
|
d115d57d1a | |
|
|
6307099bc1 | |
|
|
e0a5efdfaf | |
|
|
e014e516e6 | |
|
|
4d71eb5de0 | |
|
|
0db6853fde | |
|
|
197dd9f299 | |
|
|
99aaeefe8e | |
|
|
712804d2a8 | |
|
|
a393284acd | |
|
|
719a0e5efa | |
|
|
6221fd67e1 | |
|
|
56c7c76ac6 | |
|
|
a5aae51b1a | |
|
|
14691fa367 | |
|
|
38b2a94231 | |
|
|
4c031c8f83 | |
|
|
b100498dd7 | |
|
|
50a8e67917 | |
|
|
dfe0a0b1c7 | |
|
|
52dac2252a | |
|
|
5aee84e84f | |
|
|
3e7d7f3801 | |
|
|
184aba4de4 | |
|
|
dd482b246f | |
|
|
717e6351e7 | |
|
|
c36b1cf50f | |
|
|
896da66caf | |
|
|
f72b7763c1 | |
|
|
46c4a62046 | |
|
|
da3a1d2ce4 | |
|
|
35d6b51770 | |
|
|
35cbfdd736 | |
|
|
a7b2522c87 | |
|
|
fe8baf2ca4 | |
|
|
300aef413c | |
|
|
467c681117 | |
|
|
31a89c0641 | |
|
|
5f94543d9b | |
|
|
10fd74b4c4 | |
|
|
9bc705bfa1 | |
|
|
307d9892b2 | |
|
|
93f4ea51fd | |
|
|
a6f83493f8 | |
|
|
7e17fdf851 | |
|
|
c76da10747 | |
|
|
7c558ff842 | |
|
|
a011dc4dea | |
|
|
fa9acef279 | |
|
|
ed0936059b | |
|
|
77597fd7a9 | |
|
|
8d1bf3d245 | |
|
|
e7503e8f55 | |
|
|
9b6be66623 | |
|
|
6bc7862232 | |
|
|
69a8a9246d | |
|
|
b87b801b04 | |
|
|
dba04a407a | |
|
|
e281a39512 | |
|
|
e0364183e2 | |
|
|
6af61cbb3c | |
|
|
67dc012caa | |
|
|
8753655c9c | |
|
|
873dc1f76d | |
|
|
d781130b49 | |
|
|
effbf241e6 | |
|
|
396a67b198 | |
|
|
1939e06a24 | |
|
|
3f21e73b2e | |
|
|
bc9529c488 | |
|
|
19af05ebee | |
|
|
eb11e5b2f8 | |
|
|
b85137096d | |
|
|
77e9d72f92 | |
|
|
ec344cbaa7 | |
|
|
4cb2f42d71 | |
|
|
98723b76cf | |
|
|
45ac7f084b | |
|
|
3626777bd1 | |
|
|
5fb1070567 | |
|
|
f0bcf975b6 | |
|
|
0ca28dc630 | |
|
|
8196706538 | |
|
|
411a6efeb5 | |
|
|
ffad0cdec9 | |
|
|
ca519fe4e1 | |
|
|
6b0855cff8 | |
|
|
e8a3309822 | |
|
|
ce8c4dfc4c | |
|
|
a1b99c46c7 | |
|
|
1ee50fdfde | |
|
|
05850f61d2 | |
|
|
12d8c8e959 | |
|
|
2b4933bd5c | |
|
|
500e713764 | |
|
|
2ca50776ec | |
|
|
bd31513e5d | |
|
|
6df04390bb | |
|
|
49511276fd | |
|
|
c4e91df1ed | |
|
|
ed8a576ac3 | |
|
|
5c6a7acb7d | |
|
|
850db54a3b | |
|
|
985593b7c2 | |
|
|
857b5d1bfd | |
|
|
498e86aa7a | |
|
|
79d0fd28b0 | |
|
|
426ca90515 | |
|
|
e7328f1be5 | |
|
|
baccc8051e | |
|
|
f1c7690270 | |
|
|
d700d1530b | |
|
|
b60f44eede | |
|
|
54ab788e93 | |
|
|
ac2dd04959 | |
|
|
9a449c35ac | |
|
|
2ff5d2f87c | |
|
|
8f704dade8 | |
|
|
3b3e323ce5 | |
|
|
43ec144bf0 | |
|
|
d5f94819d0 | |
|
|
98aa7e4993 | |
|
|
7c61911dba | |
|
|
2bb107a5d7 | |
|
|
0c4a2aedb7 | |
|
|
e7e7272002 | |
|
|
f672991089 | |
|
|
473fa2671d | |
|
|
85e1515080 | |
|
|
84437a2732 | |
|
|
71911f418f | |
|
|
2741f78bd8 | |
|
|
012e54161a | |
|
|
409084c499 | |
|
|
27595b2c4a | |
|
|
62b6535625 | |
|
|
e593f438b3 | |
|
|
44d46a2cc5 | |
|
|
66e30dd1a8 | |
|
|
4c74351d21 | |
|
|
5d2b14259c | |
|
|
878d5444e1 | |
|
|
ec24ec60c6 | |
|
|
9c096d1f65 | |
|
|
dba3e895da | |
|
|
07726322df | |
|
|
bef18c0d72 | |
|
|
7c3332570a | |
|
|
a3d4a52188 | |
|
|
5530e89f41 | |
|
|
a3554b3dfd | |
|
|
674da36cd1 | |
|
|
18ce12f60d | |
|
|
a462ab36f8 | |
|
|
8aa41edc3e | |
|
|
4e0252553a | |
|
|
8745d09890 | |
|
|
ac3a02baea | |
|
|
f2bbc170da | |
|
|
d79a17adb0 | |
|
|
680dbb04ce | |
|
|
6c8403fa09 | |
|
|
fec8eaef70 | |
|
|
5f007029b4 | |
|
|
4e76ceaa0f | |
|
|
08e467baa9 | |
|
|
f78a2250f1 | |
|
|
84e8a459c8 | |
|
|
b777f80249 | |
|
|
73da203dbb | |
|
|
55a31865d0 | |
|
|
350697869b | |
|
|
939b07422d | |
|
|
88369d480a | |
|
|
cf083e94e6 | |
|
|
11e8f5acfc | |
|
|
77b3a89eb2 | |
|
|
f572f4265a | |
|
|
0b66c8e1be | |
|
|
a8328a3f56 | |
|
|
8b42b17121 | |
|
|
db418ce662 | |
|
|
96fe566e08 | |
|
|
a00f26d8a4 | |
|
|
eeef63d1c3 | |
|
|
0b88ea85ae | |
|
|
c564118156 | |
|
|
01a7dc2068 | |
|
|
bb44ecec3f | |
|
|
b7b24d7de6 | |
|
|
dcca93e34f | |
|
|
4124188505 | |
|
|
eb5d59623b | |
|
|
8631c06731 | |
|
|
40eb40fd86 | |
|
|
c486682a0e | |
|
|
336da073f1 | |
|
|
d2e187a239 | |
|
|
f69de29353 | |
|
|
61de8bc792 | |
|
|
d31f19707b | |
|
|
811312466f | |
|
|
5ecfb3e962 | |
|
|
77be5af4e5 | |
|
|
c512a716d2 | |
|
|
14f5630caf | |
|
|
8532bdc172 | |
|
|
9d74bf8e19 | |
|
|
f75445b4d0 | |
|
|
e04fa1b696 | |
|
|
e07dfb5f67 | |
|
|
3d1af05fc2 | |
|
|
0e38925d74 | |
|
|
c0f3343412 | |
|
|
6a368840fa | |
|
|
5361391b3b | |
|
|
b80662bb24 | |
|
|
4c272b6b8d | |
|
|
96abdef3ad | |
|
|
0b04bc79e9 | |
|
|
cefbbdc3a8 | |
|
|
83ffe05a99 | |
|
|
b6f73083ef | |
|
|
693736023e | |
|
|
fed87c93ab | |
|
|
f53b77b556 | |
|
|
b858d82b0b | |
|
|
ccb2af6d17 | |
|
|
8d937ac8f5 | |
|
|
2786fb65ed | |
|
|
025532f579 | |
|
|
5bd03be1ad | |
|
|
40086f1ce0 | |
|
|
be66ad9a4c | |
|
|
0df5c01bb7 | |
|
|
b85fa38b67 | |
|
|
546246d7c5 | |
|
|
d5536e78f4 | |
|
|
908738b7ca | |
|
|
797e02bffa | |
|
|
d856685782 | |
|
|
1400983d8c | |
|
|
2a43c5d868 | |
|
|
a67675c649 | |
|
|
fc344ef636 | |
|
|
2ffb2be083 | |
|
|
069772f27d | |
|
|
241ec50802 | |
|
|
b943c9cd89 | |
|
|
80625f8c3f | |
|
|
58435d3460 | |
|
|
7a7ac4a47f | |
|
|
429dfa5ca4 | |
|
|
e1f212d30d | |
|
|
670450bcfb | |
|
|
83e15e9e4a | |
|
|
b309081d75 | |
|
|
f1a7b35aa6 | |
|
|
469c5ef06d | |
|
|
07cbe561bd | |
|
|
b69a70cfc3 | |
|
|
923001928c | |
|
|
75ed461c67 | |
|
|
691aebce91 | |
|
|
846947ff49 | |
|
|
65e107d154 | |
|
|
19b809b699 | |
|
|
60fe02bb47 | |
|
|
3e708059c6 | |
|
|
424104bb66 | |
|
|
62696b9ebe | |
|
|
87403e8c2d | |
|
|
7a5a607ff6 | |
|
|
a195774726 | |
|
|
8d0da7b55b | |
|
|
58c315cd09 | |
|
|
dd6c4602db | |
|
|
d413e0c8ff | |
|
|
b356658635 | |
|
|
d92f0fc49b | |
|
|
f206a28cf7 | |
|
|
70cb0ac635 | |
|
|
a0ece18876 | |
|
|
4a22b03952 | |
|
|
5ebf797c90 | |
|
|
a20759f1f2 | |
|
|
c273cab36e | |
|
|
8306a04eda | |
|
|
f078203136 | |
|
|
15e89c3c4e | |
|
|
595c4f1440 | |
|
|
91a523dc23 | |
|
|
8567c97c09 | |
|
|
3621e99c61 | |
|
|
d99b9cbe68 | |
|
|
e319768fd3 | |
|
|
c30ffcf096 | |
|
|
7286248fef | |
|
|
dc49f80cc3 | |
|
|
1ae2da8054 | |
|
|
090f4cb560 | |
|
|
0914716b8e | |
|
|
ab463c1773 | |
|
|
36af4d11fc | |
|
|
14d8dc6fa6 | |
|
|
2032b48ad3 | |
|
|
2af795f548 | |
|
|
7b4edb2948 | |
|
|
17a906fd03 | |
|
|
af46a1fd96 | |
|
|
393c0b8c11 | |
|
|
e92f2f714d | |
|
|
42606067c0 | |
|
|
98bb65509d | |
|
|
a2599e5d43 | |
|
|
0390b1bc2a | |
|
|
531f3ec741 | |
|
|
127afdca0d | |
|
|
c4df5bf988 | |
|
|
30234e044d | |
|
|
1e98ee8916 | |
|
|
d5665c2577 | |
|
|
762ec51a19 | |
|
|
309ad724ec | |
|
|
34397fe468 | |
|
|
2f82647f5e | |
|
|
7d7854424c | |
|
|
eaeadbe933 | |
|
|
cf3f0cee6c | |
|
|
a5f7b7fa9c | |
|
|
9f7c289e94 | |
|
|
c2bba7a919 | |
|
|
4bb37a7198 | |
|
|
231694aabe | |
|
|
2fb75e6d67 | |
|
|
810af638a3 | |
|
|
9223b8cfb1 | |
|
|
b7df21a663 | |
|
|
7d47ed4dbd | |
|
|
65eaf92862 | |
|
|
8a01bc0d7d | |
|
|
1a8a3781dc | |
|
|
eadfdc41c4 | |
|
|
421abd4c5b | |
|
|
ae1a0dcc73 | |
|
|
63b8804e2d | |
|
|
634afcc089 | |
|
|
90f5c8ad57 | |
|
|
12fd45c144 | |
|
|
382258a27c | |
|
|
b5fa8ab4de | |
|
|
bbb0db2f4f | |
|
|
5b89e3dbee | |
|
|
07b29a7bdc | |
|
|
3286343026 | |
|
|
6a26b611fa | |
|
|
218fd2dcdc | |
|
|
4c85db5e49 | |
|
|
85ae42190a | |
|
|
df8a755462 | |
|
|
53713a0318 | |
|
|
61aa477cbf | |
|
|
c2adb92d28 | |
|
|
19b2b08eac | |
|
|
504346256f | |
|
|
34e92d2223 | |
|
|
573953704c | |
|
|
0b9f10985d | |
|
|
8e3dd42a32 | |
|
|
0647933df7 | |
|
|
93f4da3962 | |
|
|
f72351ea88 | |
|
|
d3c4c8c846 | |
|
|
cf047024a1 | |
|
|
ad3ed68932 | |
|
|
62b8ee9d30 | |
|
|
eedd92c894 | |
|
|
622d5e5a44 | |
|
|
475d653979 | |
|
|
19a6f8f422 | |
|
|
e69d69760d | |
|
|
06de349fac | |
|
|
c3ec83f640 | |
|
|
37c20e4e0d | |
|
|
400ba5bb20 | |
|
|
a1ea5214a9 | |
|
|
aae633c4ac | |
|
|
ec8d7a36a8 | |
|
|
21dc26b06f | |
|
|
6a8d2cf1d4 | |
|
|
b803ba4c55 | |
|
|
4378fc2719 | |
|
|
244e1290b4 | |
|
|
5a64bd3a32 | |
|
|
cc0b2126ba | |
|
|
97f92e508e | |
|
|
09fbe4e4ad | |
|
|
508af6a80f | |
|
|
073f816dbd | |
|
|
bf327afd19 | |
|
|
8fb8ce319e | |
|
|
eabfef22be | |
|
|
06c31e0808 | |
|
|
509fb5cfed | |
|
|
f1aa66119a | |
|
|
7150b1545a | |
|
|
59bbbe4a8d | |
|
|
f64641cfdd | |
|
|
f1783a6110 | |
|
|
c8c1155c9c | |
|
|
338a08da6c | |
|
|
15aed891f4 | |
|
|
c1c54780cb | |
|
|
e93704bbdb | |
|
|
c0db914213 | |
|
|
b78e31e518 | |
|
|
2533f0ced1 | |
|
|
c7f4e48a27 | |
|
|
87c1a95b06 | |
|
|
4efb39358a | |
|
|
619b93a545 | |
|
|
a0598997d7 | |
|
|
d0b8095bba | |
|
|
daab1bca3d | |
|
|
a0a2fddd90 | |
|
|
f61c30bd5a | |
|
|
4921d1eb19 | |
|
|
28c8b1a6da | |
|
|
3f0a6df717 | |
|
|
07c8398e0d | |
|
|
82c7a64a25 | |
|
|
164a6a5604 | |
|
|
9b6ea851e7 | |
|
|
3804e18e53 | |
|
|
244a47efad | |
|
|
89e59b2448 | |
|
|
bf9c2f6de7 | |
|
|
ff80a3db3f | |
|
|
c7d00d7b0d | |
|
|
1c6e74b08e | |
|
|
f8d054f8aa | |
|
|
e5dc50ec35 | |
|
|
e0dfaf2d22 | |
|
|
757a62c35b | |
|
|
52a62fc23c | |
|
|
44b70d7161 | |
|
|
ad9e4b4fb4 | |
|
|
4fdd5ae769 | |
|
|
9f2b9e3b3e | |
|
|
2c074e9dc4 | |
|
|
0fad31d683 | |
|
|
b154930a4c | |
|
|
359b04e7d1 | |
|
|
cc00e3bf75 | |
|
|
0454685039 | |
|
|
b5eb325c5e | |
|
|
ed6276a3e4 | |
|
|
5da1310e34 | |
|
|
a69b09c9e6 | |
|
|
8da3bb15e9 | |
|
|
8488445bf4 | |
|
|
546d21adac | |
|
|
580f50b187 | |
|
|
4744276f2a | |
|
|
6106b8aebb | |
|
|
dd2b203321 | |
|
|
7994c9c7a8 | |
|
|
ae59aa3326 | |
|
|
0ab9a1c97d | |
|
|
2908568e2a | |
|
|
2b673a1b6c | |
|
|
bece8abfe2 | |
|
|
ac2b355399 | |
|
|
7e658ee1f2 | |
|
|
bbee81c3bb | |
|
|
45b61ba335 | |
|
|
32e6345cbc | |
|
|
afff46972a | |
|
|
d197995226 | |
|
|
09fa23519d | |
|
|
9da736daee | |
|
|
410d1e0f86 | |
|
|
12ad7962c0 | |
|
|
397ebc5112 | |
|
|
d48823925a | |
|
|
6f49f16f6b | |
|
|
c2664a2888 | |
|
|
4529a56d7b | |
|
|
fded8177c5 | |
|
|
e670a9847a | |
|
|
8aada99f06 | |
|
|
04e624cc14 | |
|
|
55ebe70808 | |
|
|
f23afdd85c | |
|
|
bac76871c1 | |
|
|
56cbcf2921 | |
|
|
f2dc25aedb | |
|
|
2367ca2255 | |
|
|
f28c1ce398 | |
|
|
6cc6e80f92 | |
|
|
9aeda2ce53 | |
|
|
a08393b4d4 | |
|
|
02c1446cb8 | |
|
|
8e66434ec4 | |
|
|
66d43d8a95 | |
|
|
6c7052ea0d | |
|
|
a994bb7e8c | |
|
|
9b109c7abd | |
|
|
95855fc22e | |
|
|
3218ea85b2 | |
|
|
a1831c7406 | |
|
|
c022e5fe32 | |
|
|
2cddda5062 | |
|
|
daeb71abb3 | |
|
|
915161d812 | |
|
|
0e3959ce00 | |
|
|
8d05633d7d | |
|
|
2d39f21b51 | |
|
|
69850f83d3 | |
|
|
7e041d52df | |
|
|
58a8f08bd2 | |
|
|
8eea19d03d | |
|
|
5d18e85d62 | |
|
|
8a13207327 | |
|
|
2a974c28b0 | |
|
|
9b5eb86cac | |
|
|
86212ee088 | |
|
|
e0a1e45928 | |
|
|
5c3e0f0c31 | |
|
|
25db6c6e03 | |
|
|
87779e5c10 | |
|
|
94ff910564 | |
|
|
1b59eef9e2 | |
|
|
b10d757b77 | |
|
|
4dd55df7a8 | |
|
|
554c4d99bb | |
|
|
17920e3cda | |
|
|
073713a6a2 | |
|
|
43c8e0785a | |
|
|
22cd28de98 | |
|
|
c1548ce83d | |
|
|
64f3df6baf | |
|
|
ab3ffdf7fc | |
|
|
162ce8261a | |
|
|
fe68a1f641 | |
|
|
713bd0dab7 | |
|
|
fc00b5e78f | |
|
|
2795b4e15e | |
|
|
7628fc2643 | |
|
|
15d7875369 | |
|
|
a5d69d47ee | |
|
|
d76d405d5a | |
|
|
70495ef298 | |
|
|
7f5893a627 | |
|
|
4c02a4e8c2 | |
|
|
afdb4c06da | |
|
|
2fe8adcfca | |
|
|
a2c77cf0c2 | |
|
|
eab441da06 | |
|
|
19daa289b2 | |
|
|
d228c30b03 | |
|
|
a5ff978a77 | |
|
|
78cfda1dbc | |
|
|
78ae44c334 | |
|
|
e6e30dcd7c | |
|
|
17489e60c8 | |
|
|
f723450d51 | |
|
|
f8e4163c2f | |
|
|
1dee1b4abb | |
|
|
6f8cc40167 | |
|
|
88ee9b60e7 | |
|
|
a9e3e4744d | |
|
|
b9f6871bae | |
|
|
f920b8fac9 | |
|
|
d8955f1f2d | |
|
|
43b50c44b1 | |
|
|
f6e33d5892 | |
|
|
206dfc2426 | |
|
|
2467274f7b | |
|
|
e02d45db3e | |
|
|
650b8754e8 | |
|
|
64a6b7dc39 | |
|
|
7aa773b336 | |
|
|
a8b63ed429 | |
|
|
08f2a307d3 | |
|
|
72a84af1a7 | |
|
|
a8f682286a | |
|
|
b0a79c1b6f | |
|
|
ccec330ceb | |
|
|
093fd69415 | |
|
|
3cf33afac4 | |
|
|
5f7b297cf1 | |
|
|
b6ffa0595d | |
|
|
4a3154baca | |
|
|
ca0b1a1fb7 | |
|
|
5f02bc07aa | |
|
|
7c070e3929 | |
|
|
ed0b192f5b | |
|
|
36e1df93bb | |
|
|
615604661c | |
|
|
9458c1cc7a | |
|
|
298a584377 | |
|
|
74a1603943 | |
|
|
117861034a | |
|
|
7452e8f08e | |
|
|
5ed5473b36 | |
|
|
2e9e49a203 | |
|
|
c81b430318 | |
|
|
33beae68e5 | |
|
|
1b8cd5630a | |
|
|
b8d17be015 | |
|
|
fe762ef926 | |
|
|
8d8b0f4a48 | |
|
|
a2e16f918c | |
|
|
228a457518 | |
|
|
f0cc9412bb | |
|
|
7509301f5c | |
|
|
d97da07cb1 | |
|
|
2ec38606e0 | |
|
|
2c85922788 | |
|
|
fc24bcb24f | |
|
|
08ebedef03 | |
|
|
eac46d0da0 | |
|
|
a69b60703b | |
|
|
4ed0767f1c | |
|
|
72ae7843f6 | |
|
|
d92b803526 | |
|
|
506783f022 | |
|
|
2fe7bf6870 | |
|
|
c36fb6ae12 | |
|
|
55c761b11e | |
|
|
a769993fa8 | |
|
|
6df3a35766 | |
|
|
abd985d0f4 | |
|
|
db5cfed96f | |
|
|
9ce502c1b9 | |
|
|
0172895e3c | |
|
|
a29bfc3cb5 | |
|
|
341ff69c58 | |
|
|
66ab0d3f11 | |
|
|
b147bc46cf | |
|
|
11203f1ad2 | |
|
|
ee2e4b671d | |
|
|
65bb1b4007 | |
|
|
cdc0422d31 | |
|
|
1bbf7c7662 | |
|
|
1ca31662f8 | |
|
|
3de97fcd15 | |
|
|
9218d828b0 | |
|
|
4ed9c5cfb8 | |
|
|
17cd1fd0c1 | |
|
|
c7b9c95d0d | |
|
|
c0360f4e68 | |
|
|
571ed4818e | |
|
|
04226101af | |
|
|
b50c2295a6 | |
|
|
75e002a054 | |
|
|
e6c18970b3 | |
|
|
00a9c53fc4 | |
|
|
942f5bff52 | |
|
|
9ea41c6ae5 | |
|
|
c40d15a545 | |
|
|
4c966f19e5 | |
|
|
73d0cb7497 | |
|
|
e3ae8df4d8 | |
|
|
ef06c4141f | |
|
|
f4eaba4384 | |
|
|
9ee159c458 | |
|
|
180635f318 | |
|
|
e498cc6036 | |
|
|
b6e0990a78 | |
|
|
04fac54987 | |
|
|
b31d1eba6a | |
|
|
dec91d116a | |
|
|
1fb243df11 | |
|
|
c02b267d44 | |
|
|
ed5aa9a0c2 | |
|
|
3e0e72dedc | |
|
|
3aa26226e5 | |
|
|
bb787cdc70 |
|
|
@ -1 +1 @@
|
|||
custom: ["https://donate.itflow.org"]
|
||||
custom: ["https://services.itflow.org"]
|
||||
|
|
|
|||
|
|
@ -4,8 +4,16 @@ config.php
|
|||
uploads/favicon.ico
|
||||
uploads/clients/*
|
||||
!uploads/clients/index.php
|
||||
uploads/custom/*
|
||||
!uploads/custom/index.php
|
||||
uploads/documents/*
|
||||
!uploads/documents/index.php
|
||||
uploads/document_templates/*
|
||||
!uploads/document_templates/index.php
|
||||
uploads/expenses/*
|
||||
!uploads/expenses/index.php
|
||||
uploads/recurring_tickets/*
|
||||
!uploads/recurring_tickets/index.php
|
||||
uploads/settings/*
|
||||
!uploads/settings/index.php
|
||||
uploads/users/*
|
||||
|
|
@ -14,6 +22,8 @@ uploads/tmp/*
|
|||
!uploads/tmp/index.php
|
||||
uploads/tickets/*
|
||||
!uploads/tickets/index.php
|
||||
uploads/ticket_templates/*
|
||||
!uploads/ticket_templates/index.php
|
||||
.idea/*
|
||||
plugins/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer/HTML/*
|
||||
!plugins/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer/HTML/.gitkeep
|
||||
|
|
@ -26,3 +36,22 @@ xcustom/*
|
|||
!xcustom/readme.php
|
||||
post/xcustom
|
||||
!post/xcustom/readme.php
|
||||
admin/custom/*
|
||||
!admin/custom/readme.php
|
||||
agent/custom/*
|
||||
!agent/custom/readme.php
|
||||
client/custom/*
|
||||
!client/custom/readme.php
|
||||
guest/custom/*
|
||||
!guest/custom/readme.php
|
||||
cron/custom/*
|
||||
!cron/custom/readme.php
|
||||
scripts/custom/*
|
||||
!scripts/custom/readme.php
|
||||
setup/custom/*
|
||||
!setup/custom/readme.php
|
||||
api/v1/custom/*
|
||||
!api/v1/custom/readme.php
|
||||
.zed
|
||||
|
||||
|
||||
|
|
|
|||
646
CHANGELOG.md
646
CHANGELOG.md
|
|
@ -2,6 +2,650 @@
|
|||
|
||||
This file documents all notable changes made to ITFlow.
|
||||
|
||||
## [25.12.1] Maint Release
|
||||
|
||||
### Major Changes
|
||||
- Unified the Client/Agent Login and process (Note only Client Users can Reset passwords from the login page, does not apply to agent users).
|
||||
|
||||
### Bug Fixes
|
||||
- Fix Payment Provider not adding an account.
|
||||
- Fix New ticket button in contact details in the related tickets section.
|
||||
|
||||
### New Features & Updates
|
||||
- You can now Set Payment Provider income/expense account, expense vendor and expense category upond creation or editing.
|
||||
- Moved Saved Payment Provider Methods away from admin side nav to the count link within Payment Providers page.
|
||||
- Moved AI Models from the admin side nav to the model count link within AI Providers.
|
||||
- Add Favicon Reset.
|
||||
|
||||
## [25.12] Stable Release
|
||||
|
||||
### Breaking Changes ###
|
||||
- For Existing installs: **php-xml** extension needs to be installed for document creation and editing, new install script does this for you as of Dec 6th 2025. To install php-xml: `sudo apt install php-xml`
|
||||
|
||||
### Major Changes
|
||||
- Consolidated "Files" and "Documents" into a single section called **Files**.
|
||||
|
||||
### Bug Fixes
|
||||
- Resolved issue with updating asset notes in asset details.
|
||||
- Fixed problem with bulk ticket merging.
|
||||
- Corrected issue where decimal inputs (e.g., price, cost) weren’t displaying on iPhones in certain forms.
|
||||
- Added CSV escaping to the sample export data in areas where a sample CSV template is provided.
|
||||
- Fix a race condition where dupe tickets, invoices, recurring invoices, recurring tickets, quotes will be created using the same number if created in parallel espcecially when using the API.
|
||||
|
||||
### New Features & Updates
|
||||
- Introduced automatic subject-based ticket merging/reply detection. Now, if an email comes from a known contact or domain and the subject matches 95% of a ticket opened in the last 7 days, it will be merged automatically.
|
||||
- Added `cleanInput` function to sanitize data before inserting it into the database when using MySQLi prepared statements.
|
||||
- Migrated client post functionality to use MySQLi prepared statements.
|
||||
- Updated payment method post functionality to use MySQLi prepared statements.
|
||||
- Implemented `saveBase64Images()` to convert base64-encoded `<img>` tags into actual image files stored under `/uploads/<module>/<id>/` with secure filenames. Added wrapper functions, and updated document creation to use processed image paths.
|
||||
- For new documents and document templates, images are now stored in `/uploads/documents/$document_id` instead of being stored as base64 in the database, using the `saveBase64Images()` function.
|
||||
- UI/UX improvements made to the document details page.
|
||||
- Removed sidebar quick-add options.
|
||||
- Created new folders in the uploads directory: `documents`, `document_templates`, and `recurring_tickets`.
|
||||
- Reworked the bulk action function to pass the name arrays, instead of a generic `selected_ids` array. This allows multiple bulk name arrays to be passed at once, currently used for the new file-document merge.
|
||||
- Big task: Converted the remaining modals to use the new `ajax-modal` system, enabling more flexible flow expansion going forward.
|
||||
- Mail queue: Added a `--no-mx-validation` flag to bypass recipient domain MX validation.
|
||||
- Bump PHPMailer from 7.0.0 to 7.0.1.
|
||||
- Bump stripe-php from 18.1.0 to 19.0.0.
|
||||
- Bump TCPDF from 6.10.0 to 6.10.1.
|
||||
- Bump TinyMCE from 8.2.0 to 8.2.2.
|
||||
|
||||
## [25.11.1] Maint Release
|
||||
|
||||
### Fixes
|
||||
- Fix broken edit Payment Method.
|
||||
- Fix unable to delete Vendor Template.
|
||||
- Fix Mail Queue link in flash alert for testing email and sending a quote.
|
||||
- Add Show Category Type select if not defined.
|
||||
- Add Show Product Type select if not defined.
|
||||
- Fix add ticket watcher.
|
||||
- Fix if Client isn't assigned to a ticket dont show client view.
|
||||
- Fix missing session client id check when paying an invoice from client portal.
|
||||
- Update Composer Webklex-IMAP library dependency symfony/http-foundation from 7.3.3 to 7.3.7 to fix security related issues.
|
||||
- Add back delete Payment provider the database will handle cascade deletes to saved cards, recurring payments and client payment provider reference.
|
||||
- Don't show Client Tickets Breadcrumb if no client is assigned to a ticket.
|
||||
- Don't Show Contact or Assignment Tab in edit ticket if no Client is Assigned.
|
||||
- Don't Show add contact, asset, vendor, watcher if not client is assigned to a ticket.
|
||||
- Don't Show Public Comment & Email if contact email doesn't exist.
|
||||
- Fixed IMAP Test whicn now uses RAW TCP Connection instead of the depracated php-imap extension.
|
||||
- Fix Broken Link in Ticket Updates via Client Portal to agent.
|
||||
|
||||
### Added / Changed
|
||||
- [Feature] Added Asset Tags.
|
||||
- [Feature] Added Quick Add Links to most side bar navs example quickly add a client from sidebar.
|
||||
- Migrate ticket template add to ajax modal.
|
||||
- Add TOTP secret to Client Export PDF in Credential section.
|
||||
- Add UserID on hover in users listing.
|
||||
- Merge ticket now redirects to the new ticket details page.
|
||||
- [Feature] Add Pay via saved card under invoice Listings.
|
||||
- Ticket Related Side Items UI Cleanup to use btn-tool class.
|
||||
|
||||
## [25.11] Stable
|
||||
|
||||
### Deprecation Notice:
|
||||
- **Outdated CRON Scripts**: The following scripts are removed.
|
||||
- `/scripts/cron_mail_queue.php`
|
||||
- `/scripts/cron_ticket_email_parser.php`
|
||||
- `/scripts/cron.php`
|
||||
- `/scripts/cron_domain_refresher.php`
|
||||
- `/scripts/cron_certificate_refresher.php`
|
||||
|
||||
**Action Required**: Transition to the new versions:
|
||||
- `/cron/mail_queue.php`
|
||||
- `/cron/ticket_email_parser.php`
|
||||
- `/cron/cron.php`
|
||||
- `/cron/domain_refresher.php`
|
||||
- `/cron/certificate_refresher.php`
|
||||
|
||||
- PHP Extensions php-imap and php-mime-mail-parser are no longer required.
|
||||
---
|
||||
|
||||
### Fixes
|
||||
- **Ticket Listing**: Resolved issue where the “Check All” checkbox was visible even when ticket status wasn’t set. Now hidden for closed tickets only.
|
||||
- **Timer Auto-Start**: Show H/M/S placeholders when timer auto-start is disabled.
|
||||
- **Ticket Guest URL**: Fixed email not including the ticket guest URL key.
|
||||
- **EML Generation**: Resolved issue with EML not being generated in the new ticket parser.
|
||||
- **New Ticket Mail Notification**: Included message when notifying the tech of a reply in the new ticket mail parser.
|
||||
- **Advanced Filter Collapse**: Added clause to prevent collapse of advanced filters when the “from” date is set to the default (1970-01-01).
|
||||
- **Recurring Invoice**: Fixed issue where email was marked as sent but not actually sent when forcing a recurring invoice to an invoice.
|
||||
- **CSRF Token**: Fixed issue with deleting recurring ticket from asset details page due to missing CSRF check token.
|
||||
- **Vendor Website Link**: Fixed missing `https://` prefix in the vendor website link on the vendor details modal.
|
||||
- **Agent Select Box**: Resolved issue where agents sometimes didn’t appear in the agent select boxes.
|
||||
- **TinyMCE**: Fixed TinyMCE editor issue on Bulk Create Ticket in Assets.
|
||||
- **Ticket Timer**: Fixed ticket timer initialization after reload and when the tab is put to sleep (background tab).
|
||||
- **Client Deletion**: Fixed issue with client deletion.
|
||||
- **Domain Records**: Added flag for missing SOA record when adding a domain (prevents subdomain creation).
|
||||
- **Domain Fetching**: Quits domain record fetching if no SOA record exists (prevents subdomains).
|
||||
- **Domain Expiry**: Only show time to expiry when there’s an expiry date set; otherwise, display a dash.
|
||||
- **Certificates**: Improved handling of empty date in the agent UI.
|
||||
- **Certificates API**: Fixed bug with missing JS to fetch certificate details.
|
||||
- **API Updates**:
|
||||
- Clients API: Added support for archiving/un-archiving clients, updating client data, and abbreviation support.
|
||||
- Contacts API: Added archiving/un-archiving and restriction to only allow one primary contact per client.
|
||||
- Mail Queue: Added recipient domain MX validation before sending emails.
|
||||
|
||||
---
|
||||
|
||||
### Added / Changed
|
||||
- **Backup / Restore**: Improved backup and restore by streaming data to disk (to prevent memory issues), setting unlimited timeouts, checking for bad backup contents, and using PHP for DB import instead of shell exec. Added `.htaccess` to prevent PHP execution in `/uploads/` directory.
|
||||
- **Ajax Modals**: Migrated all Add and Bulk modals to the new Ajax Modal for improved performance.
|
||||
- **Recurring Ticket Sorting**: Default sorting of recurring tickets by `RunDate` instead of subject.
|
||||
- **Recurring Ticket Enhancements**:
|
||||
- Added Billable column.
|
||||
- Added bulk actions for setting priority, agent, billable status, and next run dates.
|
||||
- Added filters for category, assigned agent, and billable status.
|
||||
- Added new frequency options: 3-day and biweekly.
|
||||
- **Asset Select**: Updated asset select dropdown to separate asset types using opt groups (planned for wider use).
|
||||
- **Expiring Domains & Certificates**: Added "30 Day" warning for expiring domains and certificates in the dashboard.
|
||||
- **Ticket Search**: Allowed search using both ticket prefix and number.
|
||||
- **Recurring Invoice**: Cancel recurring invoices when the associated client is archived.
|
||||
- **Credentials Import/Export**: Now includes TOTP secrets when importing/exporting credentials.
|
||||
- **Asset Notes Import**: Allowed importing of asset notes.
|
||||
- **Ticket View**: Added a "View HTML Code" button in all ticket views for TinyMCE.
|
||||
- **Date Range Picker**: Updated all date filters to use the improved DateRangePicker JS.
|
||||
- **Bulk Ticket Creation**: Added bulk ticket creation for clients.
|
||||
- **Sidebar Updates**: Updated all sidebars to use absolute paths for easier integration with custom code.
|
||||
- **Document Actions**: Added Archive and Delete buttons to the Document Details view with improved redirect behavior.
|
||||
- **Ticket Template Sorting**: Allowed sorting by task count in ticket templates.
|
||||
- **Contact Modal UI**: Updated contact details modal to display contact information at the top.
|
||||
- **API & Code Updates**:
|
||||
- Separated out post files for recurring tickets, invoices, expenses, and payments.
|
||||
- Removed unused budget code.
|
||||
- **Invoice Product Autocomplete**: Now allows searching for product codes as well as names.
|
||||
- **Client Duplicate Check**: Flags duplicate clients or leads when using the client add modal.
|
||||
- **Recurring Invoice Reference**: Added a column to invoices indicating if they were created from a recurring invoice.
|
||||
- **Global Search Enhancements**:
|
||||
- Allowed ticket details to be searchable in global search.
|
||||
- Allowed searching for quotes in global search.
|
||||
- **UI/UX Improvements**:
|
||||
- Spruced up the ticket details page UI.
|
||||
- Added contact email validation to flag duplicates or invalid addresses.
|
||||
- **API Debugging**: Log API endpoint/URL path for authentication failures to aid in debugging.
|
||||
- **Image Upload Optimization**: Removed image optimization from uploads (this will be handled by a cron job in the future).
|
||||
- **View Behavior Change**: Updated ticket/invoice/quote views to always be in the Client section, showing client-side navigation and top info bar.
|
||||
|
||||
---
|
||||
|
||||
### Library Updates:
|
||||
- **DataTable**: Bumped from 2.3.3 to 2.3.4.
|
||||
- **TinyMCE**: Bumped from 8.0.2 to 8.2.0.
|
||||
- **Stripe-PHP**: Bumped from 17.6.0 to 18.1.0.
|
||||
- **PHPMailer**: Bumped from 6.10.0 to 7.0.0.
|
||||
- **Chart.js**: Bumped from 4.5.0 to 4.5.1.
|
||||
|
||||
|
||||
|
||||
|
||||
## [25.10.1]
|
||||
- Deprecation Notice: `/scripts/cron_mail_queue.php` , `/scripts/cron_ticket_email_parser.php` , `/scripts/cron.php` `/scripts/cron_domain_refresher.php`, `/scripts/cron_certificate_refresher.php` are being phased out. Please transition to `/cron/mail_queue.php` , `/cron/ticket_email_parser.php`, `/cron/cron.php`, `/cron/domain_refresher.php`, `/cron/certificate_refresher.php` These older scripts will be removed in the November 25.11 release—update accordingly. 25.10.1 installs have the script already configured.
|
||||
|
||||
### Fixes
|
||||
- Fix regression missing custom Favicon.
|
||||
- Update SMTP and IMAP provider to allow for empty strings, empty means disabled.
|
||||
- Fix Client portal Microsoft SSO Logins.
|
||||
- Fix regression in Vendor Templates.
|
||||
- Fix refression in some broken links from user to agent.
|
||||
- Fix Project edit.
|
||||
- Prevent open redirects upon agent login.
|
||||
- Fix regression on switching to Webklex IMAP to allow for no SSL/TLS in IMAP.
|
||||
- Fix Setup Redirect not behaving properly when setup hasnt been performed.
|
||||
- Added Server Document Root Var to several includes, headers, footers files to allow includes from deeper directory strutures such as the new custom directories.
|
||||
- Fix edit contact in contact details.
|
||||
- Add .htaccess to /cron/.
|
||||
|
||||
### Added / Changed
|
||||
- Support for HTML Signatures.
|
||||
- Add Edit Project Functionality in a ticket.
|
||||
- Added more custom locations: /cron/custom/, /scripts/custom/, /api/v1/custom/, /setup/custom/.
|
||||
- Copied `/scripts/cron.php` `/scripts/cron_domain_refresher.php`, `/scripts/cron_certificate_refresher.php` to `/cron/cron.php`, `/cron/domain_refresher.php`, `/cron/certificate_refresher.php`. See Above!
|
||||
- Signatures is now handled in post ticket reply on Public Comments only.
|
||||
|
||||
## [25.10]
|
||||
|
||||
### Breaking Changes
|
||||
- Renamed `/user/` directory to `/agent/`.
|
||||
- Deprecation Notice: `/scripts/cron_mail_queue.php` and `/scripts/cron_ticket_email_parser.php` are being phased out. Please transition to `/cron/mail_queue.php` and `/cron/ticket_email_parser.php`. These older scripts will be removed in the November release—update accordingly. New Installs via the script will have this already configured.
|
||||
- Custom is working now. Custom code should be placed in /admin/custom/ , /agent/custom/ , /client/custom/ /guest/custom/
|
||||
We will provide example code with directory structure for each custom directory a week after this release.
|
||||
|
||||
### Fixes
|
||||
- Resolved issue with "Restore from Setup" not functioning correctly.
|
||||
- Corrected asset name display in logs and flash messages when editing an asset in a ticket.
|
||||
- Fixed Payment Provider Threshold not being applied.
|
||||
- Fixed issue where Threshold setting was not saving properly.
|
||||
- Various minor fixes for Payment Provider issues.
|
||||
- Removed leads from the client selection list in the "New Ticket" modal.
|
||||
- Fixed issues with the MFA modal.
|
||||
- Resolved MFA enforcement bugs.
|
||||
- Fixed KeepAlive functionality to maintain user sessions longer.
|
||||
- Fixed multiple broken links caused by the `/user/` to `/agent/` path migration.
|
||||
- Fixed Custom code directories.
|
||||
|
||||
### Added / Changed
|
||||
- Removed "ACH" as a payment method; added "Bank Transfer" instead.
|
||||
- Replaced relative paths with absolute paths for web assets.
|
||||
- Tickets can now be resolved via the API.
|
||||
- Added a filter for Archived Users and an option to restore them.
|
||||
- Introduced a modal when archiving users, allowing reassignment of open and recurring tickets to another agent.
|
||||
- Improved logic for determining the index/root page.
|
||||
- Added "Assigned Agent" column for recurring tickets.
|
||||
- Introduced "Additional Assets" option when editing assets in tickets; modal now uses the updated AJAX method.
|
||||
- Added Gibraltar to the list of supported countries.
|
||||
- Added Custom Link Option for the Admin Nav.
|
||||
- Added Custom Link Option for the Reports Nav.
|
||||
|
||||
### Other notes
|
||||
- Major releases will happen on the first week of every Month.
|
||||
|
||||
|
||||
## [25.09.2]
|
||||
|
||||
### Fixes
|
||||
- Fix Payment Method Select box in Revenue.
|
||||
- Remove Extra Feeback Wording When Invoice Sends.
|
||||
- Updated all CSV exports to use escape parameters.
|
||||
- Fix Missing First row on Asset interface export.
|
||||
- Fix Edit User not working due to incorrect modal footer path.
|
||||
- Fix Add Certificate breaking due spelling on function.
|
||||
- Update all CSV Exports to include company name or client name depending on when its being exported from.
|
||||
- Introduced new function sanitize_filename and implmented it in all exports.
|
||||
- Spruced up UI/UX Saved Paymented section in Client Portal.
|
||||
- Fix add Payment Link in client portal recurring invoice section.
|
||||
- Better Logic handling for default page redirect.
|
||||
|
||||
### Features
|
||||
- Introduced new Beta mail parser cron using webklex imap library instead of php-imap as this is deprecated --Not Enabled on existing installs, only new installs.
|
||||
- Introduced Beta support for OAUTH2 Authentication for Microsoft 365 and Google Workspaces for both incoming ticket parsing and outgoing email but must use new mail parser and mail queue for this to work, and requires changing the cron jobs: scripts/cron_mail_queue.php to cron/mail_queue.php and scripts/cron_ticket_email_parser.php to cron/ticket_email_parser.php.
|
||||
|
||||
---
|
||||
|
||||
## [25.09.1]
|
||||
|
||||
### Fixes
|
||||
- **Web Installer**: Resolved issue with broken installer caused by incorrect database schema file name.
|
||||
- Hide the "Add Credit" button as the feature is not fully implemented yet.
|
||||
- Corrected long invoice/quote notes that were overlapping with the footer in PDF exports.
|
||||
- Fixed AI settings not appearing in the Admin Menu when the Billing module was disabled.
|
||||
- Enabled wrapping of client tags when they are too long.
|
||||
- Fixed an issue where AI was not functioning correctly.
|
||||
- Removed extra spacing between the contact name and icon in the Ticket Details contact card.
|
||||
|
||||
### Features
|
||||
- Redesigned **AI Ticket Summary**, now divided into 3 sections: Main Issue, Actions Taken, and Resolution/Next Steps.
|
||||
- Updated the **AI Ticket Summary** prompt to include ticket status, reply author, source, category, and priority.
|
||||
|
||||
---
|
||||
|
||||
## [25.09]
|
||||
|
||||
***BACK UP*** before updating.
|
||||
|
||||
---
|
||||
|
||||
### Breaking Changes and Notes
|
||||
- We strongly recommend updating from the command line, however if performed via the webui and after performed it will return a 404. thats normal as the directory structure has changed, just close your browser then log back in then go back to update to perform the many database updates.
|
||||
- This is a major release with significant changes. While the community has done a great job identifying bugs, some may still remain — continued testing is encouraged.
|
||||
- All AI settings will be **reset** and must be reconfigured using the new AI provider backend.
|
||||
- The `xcustom` directory has been renamed to `custom`. All custom libraries and post-processing scripts should now be placed here.
|
||||
|
||||
---
|
||||
|
||||
### Added / Changed
|
||||
- Numerous UI improvements and refinements across the application.
|
||||
- Enhanced visual clarity by thickening the left border on ticket comments to help identify comment types.
|
||||
- Ticket details UI redesigned to use less space at the top of the screen.
|
||||
- Introduced tracking for the **first response date/time** on tickets.
|
||||
- New reporting feature: **Average time to first response** on tickets.
|
||||
- Stripe integration rebuilt using the new **payment provider backend**.
|
||||
- Clients can now save and manage **multiple payment methods**.
|
||||
- Support for selecting saved cards for **recurring invoices** in both the client and agent portals.
|
||||
- Initial database structure and logic added for **credit management** (feature not yet enabled).
|
||||
- Major **backend directory restructuring**.
|
||||
- Introduced **stock/inventory management**, including a stock ledger backend.
|
||||
- Stock quantities now update automatically when invoice items are added or removed.
|
||||
- Invoice autocomplete now includes: **name, description, price, tax, stock levels**, and links `product_id` to `item_id`.
|
||||
- Added a **category filter** to invoices.
|
||||
- Linked stock to related expenses.
|
||||
- New product fields: **location, code, and type**.
|
||||
- Products now separated into two types: **Service** and **Product**.
|
||||
- **Dark mode** introduced.
|
||||
- Projects: Now support linking **closed tickets**.
|
||||
- Clients: Added bulk actions for tags, referral source, industry, hourly rate, email, archive, and restore.
|
||||
- Invoices: Bulk action added to **assign categories**.
|
||||
- Assets: New `client_uri` field, visible in both the agent and client portals.
|
||||
- Client Portal: Clients can now **select an asset** during ticket creation.
|
||||
- Client Portal: Company logo now **displays in the header**.
|
||||
- Client Portal: Dashboard cards are now **clickable** for more detail.
|
||||
- Assets: Option added to include **MAC Address** in additional columns.
|
||||
- Asset Interface: Bulk actions added — set DHCP, network type, and delete.
|
||||
- API:
|
||||
- Added `/location` endpoint.
|
||||
- Ticket content now supports **HTML formatting**.
|
||||
- New option to filter and display **500 records per page** in the footer.
|
||||
- Payment methods are now treated as a **separate entity** instead of being grouped under categories.
|
||||
- Updated libraries:
|
||||
- **TinyMCE**
|
||||
- **Chart.js** (major upgrade)
|
||||
- **DataTables**
|
||||
- **Bootstrap**
|
||||
- **FullCalendar**
|
||||
- **php-stripe**
|
||||
|
||||
---
|
||||
|
||||
### Fixed
|
||||
- Several security vulnerabilities patched (with thanks to www.helx.io).
|
||||
- Ticket status is no longer updated when scheduling.
|
||||
- Client Portal: Tech contacts can no longer edit their own details.
|
||||
- Fixed overlapping logo issue in Invoice/Quote PDF exports.
|
||||
- Refactored `check_login.php` into multiple files for modular login functionality.
|
||||
- Removed redundant logging comments for redirects.
|
||||
- Renamed `get_settings.php` to `load_global_settings.php`.
|
||||
- Simplified syntax for `ajax-modal` and updated usage throughout the app.
|
||||
- Fixed issue where primary contact text wasn’t displaying.
|
||||
- Corrected client **Net Terms** display.
|
||||
- Fixed logic for recurring expense **next run date**.
|
||||
- Resolved broken **IMAP test button**.
|
||||
- Archived clients can no longer log into the portal.
|
||||
- Searching closed tickets no longer reverts to open tickets.
|
||||
- Fixed project search filter not showing completed projects.
|
||||
- Fixed issue where company logo was not being removed correctly.
|
||||
- Resolved API bugs:
|
||||
- Default rate and net terms.
|
||||
- Contact location.
|
||||
- Document endpoint.
|
||||
|
||||
---
|
||||
|
||||
### Developer Updates
|
||||
- Replaced legacy code with newer functions like `redirect()`, `getFieldById()`, and `flash_alert()`.
|
||||
- Significantly improved performance of queries used for filter selection boxes.
|
||||
|
||||
|
||||
## [25.06.1]
|
||||
|
||||
### Fixed
|
||||
- Fixed a regression in setup causing it to crash and never complete, due to missing default for currency.
|
||||
|
||||
## [25.06]
|
||||
|
||||
### Breaking CHANGES
|
||||
- Old Document Verions will be deleted due to the major backend rewrite how document versions work.
|
||||
|
||||
### Added / Changed
|
||||
- Improved function for retrieving remote IP address for logging purposes.
|
||||
- Ticket categories are now sorted alphabetically.
|
||||
- Visiting a deleted invoice or recurring invoice now redirects to the listing page; delete option added to invoice details page.
|
||||
- Added "Mark as Sent" and "Make Payment" actions directly on the invoice listing page.
|
||||
- Introduced Ticket Category UI for recurring tickets.
|
||||
- In Project Details, bulk actions and sorting are now available for tickets.
|
||||
- Updated ticket details UI to use full card stacks with edit icons for stackable items (e.g., asset, watchers, contact).
|
||||
- Added a new setting to toggle AutoStart Timer in ticket details (disabled by default).
|
||||
- Applied gray accent theme in the client section to visually distinguish from the global view.
|
||||
- Introduced Ticket Due Date functionality (currently supports add/edit only; more updates coming next release).
|
||||
- Added settings option to display Company Tax ID on invoices.
|
||||
- Client overview now displays badge counts for all entities.
|
||||
- Overhauled UI for Invoice, Quote, and Recurring Invoice details; switched PDF generation to TCPDF PHP from PDFMake JS.
|
||||
- Document versioning has been moved to a separate backend table to resolve permanent link issues -- SEE Breaking CHANGES.
|
||||
- Migrated Document Templates, Vendor Templates, and Software/License Templates to dedicated tables.
|
||||
- Added functionality to mark all tasks in a ticket as complete or incomplete.
|
||||
- Asset CSV import now supports a purchase date field.
|
||||
- Recurring Payments have been restructured to auto-charge on the invoice due date instead of at generation time.
|
||||
- Added "Base Template" label for vendor templates when available.
|
||||
- Backup and restore processes now use a temporary directory; files are cleaned up automatically if operations fail.
|
||||
- Added confirmation prompt when accepting or declining a quote.
|
||||
- Other minor code UI/UX cleanups and refactoring throughout the app.
|
||||
|
||||
### Fixed
|
||||
- Resolved issue with enabling MFA.
|
||||
- Fixed UI regression where ticket listing columns would misalign.
|
||||
- Non-billable invoices are no longer included in calculations.
|
||||
- Addressed multiple minor reported security vulnerabilities.
|
||||
- Tickets with open tasks are no longer resolved in bulk; a warning is shown along with a count of affected tickets.
|
||||
|
||||
|
||||
## [25.05.1]
|
||||
|
||||
### Added / Changed
|
||||
- Added Domain Expiring Card to Client Portal Dashboard for Primary and Technical Users.
|
||||
- Added Balance and Monthly Recurring Amount to Client Portal Dahboard for Primary and Technical Users.
|
||||
- Added Archive Searching to network and certificates also added unarchive capabilities to them as well.
|
||||
|
||||
### Fixed
|
||||
- Add Payment not showing in Invoice.
|
||||
- Updated Client Overview Entities to not show archived client's Entities even though the entity may not be archived.
|
||||
|
||||
|
||||
## [25.05]
|
||||
|
||||
### Added / Changed
|
||||
- Expanded file upload allow-list to include .bat and .stk file types.
|
||||
- Added full backup/restore functionality. Backup downloads a zip that includes the SQL dump and uploads folder, setup now has option to restore from zip backup.
|
||||
- Migrated Asset and Contact Links to modals to resolve variable overlap issue.
|
||||
- Added Pagination to Notification Modal.
|
||||
- Removed 500 Records Per Page option.
|
||||
- Removed unused old DB checks in the top nav.
|
||||
- Clients can now use the portal to setup Stripe automatic payments themselves for recurring invoices
|
||||
- Automatic payments are now disabled for all recurring invoices if the saved payment method is removed
|
||||
- Added Card Details and Payment added to Client Stripe.
|
||||
- UI / UX updates to guest pay Make use of cards.
|
||||
- Don't show Checkbox columns when ticket is closed, compact ticket list now matches round pills for status and priority.
|
||||
- Ticket UI/UX update allow the ticket toolbar to be a little more mobile-friendly
|
||||
- UI / UX Updates to Expenses - Combine Category and Description into 1 column.
|
||||
- Country information is now displayed in Invoices, Quotes, Recurring Invoices, Clients, Locations, and the client top header.
|
||||
- Added country-based search filters in Locations and Clients sections.
|
||||
- Changed the settings name from Integrations to Identity Providers to make room for future iDPs (e.g. Google).
|
||||
- Bump FullCalendar from 6.1.15 to 6.1.17.
|
||||
- Bump DataTables from 2.2.2 to 2.3.1.
|
||||
- Bump TCPDF from 6.8.2 to 6.9.4.
|
||||
- Bump tinyMCE from 7.7.1 to 7.9.0.
|
||||
- Bump phpMailer from 6.9.2 to 6.10.0.
|
||||
- Bump stripe-php from 16.4.0 to 17.2.1.
|
||||
|
||||
|
||||
### Fixed
|
||||
- "None" option for SMTP encryption now functions correctly.
|
||||
- Debug table row counts now reflect actual counts instead of relying on SHOW TABLE STATUS.
|
||||
- Archived Categories now display properly.
|
||||
- Stripe saved payment methods are now limited to credit/debit cards only.
|
||||
|
||||
## [25.03.6]
|
||||
|
||||
### Fixed
|
||||
- Set default to date to 2035-12-31 as 9999-12-31 and 2999-12-31 broke certain browsers.
|
||||
- Update Client PDF Export, add header added company logo.
|
||||
- Present Larger clearer Warning about updates on update page.
|
||||
- Allow to search by project reference.
|
||||
|
||||
## [25.03.5]
|
||||
|
||||
### Fixed
|
||||
- Fixed the user listing issue when copying a trip.
|
||||
- Corrected the display of recurring invoice amounts on the dashboard.
|
||||
- Fixed the linking of entities with assets and contacts.
|
||||
- Resolved the issue with displaying the correct mobile country code in the contact listing.
|
||||
- Set the default date to `9999-12-31` to ensure future items (like invoices) are displayed by default.
|
||||
- Fixed the display issue where file folders were not showing properly during document creation.
|
||||
- Migrated from Dragula to SortableJS for a more modern, mobile-friendly solution.
|
||||
- Added Handlebars icons for drag-and-drop items.
|
||||
- Changed behavior to open Contact and Asset Details pages directly instead of using a modal.
|
||||
|
||||
## [25.03.4]
|
||||
|
||||
### Fixed
|
||||
- Ability to remove additional assets from the ticket details screen.
|
||||
- Fix the ability to remove assets from edit ticket not working when only 1 asset exists.
|
||||
- Fix Database Backup corruption.
|
||||
- Client Portal - show ticket number instead of ticket id in ticket listing.
|
||||
- Add Purchase Reference to copy asset.
|
||||
- Add Link to asset details from the global search.
|
||||
- Fix Bulk assign ticket only showing contacts instead of ITFlow users.
|
||||
|
||||
|
||||
## [25.03.3]
|
||||
|
||||
### Fixed
|
||||
- Fix adding ITFlow user.
|
||||
- Do not alert on inactive recurring invoices.
|
||||
- Fix ticket user assignment including bulk assignment.
|
||||
- Fix adding a location phone extension.
|
||||
- Do not default to +1 Country code, instead default to null.
|
||||
- Do not format numbers unless a country code is entered.
|
||||
- Fix editing network location.
|
||||
- Fix ticket redaction on client replies.
|
||||
- Remove more from user activity as it requires admin privledges.
|
||||
- Fix MFA Enforcement page.
|
||||
|
||||
## [25.03.2]
|
||||
|
||||
### Fixed
|
||||
- Revert DB.sql change
|
||||
|
||||
## [25.03.1]
|
||||
|
||||
### Fixed
|
||||
- Phone number missing in various sections.
|
||||
- Match Database.
|
||||
- Client Export Only display licenses users and assets from the selected client only.
|
||||
|
||||
## [25.03]
|
||||
|
||||
### Fixed
|
||||
- Resolved missing attachments in ticket replies processed via the email parser.
|
||||
- Fixed issue where the top half of portrait image uploads appeared cut off at the bottom.
|
||||
- Ensured all tables and fields use `CHARACTER SET utf8mb4` and `COLLATE utf8mb4_general_ci` for updates and new installations.
|
||||
- Converted `service_domains` table to use InnoDB instead of MyISAM.
|
||||
- Fixed the initials function to properly handle UTF-8 characters, preventing contact-related issues.
|
||||
- Interfaces can now start with `0`.
|
||||
- Adjusted AI prompt handling to focus solely on content, avoiding unnecessary additions.
|
||||
|
||||
### Added / Changed
|
||||
- Introduced bulk delete functionality for assets.
|
||||
- Added the ability to redact ticket replies after a ticket is closed.
|
||||
- Added support for redacting specific text while a ticket is open.
|
||||
- Switched file upload hashing from SHA256 to MD5 to significantly improve performance.
|
||||
- Enabled assigning multiple assets to a single ticket.
|
||||
- Updated all many-to-many tables to support cascading deletes using foreign key associations, improving efficiency, performance, and data integrity.
|
||||
- Enabled caching for AJAX modals to reduce repeated reloads and enhance browser performance.
|
||||
- Upgraded DataTables from 2.2.1 to 2.2.2.
|
||||
- Upgraded TinyMCE from 7.6.1 to 7.7.1, providing a significant performance boost.
|
||||
- Added “Copy Credentials to Clipboard” button in AJAX asset and contact views.
|
||||
- Renamed and reorganized several tables.
|
||||
- Improved theme color organization by grouping primary colors and their related shades.
|
||||
- Displayed a user icon next to contacts who have user accounts.
|
||||
- New image uploads are now converted to optimized `.webp` format by default; original files are no longer saved. Existing images remain unchanged.
|
||||
- Added international phone number support throughout the system.
|
||||
- Introduced user signatures in preferences, which are now appended to all ticket replies.
|
||||
- Optimized search filters to only display defined tags.
|
||||
- Added “Projects” to the client-side navigation.
|
||||
- Enabled “Create New Ticket” from within project details.
|
||||
- Reintroduced batch payment functionality in client invoices.
|
||||
- Included client abbreviations in both client and global search options.
|
||||
- Added assigned software license details (User/Asset) to the client PDF export.
|
||||
- Replaced client-side `pdfMake` with the PHP-based `TCPDF` library for generating client export runbooks.
|
||||
- Introduced the ability to download documents as PDFs.
|
||||
- Added a “Reference” field to tickets and invoices generated from recurring templates (not yet in active use).
|
||||
|
||||
### Breaking Changes
|
||||
> **Important:** To update to this version, you **must** run the following commands from the command line from the scripts directory:
|
||||
>
|
||||
> ```bash
|
||||
> php update_cli.php
|
||||
> php update_cli.php --db_update
|
||||
> ```
|
||||
>
|
||||
> Repeat `--db_update` until no further updates are found.
|
||||
>
|
||||
> **Back up your system before upgrading.**
|
||||
> This version includes numerous backend changes critical for future development.
|
||||
|
||||
## [25.02.4]
|
||||
|
||||
### Fixed
|
||||
- Resolved issue preventing the addition or editing of licenses when no vendor was selected.
|
||||
- Fixed several undeclared variables in AJAX contact details.
|
||||
- Corrected the contact ticket count display.
|
||||
- Addressed an issue where clicking "More Details" in AJAX contact/asset details failed to include the `client_id` in the URL.
|
||||
- Fixed an issue with recurring invoices in the client URL: clicking "Inactive" or "Active" would unexpectedly navigate away from the client section.
|
||||
- Added new php function getFieldById() to return a record using just an id and sanitized as well.
|
||||
|
||||
## [25.02.3]
|
||||
|
||||
### Fixed
|
||||
- Fixed notifications being reversed as dismissed notifications.
|
||||
|
||||
## [25.02.2]
|
||||
|
||||
### Fixed
|
||||
- Corrected some edit modals not showing notes correctly.
|
||||
- Bugfix: When exporting to CSV, the first asset wasn't being shown.
|
||||
- Fix broken create / edit credentials.
|
||||
- Fixed missing Notificatons link.
|
||||
- Fixed a few dead links.
|
||||
- Fixed Overdue count also counting Non-Billable Invoices.
|
||||
- Fix Edit Client Notes.
|
||||
|
||||
### Added / Changed
|
||||
- Implemented SSL certificate history tracking.
|
||||
- Added Inactive / Active Filter to Recurring Invoices.
|
||||
- Merged Dismissed notifications and notification in one.
|
||||
- Added Link Button to addd / edit Document WYSIWYG.
|
||||
- Added Physical location to the asset export / import.
|
||||
|
||||
## [25.02.1]
|
||||
### Fixed
|
||||
- Resolved broken links in the client overview, project and client listings, and rack details.
|
||||
- Corrected asset transfer functionality to clients.
|
||||
- Fixed the ticket scheduling redirect.
|
||||
- Corrected the ticket link in the Scheduled Ticket Agent Notification email.
|
||||
- Addressed issues with credentials and ticket actions in the Contact Detail Modal.
|
||||
- Fixed text wrapping in notifications.
|
||||
- Adjusted notifications so that they are sorted with the newest first.
|
||||
- Fixed drag-and-drop functionality for tickets in the Kanban view on mobile devices.
|
||||
- Resolved a weird issue with TinyMCE that prevented using links referencing your ITFlow instance url.
|
||||
- Corrected image orientation issues during upload and the preview optimization process.
|
||||
|
||||
### Added / Changed
|
||||
- Introduced entity link indicator icons and counts in the contacts and credentials section.
|
||||
- Implemented a fade animation for the new AJAX modal.
|
||||
- Removed the Client Overview Expire Day Select and replaced it with simplified 1, 7, or 45-day options.
|
||||
- Added the ability to link and unlink entities within asset details.
|
||||
- Introduced quick tag/category creation across the app.
|
||||
- Added a Vendor Quick Details Modal.
|
||||
- Enabled vendor linking and added a License Purchase Reference in the Software Licenses section.
|
||||
- Added download original, optimized and thumbnail option for images.
|
||||
- Added Paid status to the top corner of Invoice PDFs.
|
||||
|
||||
## [25.02]
|
||||
### Fixed
|
||||
- Migrated several reports to the new permissions/roles system.
|
||||
- Resolved issue with empty task box showing for closed/resolved tickets.
|
||||
- Corrected ticket priority sorting.
|
||||
- Cloned asset interfaces when transferring assets between clients.
|
||||
|
||||
### Added / Changed
|
||||
- Restored max number of records per page option back to 500 since we dont have repeating modals.
|
||||
- Bulk Categorize Tickets feature.
|
||||
- Renamed "Interface port" to "Interface Description." "Interface Name" should now refer to port name and/or number.
|
||||
- Changed "Transfer Asset to Client" from a single action to a bulk action.
|
||||
- Updated Filter Footer UI to show "Showing x to x of x records" instead of just the total records.
|
||||
- Added Client Overview section to view client assets, contacts, licenses, credentials, etc.
|
||||
- Introduced Quick Peek for asset details, contact information, and document viewing throughout the ITFlow App, all made possible by AJAX.
|
||||
- Enabled Simple Drag-and-Drop Ordering for Invoices, Recurring Invoices, Quotes, Ticket Tasks, and Ticket Template Tasks.
|
||||
- Added new Ticket View options: Kanban and Simple View.
|
||||
- Migrated all repeating modals to the new AJAX modal function for faster loading times and quicker development.
|
||||
- Allowed clients to upload PDF documents to accepted quotes.
|
||||
- Client Portal now shows ticket category.
|
||||
- Custom links can now be added to the Client Portal navbar.
|
||||
- Lots of little tweaks to UI, performance, bugs, etc.
|
||||
|
||||
### Breaking Changes
|
||||
- Cron scripts have officially been moved to the /scripts folder and are no longer in the root directory; they must be updated to function properly.
|
||||
|
||||
## [25.01.3]
|
||||
### Fixed
|
||||
- Fixed ticket assignment modal showing client contacts.
|
||||
|
||||
## [25.01.2]
|
||||
### Fixed
|
||||
- Fixed app version.
|
||||
|
|
@ -83,4 +727,4 @@ This file documents all notable changes made to ITFlow.
|
|||
## [24.12]
|
||||
|
||||
### Added / Changed
|
||||
- Introduced versioned releases for the first time!
|
||||
- Introduced versioned releases for the first time!
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ If you want to improve ITFlow, feel free to fork the repo and create a pull requ
|
|||
We’re incredibly grateful to the organizations and individuals who support the project - a big thank you to:
|
||||
- CompuMatter
|
||||
- F1 for HELP
|
||||
- JetBrains (PhpStorm)
|
||||
|
||||
## License
|
||||
ITFlow is distributed "as is" under the GPL License, WITHOUT WARRANTY OF ANY KIND. See [`LICENSE`](https://github.com/itflow-org/itflow/blob/master/LICENSE) for details.
|
||||
|
|
|
|||
|
|
@ -12,10 +12,8 @@
|
|||
We operate a rolling release model. Any bug fixes will be released into latest version of ITFlow, so you must stay up-to-date.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| Beta | :x: |
|
||||
| 24.12 | :white_check_mark: |
|
||||
| 25.1 | :white_check_mark: (When released) |
|
||||
|---------| ------------------ |
|
||||
| 25.12 | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability via GitHub Security Advisories
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "ai_model_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models LEFT JOIN ai_providers ON ai_model_ai_provider_id = ai_provider_id ORDER BY $sort $order");
|
||||
|
||||
$num_rows = mysqli_num_rows($sql);
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="/admin">Admin</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="ai_provider.php">AI Providers</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">AI Models</li>
|
||||
</ol>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Models</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_model_add.php"><i class="fas fa-plus mr-2"></i>Add Model</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_name&order=<?php echo $disp; ?>">
|
||||
Model <?php if ($sort == 'ai_model_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
|
||||
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_use_case&order=<?php echo $disp; ?>">
|
||||
Use Case<?php if ($sort == 'ai_model_use_case') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark">Prompt</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$provider_id = intval($row['ai_provider_id']);
|
||||
$provider_name = nullable_htmlentities($row['ai_provider_name']);
|
||||
$model_id = intval($row['ai_model_id']);
|
||||
$model_name = nullable_htmlentities($row['ai_model_name']);
|
||||
$use_case = nullable_htmlentities($row['ai_model_use_case']);
|
||||
$prompt = nl2br(nullable_htmlentities($row['ai_model_prompt']));
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark text-bold ajax-modal" href="#"
|
||||
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
|
||||
<?php echo $model_name; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $provider_name; ?></td>
|
||||
<td><?php echo $use_case; ?></td>
|
||||
<td><?php echo $prompt; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_model=<?php echo $model_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
if ($num_rows == 0) {
|
||||
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "ai_provider_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers ORDER BY $sort $order");
|
||||
|
||||
$num_rows = mysqli_num_rows($sql);
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Providers</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_provider_add.php"><i class="fas fa-plus mr-2"></i>Add Provider</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
|
||||
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_url&order=<?php echo $disp; ?>">
|
||||
URL <?php if ($sort == 'ai_provider_api_url') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_key&order=<?php echo $disp; ?>">
|
||||
Key <?php if ($sort == 'ai_provider_api_key') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<a class="text-dark">Models</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$provider_id = intval($row['ai_provider_id']);
|
||||
$provider_name = nullable_htmlentities($row['ai_provider_name']);
|
||||
$url = nullable_htmlentities($row['ai_provider_api_url']);
|
||||
$key = nullable_htmlentities($row['ai_provider_api_key']);
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ai_model_id') AS ai_model_count FROM ai_models WHERE ai_model_ai_provider_id = $provider_id"));
|
||||
$ai_model_count = intval($row['ai_model_count']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark text-bold ajax-modal" href="#"
|
||||
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
|
||||
<?php echo $provider_name; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $url; ?></td>
|
||||
<td><?php echo $key; ?></td>
|
||||
<td class="text-center">
|
||||
<a class="badge badge-dark badge-pill p-2" href="ai_model.php"><?= $ai_model_count ?></a>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_provider=<?php echo $provider_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
if ($num_rows == 0) {
|
||||
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "api_key_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM api_keys
|
||||
LEFT JOIN clients on api_keys.api_key_client_id = clients.client_id
|
||||
WHERE (api_key_name LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-key mr-2"></i>API Keys</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/api/api_key_add.php"><i class="fas fa-plus mr-2"></i>New API Key</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<form autocomplete="off">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search keys">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="btn-group float-right">
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_api_keys">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Revoke
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_name&order=<?php echo $disp; ?>">
|
||||
Name <?php if ($sort == 'api_key_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_client_id&order=<?php echo $disp; ?>">
|
||||
Client <?php if ($sort == 'api_key_client_id') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_secret&order=<?php echo $disp; ?>">
|
||||
Secret <?php if ($sort == 'api_key_secret') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_created_at&order=<?php echo $disp; ?>">
|
||||
Created <?php if ($sort == 'api_key_created_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_expire&order=<?php echo $disp; ?>">
|
||||
Expires <?php if ($sort == 'api_key_expire') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$api_key_id = intval($row['api_key_id']);
|
||||
$api_key_name = nullable_htmlentities($row['api_key_name']);
|
||||
$api_key_secret = nullable_htmlentities("************" . substr($row['api_key_secret'], -4));
|
||||
$api_key_created_at = nullable_htmlentities($row['api_key_created_at']);
|
||||
$api_key_expire = nullable_htmlentities($row['api_key_expire']);
|
||||
if ($api_key_expire < date("Y-m-d H:i:s")) {
|
||||
$api_key_expire = $api_key_expire . " (Expired)";
|
||||
}
|
||||
|
||||
if ($row['api_key_client_id'] == 0) {
|
||||
$api_key_client = "<i>All Clients</i>";
|
||||
} else {
|
||||
$api_key_client = nullable_htmlentities($row['client_name']);
|
||||
}
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="api_key_ids[]" value="<?php echo $api_key_id ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-bold"><?php echo $api_key_name; ?></td>
|
||||
<td><?php echo $api_key_client; ?></td>
|
||||
<td><?php echo $api_key_secret; ?></td>
|
||||
<td><?php echo $api_key_created_at; ?></td>
|
||||
<td><?php echo $api_key_expire; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_api_key=<?php echo $api_key_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-times mr-2"></i>Revoke
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php"; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/bulk_actions.js"></script>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "app_log_id";
|
||||
$order = "DESC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
// Log Type Filter
|
||||
if (isset($_GET['type']) & !empty($_GET['type'])) {
|
||||
$log_type_query = "AND (app_log_type = '" . sanitizeInput($_GET['type']) . "')";
|
||||
$type_filter = nullable_htmlentities($_GET['type']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_type_query = '';
|
||||
$type_filter = '';
|
||||
}
|
||||
|
||||
// Log Category Filter
|
||||
if (isset($_GET['category']) & !empty($_GET['catergory'])) {
|
||||
$log_category_query = "AND (app_log_category = '" . sanitizeInput($_GET['category']) . "')";
|
||||
$category_filter = nullable_htmlentities($_GET['category']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_category_query = '';
|
||||
$category_filter = '';
|
||||
}
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM app_logs
|
||||
WHERE (app_log_type LIKE '%$q%' OR app_log_category LIKE '%$q%' OR app_log_details LIKE '%$q%')
|
||||
AND DATE(app_log_created_at) BETWEEN '$dtf' AND '$dtt'
|
||||
$log_type_query
|
||||
$log_category_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>App Logs</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search app logs">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="type" onchange="this.form.submit()">
|
||||
<option value="">- All Types -</option>
|
||||
|
||||
<?php
|
||||
$sql_types_filter = mysqli_query($mysqli, "SELECT DISTINCT app_log_type FROM app_logs ORDER BY app_log_type ASC");
|
||||
while ($row = mysqli_fetch_array($sql_types_filter)) {
|
||||
$log_type = nullable_htmlentities($row['app_log_type']);
|
||||
?>
|
||||
<option <?php if ($type_filter == $log_type) { echo "selected"; } ?>><?php echo $log_type; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="category" onchange="this.form.submit()">
|
||||
<option value="">- All Categories -</option>
|
||||
|
||||
<?php
|
||||
$sql_categories_filter = mysqli_query($mysqli, "SELECT DISTINCT app_log_category FROM app_logs ORDER BY app_log_category ASC");
|
||||
while ($row = mysqli_fetch_array($sql_categories_filter)) {
|
||||
$log_category = nullable_htmlentities($row['app_log_category']);
|
||||
?>
|
||||
<option <?php if ($category_filter == $log_category) { echo "selected"; } ?>><?php echo $log_category; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Date range</label>
|
||||
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
|
||||
<input type="hidden" name="canned_date" id="canned_date" value="<?php echo nullable_htmlentities($_GET['canned_date']) ?? ''; ?>">
|
||||
<input type="hidden" name="dtf" id="dtf" value="<?php echo nullable_htmlentities($dtf ?? ''); ?>">
|
||||
<input type="hidden" name="dtt" id="dtt" value="<?php echo nullable_htmlentities($dtt ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-sm table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_created_at&order=<?php echo $disp; ?>">
|
||||
Timestamp <?php if ($sort == 'app_log_created_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_type&order=<?php echo $disp; ?>">
|
||||
Type <?php if ($sort == 'app_log_type') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_category&order=<?php echo $disp; ?>">
|
||||
Category <?php if ($sort == 'app_log_category') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_details&order=<?php echo $disp; ?>">
|
||||
Details <?php if ($sort == 'app_log_details') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$log_id = intval($row['app_log_id']);
|
||||
$log_type = nullable_htmlentities($row['app_log_type']);
|
||||
$log_category = nullable_htmlentities($row['app_log_category']);
|
||||
$log_details = nullable_htmlentities($row['app_log_details']);
|
||||
$log_created_at = nullable_htmlentities($row['app_log_created_at']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><?php echo $log_created_at; ?></td>
|
||||
<td><?php echo $log_type; ?></td>
|
||||
<td><?php echo $log_category; ?></td>
|
||||
<td><?php echo $log_details; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "log_id";
|
||||
$order = "DESC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
// User Filter
|
||||
if (isset($_GET['user']) & !empty($_GET['user'])) {
|
||||
$user_query = 'AND (log_user_id = ' . intval($_GET['user']) . ')';
|
||||
$user_filter = intval($_GET['user']);
|
||||
} else {
|
||||
// Default - any
|
||||
$user_query = '';
|
||||
$user_filter = '';
|
||||
}
|
||||
|
||||
// Client Filter
|
||||
if (isset($_GET['client']) & !empty($_GET['client'])) {
|
||||
$client_query = 'AND (log_client_id = ' . intval($_GET['client']) . ')';
|
||||
$client_filter = intval($_GET['client']);
|
||||
} else {
|
||||
// Default - any
|
||||
$client_query = '';
|
||||
$client_filter = '';
|
||||
}
|
||||
|
||||
// Log Type Filter
|
||||
if (isset($_GET['type']) & !empty($_GET['type'])) {
|
||||
$log_type_query = "AND (log_type = '" . sanitizeInput($_GET['type']) . "')";
|
||||
$type_filter = nullable_htmlentities($_GET['type']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_type_query = '';
|
||||
$type_filter = '';
|
||||
}
|
||||
|
||||
// Log Action Filter
|
||||
if (isset($_GET['action']) & !empty($_GET['action'])) {
|
||||
$log_action_query = "AND (log_action = '" . sanitizeInput($_GET['action']) . "')";
|
||||
$action_filter = nullable_htmlentities($_GET['action']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_action_query = '';
|
||||
$action_filter = '';
|
||||
}
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM logs
|
||||
LEFT JOIN users ON log_user_id = user_id
|
||||
LEFT JOIN clients ON log_client_id = client_id
|
||||
WHERE (log_type LIKE '%$q%' OR log_action LIKE '%$q%' OR log_description LIKE '%$q%' OR log_ip LIKE '%$q%' OR log_user_agent LIKE '%$q%' OR user_name LIKE '%$q%' OR client_name LIKE '%$q%')
|
||||
AND DATE(log_created_at) BETWEEN '$dtf' AND '$dtt'
|
||||
$user_query
|
||||
$client_query
|
||||
$log_type_query
|
||||
$log_action_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>Audit Logs</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="mb-4" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search audit logs">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<select class="form-control select2" name="client" onchange="this.form.submit()">
|
||||
<option value="">- All Clients -</option>
|
||||
|
||||
<?php
|
||||
$sql_clients_filter = mysqli_query($mysqli, "SELECT * FROM clients ORDER BY client_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_clients_filter)) {
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
?>
|
||||
<option <?php if ($client_filter == $client_id) { echo "selected"; } ?> value="<?php echo $client_id; ?>"><?php echo $client_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<select class="form-control select2" name="user" onchange="this.form.submit()">
|
||||
<option value="">- All Users -</option>
|
||||
|
||||
<?php
|
||||
$sql_users_filter = mysqli_query($mysqli, "SELECT * FROM users ORDER BY user_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_users_filter)) {
|
||||
$user_id = intval($row['user_id']);
|
||||
$user_name = nullable_htmlentities($row['user_name']);
|
||||
?>
|
||||
<option <?php if ($user_filter == $user_id) { echo "selected"; } ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<select class="form-control select2" name="type" onchange="this.form.submit()">
|
||||
<option value="">- All Types -</option>
|
||||
|
||||
<?php
|
||||
$sql_types_filter = mysqli_query($mysqli, "SELECT DISTINCT log_type FROM logs ORDER BY log_type ASC");
|
||||
while ($row = mysqli_fetch_array($sql_types_filter)) {
|
||||
$log_type = nullable_htmlentities($row['log_type']);
|
||||
?>
|
||||
<option <?php if ($type_filter == $log_type) { echo "selected"; } ?>><?php echo $log_type; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<select class="form-control select2" name="action" onchange="this.form.submit()">
|
||||
<option value="">- All Actions -</option>
|
||||
|
||||
<?php
|
||||
$sql_actions_filter = mysqli_query($mysqli, "SELECT DISTINCT log_action FROM logs ORDER BY log_action ASC");
|
||||
while ($row = mysqli_fetch_array($sql_actions_filter)) {
|
||||
$log_action = nullable_htmlentities($row['log_action']);
|
||||
?>
|
||||
<option <?php if ($action_filter == $log_action) { echo "selected"; } ?>><?php echo $log_action; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Date range</label>
|
||||
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
|
||||
<input type="hidden" name="canned_date" id="canned_date" value="<?php echo nullable_htmlentities($_GET['canned_date']) ?? ''; ?>">
|
||||
<input type="hidden" name="dtf" id="dtf" value="<?php echo nullable_htmlentities($dtf ?? ''); ?>">
|
||||
<input type="hidden" name="dtt" id="dtt" value="<?php echo nullable_htmlentities($dtt ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-sm table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_created_at&order=<?php echo $disp; ?>">
|
||||
Timestamp <?php if ($sort == 'log_created_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=user_name&order=<?php echo $disp; ?>">
|
||||
User <?php if ($sort == 'user_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<?php if (empty($client)) { ?>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">
|
||||
Client <?php if ($sort == 'client_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<?php } ?>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_type&order=<?php echo $disp; ?>">
|
||||
Type <?php if ($sort == 'log_type') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_action&order=<?php echo $disp; ?>">
|
||||
Action <?php if ($sort == 'log_action') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_description&order=<?php echo $disp; ?>">
|
||||
Description <?php if ($sort == 'log_description') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_ip&order=<?php echo $disp; ?>">
|
||||
IP Address <?php if ($sort == 'log_ip') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_user_agent&order=<?php echo $disp; ?>">
|
||||
User Agent <?php if ($sort == 'log_user_agent') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$log_id = intval($row['log_id']);
|
||||
$log_type = nullable_htmlentities($row['log_type']);
|
||||
$log_action = nullable_htmlentities($row['log_action']);
|
||||
$log_description = nullable_htmlentities($row['log_description']);
|
||||
$log_ip = nullable_htmlentities($row['log_ip']);
|
||||
$log_user_agent = nullable_htmlentities($row['log_user_agent']);
|
||||
$log_user_os = getOS($log_user_agent);
|
||||
$log_user_browser = getWebBrowser($log_user_agent);
|
||||
$log_created_at = nullable_htmlentities($row['log_created_at']);
|
||||
$user_id = intval($row['user_id']);
|
||||
$user_name = nullable_htmlentities($row['user_name']);
|
||||
if (empty($user_name)) {
|
||||
$user_name_display = "-";
|
||||
} else {
|
||||
$user_name_display = $user_name;
|
||||
}
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
$client_id = intval($row['client_id']);
|
||||
if (empty($client_name)) {
|
||||
$client_name_display = "-";
|
||||
} else {
|
||||
$client_name_display = "<a href='../agent/client_overview.php?client_id=$client_id'>$client_name</a>";
|
||||
}
|
||||
$log_entity_id = intval($row['log_entity_id']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><?php echo $log_created_at; ?></td>
|
||||
<td><?php echo $user_name_display; ?></td>
|
||||
<?php if(empty($client)) { ?>
|
||||
<td><?php echo $client_name_display; ?></td>
|
||||
<?php } ?>
|
||||
<td><?php echo $log_type; ?></td>
|
||||
<td><?php echo $log_action; ?></td>
|
||||
<td><?php echo $log_description; ?></td>
|
||||
<td><?php echo $log_ip; ?></td>
|
||||
<td><?php echo "$log_user_os<div class='text-secondary'>$log_user_browser</div>"; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
require_once "includes/inc_all_admin.php";
|
||||
?>
|
||||
|
||||
<div class="card card-dark mb-3">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-database mr-2"></i>Download Database</h3>
|
||||
</div>
|
||||
<div class="card-body" style="text-align: center;">
|
||||
<div class="alert alert-secondary">If you are unable to back up the entire VM, you'll need to back up the files & database individually. There is no built-in restore. See the <a href="https://docs.itflow.org/backups" target="_blank">docs here</a>.</div>
|
||||
<a class="btn btn-primary btn-lg p-3" href="post.php?download_backup&csrf_token=<?php echo $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download"></i><br><br>Download Backup</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-key mr-2"></i>Backup Master Encryption Key</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body">
|
||||
<form action="post.php" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="input-group col-sm-4">
|
||||
<input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-key"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "category_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
|
||||
if (isset($_GET['category'])) {
|
||||
$category = sanitizeInput($_GET['category']);
|
||||
} else {
|
||||
$category = "Expense";
|
||||
}
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM categories
|
||||
WHERE category_name LIKE '%$q%'
|
||||
AND category_type = '$category'
|
||||
AND category_$archive_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-list-ul mr-2"></i>
|
||||
<?php echo nullable_htmlentities($category); ?> Categories
|
||||
</h3>
|
||||
<?php
|
||||
if (!isset($_GET['archived'])) {
|
||||
?>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/category/category_add.php?category=<?= nullable_htmlentities($category) ?>"><i
|
||||
class="fas fa-plus mr-2"></i>New <?php echo nullable_htmlentities($category); ?> Category</button>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form autocomplete="off">
|
||||
<input type="hidden" name="category" value="<?php echo nullable_htmlentities($category); ?>">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-2">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q"
|
||||
value="<?php if (isset($q)) {
|
||||
echo stripslashes(nullable_htmlentities($q));
|
||||
} ?>"
|
||||
placeholder="Search <?php echo nullable_htmlentities($category); ?> Categories ">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="btn-group float-right">
|
||||
<a href="?category=Expense"
|
||||
class="btn <?php if ($category == 'Expense') {
|
||||
echo 'btn-primary';
|
||||
} else {
|
||||
echo 'btn-default';
|
||||
} ?>">Expense</a>
|
||||
<a href="?category=Income"
|
||||
class="btn <?php if ($category == 'Income') {
|
||||
echo 'btn-primary';
|
||||
} else {
|
||||
echo 'btn-default';
|
||||
} ?>">Income</a>
|
||||
<a href="?category=Referral"
|
||||
class="btn <?php if ($category == 'Referral') {
|
||||
echo 'btn-primary';
|
||||
} else {
|
||||
echo 'btn-default';
|
||||
} ?>">Referral</a>
|
||||
<a href="?category=Ticket"
|
||||
class="btn <?php if ($category == 'Ticket') {
|
||||
echo 'btn-primary';
|
||||
} else {
|
||||
echo 'btn-default';
|
||||
} ?>">Ticket</a>
|
||||
<a href="?<?php echo $url_query_strings_sort ?>&archived=1"
|
||||
class="btn <?php if (isset($_GET['archived'])) {
|
||||
echo 'btn-primary';
|
||||
} else {
|
||||
echo 'btn-default';
|
||||
} ?>"><i
|
||||
class="fas fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">
|
||||
Name <?php if ($sort == 'category_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>Color</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$category_id = intval($row['category_id']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
$category_color = nullable_htmlentities($row['category_color']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark ajax-modal" href="#"
|
||||
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
|
||||
<?php echo $category_name; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><i class="fa fa-3x fa-circle" style="color:<?php echo $category_color; ?>;"></i></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php
|
||||
if ($archived) {
|
||||
?>
|
||||
<a class="dropdown-item text-success confirm-link"
|
||||
href="post.php?unarchive_category=<?php echo $category_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Unarchive
|
||||
</a>
|
||||
<a class="dropdown-item text-danger confirm-link"
|
||||
href="post.php?delete_category=<?php echo $category_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<a class="dropdown-item text-danger confirm-link"
|
||||
href="post.php?archive_category=<?php echo $category_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sort by Filter
|
||||
$sort = "contract_template_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
// Search query
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM contract_templates
|
||||
WHERE contract_template_name LIKE '%$q%' OR contract_template_type LIKE '%$q%' OR contract_template_name LIKE '%$q%'
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-contract mr-2"></i>Contract Templates</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/contract_template/contract_template_add.php" data-modal-size="lg">
|
||||
<i class="fas fa-plus mr-2"></i>New Template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form autocomplete="off">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search templates">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>Template Name</th>
|
||||
<th>Type</th>
|
||||
<th>Update Frequency</th>
|
||||
<th>SLA (L/M/H Response)</th>
|
||||
<th>SLA (L/M/H Resolution)</th>
|
||||
<th>Hourly Rate</th>
|
||||
<th>After Hours Rate</th>
|
||||
<th>Support Hours</th>
|
||||
<th>Net Terms</th>
|
||||
<th>Created</th>
|
||||
<th>Updated</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$id = intval($row['contract_template_id']);
|
||||
$name = nullable_htmlentities($row['contract_template_name']);
|
||||
$type = nullable_htmlentities($row['contract_template_type']);
|
||||
$freq = nullable_htmlentities($row['contract_template_update_frequency']);
|
||||
$sla_low_resp = nullable_htmlentities($row['sla_low_response_time']);
|
||||
$sla_med_resp = nullable_htmlentities($row['sla_medium_response_time']);
|
||||
$sla_high_resp = nullable_htmlentities($row['sla_high_response_time']);
|
||||
$sla_low_res = nullable_htmlentities($row['sla_low_resolution_time']);
|
||||
$sla_med_res = nullable_htmlentities($row['sla_medium_resolution_time']);
|
||||
$sla_high_res = nullable_htmlentities($row['sla_high_resolution_time']);
|
||||
$hourly_rate = nullable_htmlentities($row['contract_template_hourly_rate']);
|
||||
$after_hours = nullable_htmlentities($row['contract_template_after_hours_hourly_rate']);
|
||||
$support_hours = nullable_htmlentities($row['contract_template_support_hours']);
|
||||
$net_terms = nullable_htmlentities($row['contract_template_net_terms']);
|
||||
$created = nullable_htmlentities($row['contract_template_created_at']);
|
||||
$updated = nullable_htmlentities($row['contract_template_updated_at']);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-bold" href="contract_template_details.php?contract_template_id=<?php echo $id; ?>">
|
||||
<i class="fas fa-fw fa-file-alt text-dark"></i> <?php echo $name; ?>
|
||||
</a>
|
||||
<div class="mt-1 text-secondary"><?php echo nullable_htmlentities($row['contract_template_description']); ?></div>
|
||||
</td>
|
||||
<td><?php echo $type; ?></td>
|
||||
<td><?php echo $freq; ?></td>
|
||||
<td><?php echo "$sla_low_resp / $sla_med_resp / $sla_high_resp"; ?></td>
|
||||
<td><?php echo "$sla_low_res / $sla_med_res / $sla_high_res"; ?></td>
|
||||
<td><?php echo $hourly_rate; ?></td>
|
||||
<td><?php echo $after_hours; ?></td>
|
||||
<td><?php echo $support_hours; ?></td>
|
||||
<td><?php echo $net_terms; ?></td>
|
||||
<td><?php echo $created; ?></td>
|
||||
<td><?php echo $updated; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-size="xl"
|
||||
data-modal-url="modals/contract_template/contract_template_edit.php?id=<?= $id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold" href="post.php?delete_contract_template=<?php echo $id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php"; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once "../includes/footer.php"; ?>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|
||||
- Custom Pages -
|
||||
If you wish to add custom pages to ITFlow, add them to this directory"
|
||||
Link to Documentation for File Directory Structure and examples
|
||||
*/
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "custom_link_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM custom_links
|
||||
WHERE custom_link_name LIKE '%$q%'
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Custom Links</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/custom_link/custom_link_add.php"><i class="fas fa-plus mr-2"></i>New Link</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-2">
|
||||
<form autocomplete="off">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Links">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_name&order=<?php echo $disp; ?>">
|
||||
Name <?php if ($sort == 'custom_link_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_order&order=<?php echo $disp; ?>">
|
||||
Order <?php if ($sort == 'custom_link_order') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_uri&order=<?php echo $disp; ?>">
|
||||
URI / <span class="text-secondary">New Tab</span> <?php if ($sort == 'custom_link_uri') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_location&order=<?php echo $disp; ?>">
|
||||
Location <?php if ($sort == 'custom_link_location') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$custom_link_id = intval($row['custom_link_id']);
|
||||
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
|
||||
$custom_link_uri = nullable_htmlentities($row['custom_link_uri']);
|
||||
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
|
||||
$custom_link_new_tab = intval($row['custom_link_new_tab']);
|
||||
if ($custom_link_new_tab == 1 ) {
|
||||
$custom_link_new_tab_display = "<i class='fas fa-fw fa-checkmark'></i>";
|
||||
} else {
|
||||
$custom_link_new_tab_display = "";
|
||||
}
|
||||
$custom_link_order = intval($row['custom_link_order']);
|
||||
if ($custom_link_order == 0 ) {
|
||||
$custom_link_order_display = "-";
|
||||
} else {
|
||||
$custom_link_order_display = $custom_link_order;
|
||||
}
|
||||
$custom_link_location = intval($row['custom_link_location']);
|
||||
if ($custom_link_location == 1) {
|
||||
$custom_link_location_display = "Main Side Nav";
|
||||
} elseif ($custom_link_location == 2) {
|
||||
$custom_link_location_display = "Top Nav";
|
||||
} elseif ($custom_link_location == 3) {
|
||||
$custom_link_location_display = "Client Portal Nav";
|
||||
} elseif ($custom_link_location == 4) {
|
||||
$custom_link_location_display = "Admin Nav";
|
||||
} elseif ($custom_link_location == 5) {
|
||||
$custom_link_location_display = "Reports Nav";
|
||||
}
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="ajax-modal" href="#"
|
||||
data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
|
||||
<i class="fa fa-fw fa-<?php echo $custom_link_icon; ?> mr-2"></i><?php echo $custom_link_name;?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $custom_link_order_display; ?></td>
|
||||
<td><?php echo "$custom_link_uri $custom_link_new_tab_display"; ?></td>
|
||||
<td><?php echo $custom_link_location_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_custom_link=<?php echo $custom_link_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,768 @@
|
|||
<?php
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
require_once "../includes/database_version.php";
|
||||
require_once "../config.php";
|
||||
|
||||
$checks = [];
|
||||
|
||||
// Execute the git command to get the latest commit hash
|
||||
$commitHash = shell_exec('git log -1 --format=%H');
|
||||
|
||||
// Get branch info
|
||||
$gitBranch = shell_exec('git rev-parse --abbrev-ref HEAD');
|
||||
|
||||
// Section: System Information
|
||||
$systemInfo = [];
|
||||
|
||||
// Operating System and Version
|
||||
$os = php_uname();
|
||||
$systemInfo[] = [
|
||||
'name' => 'Operating System',
|
||||
'value' => $os,
|
||||
];
|
||||
|
||||
// Web Server and Version
|
||||
$webServer = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
|
||||
$systemInfo[] = [
|
||||
'name' => 'Web Server',
|
||||
'value' => $webServer,
|
||||
];
|
||||
|
||||
// Kernel and Version
|
||||
$kernelVersion = php_uname('r');
|
||||
$systemInfo[] = [
|
||||
'name' => 'Kernel Version',
|
||||
'value' => $kernelVersion,
|
||||
];
|
||||
|
||||
// Database and Version
|
||||
$dbVersion = $mysqli->server_info;
|
||||
$systemInfo[] = [
|
||||
'name' => 'Database Version',
|
||||
'value' => $dbVersion,
|
||||
];
|
||||
|
||||
// Section: PHP Extensions
|
||||
$phpExtensions = [];
|
||||
$extensions = [
|
||||
'php-mysqli' => 'mysqli',
|
||||
'php-intl' => 'intl',
|
||||
'php-curl' => 'curl',
|
||||
'php-mbstring' => 'mbstring',
|
||||
'php-gd' => 'gd',
|
||||
'php-zip' => 'zip',
|
||||
'php-xml' => 'xml',
|
||||
];
|
||||
|
||||
foreach ($extensions as $name => $ext) {
|
||||
$loaded = extension_loaded($ext);
|
||||
$phpExtensions[] = [
|
||||
'name' => "$name installed",
|
||||
'passed' => $loaded,
|
||||
'value' => $loaded ? 'Installed' : 'Not Installed',
|
||||
];
|
||||
}
|
||||
|
||||
// Section: PHP Configuration
|
||||
$phpConfig = [];
|
||||
|
||||
// Check if shell_exec is enabled
|
||||
$disabled_functions = explode(',', ini_get('disable_functions'));
|
||||
$disabled_functions = array_map('trim', $disabled_functions);
|
||||
$shell_exec_enabled = !in_array('shell_exec', $disabled_functions);
|
||||
|
||||
$phpConfig[] = [
|
||||
'name' => 'shell_exec is enabled',
|
||||
'passed' => $shell_exec_enabled,
|
||||
'value' => $shell_exec_enabled ? 'Enabled' : 'Disabled',
|
||||
];
|
||||
|
||||
// Check upload_max_filesize and post_max_size >= 500M
|
||||
function return_bytes($val) {
|
||||
$val = trim($val);
|
||||
$unit = strtolower(substr($val, -1));
|
||||
$num = (float)$val;
|
||||
switch ($unit) {
|
||||
case 'g':
|
||||
$num *= 1024;
|
||||
case 'm':
|
||||
$num *= 1024;
|
||||
case 'k':
|
||||
$num *= 1024;
|
||||
}
|
||||
return $num;
|
||||
}
|
||||
|
||||
$required_bytes = 500 * 1024 * 1024; // 500M in bytes
|
||||
|
||||
$upload_max_filesize = ini_get('upload_max_filesize');
|
||||
$post_max_size = ini_get('post_max_size');
|
||||
|
||||
$upload_passed = return_bytes($upload_max_filesize) >= $required_bytes;
|
||||
$post_passed = return_bytes($post_max_size) >= $required_bytes;
|
||||
|
||||
$phpConfig[] = [
|
||||
'name' => 'upload_max_filesize >= 500M',
|
||||
'passed' => $upload_passed,
|
||||
'value' => $upload_max_filesize,
|
||||
];
|
||||
|
||||
$phpConfig[] = [
|
||||
'name' => 'post_max_size >= 500M',
|
||||
'passed' => $post_passed,
|
||||
'value' => $post_max_size,
|
||||
];
|
||||
|
||||
// PHP Memory Limit >= 128M
|
||||
$memoryLimit = ini_get('memory_limit');
|
||||
$memoryLimitBytes = return_bytes($memoryLimit);
|
||||
$memoryLimitPassed = $memoryLimitBytes >= (128 * 1024 * 1024);
|
||||
$phpConfig[] = [
|
||||
'name' => 'PHP Memory Limit >= 128M',
|
||||
'passed' => $memoryLimitPassed,
|
||||
'value' => $memoryLimit,
|
||||
];
|
||||
|
||||
// Max Execution Time >= 300 seconds
|
||||
$maxExecutionTime = ini_get('max_execution_time');
|
||||
$maxExecutionTimePassed = $maxExecutionTime >= 300;
|
||||
$phpConfig[] = [
|
||||
'name' => 'Max Execution Time >= 300 seconds',
|
||||
'passed' => $maxExecutionTimePassed,
|
||||
'value' => $maxExecutionTime . ' seconds',
|
||||
];
|
||||
|
||||
// Check PHP version >= 8.2.0
|
||||
$php_version = PHP_VERSION;
|
||||
$php_passed = version_compare($php_version, '8.2.0', '>=');
|
||||
|
||||
$phpConfig[] = [
|
||||
'name' => 'PHP version >= 8.2.0',
|
||||
'passed' => $php_passed,
|
||||
'value' => $php_version,
|
||||
];
|
||||
|
||||
// Section: Shell Commands
|
||||
$shellCommands = [];
|
||||
|
||||
if ($shell_exec_enabled) {
|
||||
$commands = ['whois', 'dig', 'git'];
|
||||
|
||||
foreach ($commands as $command) {
|
||||
$which = trim(shell_exec("which $command 2>/dev/null"));
|
||||
$exists = !empty($which);
|
||||
$shellCommands[] = [
|
||||
'name' => "Command '$command' available",
|
||||
'passed' => $exists,
|
||||
'value' => $exists ? $which : 'Not Found',
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// If shell_exec is disabled, mark commands as unavailable
|
||||
foreach (['whois', 'dig', 'git'] as $command) {
|
||||
$shellCommands[] = [
|
||||
'name' => "Command '$command' available",
|
||||
'passed' => false,
|
||||
'value' => 'shell_exec Disabled',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Section: SSL Checks
|
||||
$sslChecks = [];
|
||||
|
||||
// Check if accessing via HTTPS
|
||||
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;
|
||||
$sslChecks[] = [
|
||||
'name' => 'Accessing via HTTPS',
|
||||
'passed' => $https,
|
||||
'value' => $https ? 'Yes' : 'No',
|
||||
];
|
||||
|
||||
// SSL Certificate Validity Check
|
||||
if ($https) {
|
||||
$streamContext = stream_context_create(["ssl" => ["capture_peer_cert" => true]]);
|
||||
$socket = @stream_socket_client("ssl://{$_SERVER['HTTP_HOST']}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext);
|
||||
|
||||
if ($socket) {
|
||||
$params = stream_context_get_params($socket);
|
||||
$cert = $params['options']['ssl']['peer_certificate'];
|
||||
$certInfo = openssl_x509_parse($cert);
|
||||
|
||||
$validFrom = $certInfo['validFrom_time_t'];
|
||||
$validTo = $certInfo['validTo_time_t'];
|
||||
$currentTime = time();
|
||||
|
||||
$certValid = ($currentTime >= $validFrom && $currentTime <= $validTo);
|
||||
|
||||
$sslChecks[] = [
|
||||
'name' => 'SSL Certificate is valid',
|
||||
'passed' => $certValid,
|
||||
'value' => $certValid ? 'Valid' : 'Invalid or Expired',
|
||||
];
|
||||
} else {
|
||||
$sslChecks[] = [
|
||||
'name' => 'SSL Certificate is valid',
|
||||
'passed' => false,
|
||||
'value' => 'Unable to retrieve certificate',
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$sslChecks[] = [
|
||||
'name' => 'SSL Certificate is valid',
|
||||
'passed' => false,
|
||||
'value' => 'Not using HTTPS',
|
||||
];
|
||||
}
|
||||
|
||||
// Section: Domain Checks
|
||||
$domainChecks = [];
|
||||
|
||||
// Check if the site has a valid FQDN
|
||||
$fqdn = $_SERVER['HTTP_HOST'];
|
||||
$isValidFqdn = (bool) filter_var('http://' . $fqdn, FILTER_VALIDATE_URL) && preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/i', $fqdn);
|
||||
|
||||
$domainChecks[] = [
|
||||
'name' => 'Site has a valid FQDN',
|
||||
'passed' => $isValidFqdn,
|
||||
'value' => $fqdn,
|
||||
];
|
||||
|
||||
// Section: File Permissions
|
||||
$filePermissions = [];
|
||||
|
||||
// Check if web user has write access to webroot directory
|
||||
$webroot = $_SERVER['DOCUMENT_ROOT'];
|
||||
$writable = is_writable($webroot);
|
||||
$filePermissions[] = [
|
||||
'name' => 'Web user has write access to webroot directory',
|
||||
'passed' => $writable,
|
||||
'value' => $webroot,
|
||||
];
|
||||
|
||||
// Section: Uploads Directory Stats
|
||||
$uploadsStats = [];
|
||||
|
||||
// Define the uploads directory path
|
||||
$uploadsDir = __DIR__ . '/../uploads'; // Adjust the path if needed
|
||||
|
||||
if (is_dir($uploadsDir)) {
|
||||
// Function to recursively count files and calculate total size
|
||||
function getDirStats($dir) {
|
||||
$files = 0;
|
||||
$size = 0;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$files++;
|
||||
$size += $file->getSize();
|
||||
}
|
||||
}
|
||||
return ['files' => $files, 'size' => $size];
|
||||
}
|
||||
|
||||
$stats = getDirStats($uploadsDir);
|
||||
$sizeInMB = round($stats['size'] / (1024 * 1024), 2);
|
||||
|
||||
$uploadsStats[] = [
|
||||
'name' => 'Number of files in uploads directory',
|
||||
'value' => $stats['files'],
|
||||
];
|
||||
|
||||
$uploadsStats[] = [
|
||||
'name' => 'Total size of uploads directory (MB)',
|
||||
'value' => $sizeInMB . ' MB',
|
||||
];
|
||||
} else {
|
||||
$uploadsStats[] = [
|
||||
'name' => 'Uploads directory exists',
|
||||
'value' => 'Directory not found',
|
||||
];
|
||||
}
|
||||
|
||||
// Section: Database Stats
|
||||
$databaseStats = [];
|
||||
|
||||
// Get list of tables
|
||||
$tablesResult = $mysqli->query("SHOW TABLE STATUS");
|
||||
if ($tablesResult) {
|
||||
$totalTables = 0;
|
||||
$totalFields = 0;
|
||||
$totalRows = 0;
|
||||
$totalSize = 0;
|
||||
$tableDetails = [];
|
||||
|
||||
while ($table = $tablesResult->fetch_assoc()) {
|
||||
$tableName = $table['Name'];
|
||||
|
||||
// Accurate row count
|
||||
$countResult = $mysqli->query("SELECT COUNT(*) AS cnt FROM `$tableName`");
|
||||
$countRow = $countResult->fetch_assoc();
|
||||
$tableRows = $countRow['cnt'];
|
||||
$countResult->free();
|
||||
|
||||
$dataLength = $table['Data_length'];
|
||||
$indexLength = $table['Index_length'];
|
||||
$tableSize = ($dataLength + $indexLength) / (1024 * 1024); // Size in MB
|
||||
|
||||
// Get number of fields
|
||||
$fieldsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
|
||||
$numFields = $fieldsResult->num_rows;
|
||||
$fieldsResult->free();
|
||||
|
||||
$totalTables++;
|
||||
$totalFields += $numFields;
|
||||
$totalRows += $tableRows;
|
||||
$totalSize += $tableSize;
|
||||
|
||||
$tableDetails[] = [
|
||||
'name' => $tableName,
|
||||
'fields' => $numFields,
|
||||
'rows' => $tableRows,
|
||||
'size' => round($tableSize, 2),
|
||||
];
|
||||
}
|
||||
$tablesResult->free();
|
||||
|
||||
$databaseStats[] = [
|
||||
'name' => 'Total number of tables',
|
||||
'value' => $totalTables,
|
||||
];
|
||||
$databaseStats[] = [
|
||||
'name' => 'Total number of fields',
|
||||
'value' => $totalFields,
|
||||
];
|
||||
$databaseStats[] = [
|
||||
'name' => 'Total number of rows',
|
||||
'value' => $totalRows,
|
||||
];
|
||||
$databaseStats[] = [
|
||||
'name' => 'Total database size (MB)',
|
||||
'value' => round($totalSize, 2) . ' MB',
|
||||
];
|
||||
}
|
||||
|
||||
// Section: Database Structure Comparison
|
||||
$dbComparison = [];
|
||||
|
||||
// Path to the db.sql file
|
||||
$dbSqlFile = __DIR__ . '/../db.sql';
|
||||
|
||||
if (file_exists($dbSqlFile)) {
|
||||
// Read the db.sql file
|
||||
$sqlContent = file_get_contents($dbSqlFile);
|
||||
|
||||
// Remove comments and empty lines
|
||||
$lines = explode("\n", $sqlContent);
|
||||
$sqlStatements = [];
|
||||
$statement = '';
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Remove single-line comments
|
||||
$line = preg_replace('/--.*$/', '', $line);
|
||||
$line = preg_replace('/\/\*.*?\*\//', '', $line);
|
||||
|
||||
// Skip empty lines
|
||||
if (trim($line) == '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append line to the current statement
|
||||
$statement .= $line . "\n";
|
||||
|
||||
// Check if the statement ends with a semicolon
|
||||
if (preg_match('/;\s*$/', $line)) {
|
||||
$sqlStatements[] = $statement;
|
||||
$statement = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the CREATE TABLE statements
|
||||
$sqlTables = [];
|
||||
|
||||
foreach ($sqlStatements as $sql) {
|
||||
if (preg_match('/CREATE TABLE\s+`?([^` ]+)`?\s*\((.*)\)(.*?);/msi', $sql, $match)) {
|
||||
$tableName = $match[1];
|
||||
$columnsDefinition = $match[2];
|
||||
|
||||
// Extract column names and data types
|
||||
$columns = [];
|
||||
$columnLines = explode("\n", $columnsDefinition);
|
||||
foreach ($columnLines as $line) {
|
||||
$line = trim($line);
|
||||
// Skip empty lines and lines that do not define columns
|
||||
if ($line == '' || strpos($line, 'PRIMARY KEY') !== false || strpos($line, 'UNIQUE KEY') !== false || strpos($line, 'KEY') === 0 || strpos($line, 'CONSTRAINT') === 0 || strpos($line, ')') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove trailing comma if present
|
||||
$line = rtrim($line, ',');
|
||||
|
||||
// Match column definition
|
||||
if (preg_match('/^`([^`]+)`\s+(.+)/', $line, $colMatch)) {
|
||||
$colName = $colMatch[1];
|
||||
$colDefinition = $colMatch[2];
|
||||
|
||||
// Extract the data type from the column definition
|
||||
$tokens = preg_split('/\s+/', $colDefinition);
|
||||
$colType = $tokens[0];
|
||||
|
||||
// Handle data types with parentheses (e.g., varchar(255), decimal(15,2))
|
||||
if (preg_match('/^([a-zA-Z]+)\(([^)]+)\)/', $colType, $typeMatch)) {
|
||||
$colType = $typeMatch[1] . '(' . $typeMatch[2] . ')';
|
||||
}
|
||||
|
||||
$columns[$colName] = $colType;
|
||||
}
|
||||
}
|
||||
$sqlTables[$tableName] = $columns;
|
||||
}
|
||||
}
|
||||
|
||||
// Get current database table structures
|
||||
$dbTables = [];
|
||||
$tablesResult = $mysqli->query("SHOW TABLES");
|
||||
while ($row = $tablesResult->fetch_row()) {
|
||||
$tableName = $row[0];
|
||||
$columnsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
|
||||
$columns = [];
|
||||
while ($col = $columnsResult->fetch_assoc()) {
|
||||
$columns[$col['Field']] = $col['Type'];
|
||||
}
|
||||
$columnsResult->free();
|
||||
$dbTables[$tableName] = $columns;
|
||||
}
|
||||
$tablesResult->free();
|
||||
|
||||
// Compare the structures
|
||||
foreach ($sqlTables as $tableName => $sqlColumns) {
|
||||
if (!isset($dbTables[$tableName])) {
|
||||
$dbComparison[] = [
|
||||
'name' => "Table `$tableName` missing in database",
|
||||
'status' => 'Missing Table',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare columns
|
||||
$dbColumns = $dbTables[$tableName];
|
||||
foreach ($sqlColumns as $colName => $colType) {
|
||||
if (!isset($dbColumns[$colName])) {
|
||||
$dbComparison[] = [
|
||||
'name' => "Column `$colName` missing in table `$tableName`",
|
||||
'status' => 'Missing Column',
|
||||
];
|
||||
} else {
|
||||
// Normalize data types for comparison
|
||||
$sqlColType = strtolower($colType);
|
||||
$dbColType = strtolower($dbColumns[$colName]);
|
||||
|
||||
// Remove attributes and constraints
|
||||
$sqlColType = preg_replace('/\s+.*$/', '', $sqlColType);
|
||||
$dbColType = preg_replace('/\s+.*$/', '', $dbColType);
|
||||
|
||||
// Remove additional attributes like unsigned, zerofill, etc.
|
||||
$sqlColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $sqlColType);
|
||||
$dbColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $dbColType);
|
||||
|
||||
if ($sqlColType != $dbColType) {
|
||||
$dbComparison[] = [
|
||||
'name' => "Data type mismatch for `$colName` in table `$tableName`",
|
||||
'status' => "Expected: $colType, Found: {$dbColumns[$colName]}",
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for extra columns in the database that are not in the SQL file
|
||||
foreach ($dbColumns as $colName => $colType) {
|
||||
if (!isset($sqlColumns[$colName])) {
|
||||
$dbComparison[] = [
|
||||
'name' => "Extra column `$colName` in table `$tableName` not present in db.sql",
|
||||
'status' => 'Extra Column',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for tables in the database not present in the db.sql file
|
||||
foreach ($dbTables as $tableName => $dbColumns) {
|
||||
if (!isset($sqlTables[$tableName])) {
|
||||
$dbComparison[] = [
|
||||
'name' => "Extra table `$tableName` in database not present in db.sql",
|
||||
'status' => 'Extra Table',
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$dbComparison[] = [
|
||||
'name' => 'db.sql file not found',
|
||||
'status' => 'File Missing',
|
||||
];
|
||||
}
|
||||
|
||||
$mysqli->close();
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-bug mr-2"></i>Debug</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<h2>Debugging</h2>
|
||||
<ul>
|
||||
<li>If you are experiencing a problem with ITFlow, this page should help you identify any configuration issues.</li>
|
||||
<li>Note: You might also need to gather <a href="https://docs.itflow.org/gathering_logs#error_logs">error logs</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mb-3">
|
||||
<tr>
|
||||
<th>ITFlow release version</th>
|
||||
<th><?php echo APP_VERSION; ?></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current DB Version</td>
|
||||
<td><?php echo CURRENT_DATABASE_VERSION; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Code Commit</td>
|
||||
<td><?php echo $commitHash; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Branch</td>
|
||||
<td><?php echo $gitBranch; ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- System Information Table -->
|
||||
<h3>System Information</h3>
|
||||
<table class="table table-sm table-bordered">
|
||||
<tbody>
|
||||
<?php foreach ($systemInfo as $info): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($info['name']); ?></td>
|
||||
<td><?= htmlspecialchars($info['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- PHP Extensions and Configuration Table -->
|
||||
<h3 class="mt-3">PHP Extensions and Configuration</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<!-- PHP Extensions Section -->
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">PHP Extensions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($phpExtensions as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<!-- PHP Configuration Section -->
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">PHP Configuration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($phpConfig as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">Shell Commands</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($shellCommands as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">SSL Checks</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($sslChecks as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">Domain Checks</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($domainChecks as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
<!-- File Permissions Table -->
|
||||
<thead>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="3">File Permissions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($filePermissions as $check): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($check['name']); ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($check['passed']): ?>
|
||||
<i class="fas fa-check" style="color:green"></i>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-times" style="color:red"></i>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($check['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Database Structure Comparison Table -->
|
||||
<h3 class="mt-3">Database Structure Comparison</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<tbody>
|
||||
<?php if (!empty($dbComparison)): ?>
|
||||
<?php foreach ($dbComparison as $issue): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($issue['name']); ?></td>
|
||||
<td colspan="2"><?= htmlspecialchars($issue['status']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="3">No discrepancies found between the database and db.sql file.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Uploads Directory Stats Table -->
|
||||
<h3 class="mt-3">Uploads Directory Stats</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<tbody>
|
||||
<?php foreach ($uploadsStats as $stat): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($stat['name']); ?></td>
|
||||
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Database Stats Table -->
|
||||
<h3 class="mt-3">Database Stats</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<tbody>
|
||||
<?php foreach ($databaseStats as $stat): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($stat['name']); ?></td>
|
||||
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Table Stats Table -->
|
||||
<h3 class="mt-3">Table Stats</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table Name</th>
|
||||
<th>Fields / Rows</th>
|
||||
<th>Size (MB)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($tableDetails as $table): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($table['name']); ?></td>
|
||||
<td><?= htmlspecialchars("Fields: {$table['fields']}, Rows: {$table['rows']}"); ?></td>
|
||||
<td><?= htmlspecialchars($table['size'] . ' MB'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sort by Filter
|
||||
$sort = "document_template_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM document_templates
|
||||
LEFT JOIN users ON document_template_created_by = user_id
|
||||
WHERE user_name LIKE '%$q%' OR document_template_name LIKE '%$q%'
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file mr-2"></i>Document Templates</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/document_template/document_template_add.php" data-modal-size="xl">
|
||||
<i class="fas fa-plus mr-2"></i>New Template
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form autocomplete="off">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control " name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search templates">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=document_template_name&order=<?php echo $disp; ?>">
|
||||
Template Name <?php if ($sort == 'document_template_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=document_template_created_at&order=<?php echo $disp; ?>">
|
||||
Created <?php if ($sort == 'document_template_created_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=document_template_updated_at&order=<?php echo $disp; ?>">
|
||||
Updated <?php if ($sort == 'document_template_updated_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$document_template_id = intval($row['document_template_id']);
|
||||
$document_template_name = nullable_htmlentities($row['document_template_name']);
|
||||
$document_template_description = nullable_htmlentities($row['document_template_description']);
|
||||
$document_template_content = nullable_htmlentities($row['document_template_content']);
|
||||
$document_template_created_by_name = nullable_htmlentities($row['user_name']);
|
||||
$document_template_created_at = nullable_htmlentities($row['document_template_created_at']);
|
||||
$document_template_updated_at = nullable_htmlentities($row['document_template_updated_at']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-bold" href="document_template_details.php?document_template_id=<?php echo $document_template_id; ?>"><i class="fas fa-fw fa-file-alt text-dark"></i> <?php echo $document_template_name; ?></a>
|
||||
<div class="mt-1 text-secondary"><?php echo $document_template_description; ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo $document_template_created_at; ?>
|
||||
<div class="text-secondary"><?php echo $document_template_created_by_name; ?></div>
|
||||
</td>
|
||||
<td><?php echo $document_template_updated_at; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-size="xl"
|
||||
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold" href="post.php?delete_document_template=<?php echo $document_template_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php"; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
|
||||
//Initialize the HTML Purifier to prevent XSS
|
||||
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
|
||||
|
||||
$purifier_config = HTMLPurifier_Config::createDefault();
|
||||
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
|
||||
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
|
||||
$purifier = new HTMLPurifier($purifier_config);
|
||||
|
||||
if (isset($_GET['document_template_id'])) {
|
||||
$document_template_id = intval($_GET['document_template_id']);
|
||||
}
|
||||
|
||||
$sql_document = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
|
||||
|
||||
if (mysqli_num_rows($sql_document) == 0) {
|
||||
echo "<center><h1 class='text-secondary mt-5'>Nothing to see here</h1><a class='btn btn-lg btn-secondary mt-3' href='javascript:history.back()'><i class='fa fa-fw fa-arrow-left'></i> Go Back</a></center>";
|
||||
require_once "../includes/footer.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
$row = mysqli_fetch_array($sql_document);
|
||||
|
||||
$document_template_name = nullable_htmlentities($row['document_template_name']);
|
||||
$document_template_description = nullable_htmlentities($row['document_template_description']);
|
||||
$document_template_content = $purifier->purify($row['document_template_content']);
|
||||
$document_template_created_at = nullable_htmlentities($row['document_template_created_at']);
|
||||
$document_template_updated_at = nullable_htmlentities($row['document_template_updated_at']);
|
||||
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="../">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="users.php">Admin</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="document_template.php">Document Templates</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active"><i class="fas fa-file mr-2"></i><?php echo $document_template_name; ?></li>
|
||||
</ol>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file mr-2"></i><?php echo $document_template_name; ?></h3>
|
||||
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal"
|
||||
data-modal-size="xl"
|
||||
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
|
||||
<i class="fas fa-edit mr-2"></i>Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body prettyContent">
|
||||
<?php echo $document_template_content; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/pretty_content.js"></script>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
require_once "includes/inc_all_admin.php";
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-fingerprint mr-2"></i>Identity Providers</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<h4>Client Portal SSO via Microsoft Entra</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Identity Provider <small class='text-secondary'>(Currently only works with Microsoft Entra ID/AAD)</small></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" readonly>
|
||||
<option <?php if (empty($config_azure_client_id)) { echo "selected"; } ?>>Disabled</option>
|
||||
<option <?php if ($config_azure_client_id) { echo "selected"; } ?>>Microsoft Entra</option>
|
||||
<option>Google (WIP)</option>
|
||||
<option>Custom SSO (WIP)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MS Entra OAuth App (Client) ID</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="azure_client_id" placeholder="e721e3b6-01d6-50e8-7f22-c84d951a52e7" value="<?php echo nullable_htmlentities($config_azure_client_id); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MS Entra OAuth Secret</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" name="azure_client_secret" placeholder="Auto-generated from App Registration" value="<?php echo nullable_htmlentities($config_azure_client_secret); ?>" autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<button type="submit" name="edit_identity_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/config.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/functions.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/check_login.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/page_title.php';
|
||||
if (!isset($session_is_admin) || !$session_is_admin) {
|
||||
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
|
||||
}
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/top_nav.php';
|
||||
require_once 'includes/side_nav.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_wrapper.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_alert_feedback.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/filter_header.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/app_version.php';
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
<!-- Main Sidebar Container -->
|
||||
<aside class="main-sidebar sidebar-dark-<?php echo nullable_htmlentities($config_theme); ?> d-print-none">
|
||||
<a class="brand-link pb-1 mt-1" href="/agent/<?php echo $config_start_page ?>">
|
||||
<p class="h6">
|
||||
<i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i>
|
||||
<span class="brand-text">
|
||||
Back | <strong>Administration</strong>
|
||||
</span>
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<!-- Sidebar Menu -->
|
||||
<nav>
|
||||
<ul class="nav nav-pills nav-sidebar flex-column mt-2" data-widget="treeview" data-accordion="false">
|
||||
<li class="nav-header">ACCESS</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/users.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "users.php") {echo "active";} ?>">
|
||||
<i class="nav-icon fas fa-users"></i>
|
||||
<p>Users</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/roles.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "roles.php") {echo "active";} ?>">
|
||||
<i class="nav-icon fas fa-user-shield"></i>
|
||||
<p>Roles</p>
|
||||
</a>
|
||||
</li>
|
||||
<!-- 2025-12-05 JQ - Hide Permission Modules currently just shows modules
|
||||
<li class="nav-item">
|
||||
<a href="/admin/modules.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "modules.php") {echo "active";} ?>">
|
||||
<i class="nav-icon fas fa-puzzle-piece"></i>
|
||||
<p>Modules</p>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item">
|
||||
<a href="/admin/api_keys.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "api_keys.php") {echo "active";} ?>">
|
||||
<i class="nav-icon fas fa-key"></i>
|
||||
<p>API Keys</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-header">TAGS & CATEGORIES</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="/admin/tag.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tag.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-tags"></i>
|
||||
<p>Tags</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/category.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'category.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-list-ul"></i>
|
||||
<p>Categories</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($config_module_enable_accounting) { ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/tax.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tax.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-balance-scale"></i>
|
||||
<p>Taxes</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/payment_method.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'payment_method.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-hand-holding-usd"></i>
|
||||
<p>Payment Methods</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/payment_provider.php"
|
||||
class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['payment_provider.php', 'saved_payment_method.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon far fa-credit-card"></i>
|
||||
<p>Payment Providers</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/ai_provider.php"
|
||||
class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['ai_provider.php', 'ai_model.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-robot"></i>
|
||||
<p>AI Providers</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php if ($config_module_enable_ticketing) { ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/ticket_status.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ticket_status.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-info-circle"></i>
|
||||
<p>Ticket Statuses</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/custom_link.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'custom_link.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-external-link-alt"></i>
|
||||
<p>Custom Links</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php if ($config_module_enable_itdoc) { ?>
|
||||
<li class="nav-header">TEMPLATES</li>
|
||||
|
||||
<!-- 2025-11-16 JQ - Hide Contracts not yet ready
|
||||
<li class="nav-item">
|
||||
<a href="/admin/contract_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'contract_template.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-file-contract"></i>
|
||||
<p>
|
||||
<span href="#" class="fas fa-plus-circle right ajax-modal" data-modal-url="/admin/modals/contract_template/contract_template_add.php" data-modal-size="lg"></span>
|
||||
Contract Templates
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item">
|
||||
<a href="/admin/project_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['project_template.php', 'project_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-project-diagram"></i>
|
||||
<p>Project Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/ticket_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['ticket_template.php', 'ticket_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-life-ring"></i>
|
||||
<p>Ticket Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/vendor_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'vendor_template.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-building"></i>
|
||||
<p>Vendor Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/software_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'software_template.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-rocket"></i>
|
||||
<p>License Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/document_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['document_template.php', 'document_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-file"></i>
|
||||
<p>Document Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
<li class="nav-header">MAINTENANCE</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="/admin/mail_queue.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'mail_queue.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-mail-bulk"></i>
|
||||
<p>Mail Queue</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/audit_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'audit_log.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-history"></i>
|
||||
<p>Audit Logs</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/app_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'app_log.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-history"></i>
|
||||
<p>App Logs</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/backup.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'backup.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-cloud-upload-alt"></i>
|
||||
<p>Backup</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/debug.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'debug.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-bug"></i>
|
||||
<p>Debug</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/update.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'update.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-download"></i>
|
||||
<p>Update</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- SETTINGS Section -->
|
||||
<li class="nav-item has-treeview mt-2 <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_provider.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : ''); ?>">
|
||||
<a href="#" class="nav-link">
|
||||
<p>
|
||||
SETTINGS
|
||||
<i class="right fas fa-angle-left"></i>
|
||||
</p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_company.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_company.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fa fa-briefcase"></i>
|
||||
<p>Company Details</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_localization.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_localization.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fa fa-globe"></i>
|
||||
<p>Localization</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_theme.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_theme.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fa fa-paint-brush"></i>
|
||||
<p>Theme</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_security.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_security.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-shield-alt"></i>
|
||||
<p>Security</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_mail.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_mail.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon far fa-envelope"></i>
|
||||
<p>Mail</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_notification.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_notification.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon far fa-bell"></i>
|
||||
<p>Notifications</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_default.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_default.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-cogs"></i>
|
||||
<p>Defaults</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php if ($config_module_enable_accounting) { ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_invoice.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_invoice.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-file-invoice"></i>
|
||||
<p>Invoice</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_quote.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_quote.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-comment-dollar"></i>
|
||||
<p>Quote</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php if ($config_module_enable_ticketing) { ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_project.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_project.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-project-diagram"></i>
|
||||
<p>Project</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_ticket.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_ticket.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-life-ring"></i>
|
||||
<p>Ticket</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<!-- Currently the only integration is the client portal SSO -->
|
||||
<?php if ($config_client_portal_enable) { ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/identity_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'identity_provider.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-fingerprint"></i>
|
||||
<p>Identity Provider</p>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_telemetry.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_telemetry.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-satellite-dish"></i>
|
||||
<p>Telemetry</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/settings_module.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_module.php' ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-cube"></i>
|
||||
<p>Modules</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<?php
|
||||
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links
|
||||
WHERE custom_link_location = 4 AND custom_link_archived_at IS NULL
|
||||
ORDER BY custom_link_order ASC, custom_link_name ASC"
|
||||
);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_custom_links)) {
|
||||
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
|
||||
$custom_link_uri = sanitize_url($row['custom_link_uri']);
|
||||
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
|
||||
$custom_link_new_tab = intval($row['custom_link_new_tab']);
|
||||
if ($custom_link_new_tab == 1) {
|
||||
$target = "target='_blank' rel='noopener noreferrer'";
|
||||
} else {
|
||||
$target = "";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="<?php echo $custom_link_uri; ?>" <?php echo $target; ?> class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == basename($custom_link_uri)) { echo "active"; } ?>">
|
||||
<i class="fas fa-<?php echo $custom_link_icon; ?> nav-icon"></i>
|
||||
<p><?php echo $custom_link_name; ?></p>
|
||||
<i class="fas fa-angle-right nav-icon float-right"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
<!-- /.sidebar-menu -->
|
||||
<div class="mb-3"></div>
|
||||
</div>
|
||||
<!-- /.sidebar -->
|
||||
</aside>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
header('Location: users.php');
|
||||
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "email_id";
|
||||
$order = "DESC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM email_queue
|
||||
WHERE (email_id LIKE '%$q%' OR email_from LIKE '%$q%' OR email_from_name LIKE '%$q%' OR email_recipient LIKE '%$q%' OR email_recipient_name LIKE '%$q%' OR email_subject LIKE '%$q%')
|
||||
AND DATE(email_queued_at) BETWEEN '$dtf' AND '$dtt'
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-3">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-mail-bulk mr-2"></i>Email Queue</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="mb-4" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search mail queue">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="dropdown float-right" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item"
|
||||
type="submit" form="bulkActions" name="bulk_cancel_emails">
|
||||
<i class="fas fa-fw fa-ban mr-2"></i>Cancel
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_emails">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label>Date range</label>
|
||||
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
|
||||
<input type="hidden" name="canned_date" id="canned_date" value="<?php echo nullable_htmlentities($_GET['canned_date']) ?? ''; ?>">
|
||||
<input type="hidden" name="dtf" id="dtf" value="<?php echo nullable_htmlentities($dtf ?? ''); ?>">
|
||||
<input type="hidden" name="dtt" id="dtt" value="<?php echo nullable_htmlentities($dtt ?? ''); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-sm table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_queued_at&order=<?php echo $disp; ?>">
|
||||
Queued <?php if ($sort == 'email_queued_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_from&order=<?php echo $disp; ?>">
|
||||
From <?php if ($sort == 'email_from') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_recipient&order=<?php echo $disp; ?>">
|
||||
To <?php if ($sort == 'email_recipient') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_subject&order=<?php echo $disp; ?>">
|
||||
Subject <?php if ($sort == 'email_subject') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_status&order=<?php echo $disp; ?>">
|
||||
Status <?php if ($sort == 'email_status') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_attempts&order=<?php echo $disp; ?>">
|
||||
Attempts <?php if ($sort == 'email_attempts') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$email_id = intval($row['email_id']);
|
||||
$email_from = nullable_htmlentities($row['email_from']);
|
||||
$email_from_name = nullable_htmlentities($row['email_from_name']);
|
||||
$email_recipient = nullable_htmlentities($row['email_recipient']);
|
||||
$email_recipient_name = nullable_htmlentities($row['email_recipient_name']);
|
||||
$email_subject = nullable_htmlentities($row['email_subject']);
|
||||
$email_attempts = intval($row['email_attempts']);
|
||||
$email_queued_at = nullable_htmlentities($row['email_queued_at']);
|
||||
$email_failed_at = nullable_htmlentities($row['email_failed_at']);
|
||||
$email_sent_at = nullable_htmlentities($row['email_sent_at']);
|
||||
$email_status = intval($row['email_status']);
|
||||
if ($email_status == 0) {
|
||||
$email_status_display = "<div class='text-primary'>Queued</div>";
|
||||
} elseif($email_status == 1) {
|
||||
$email_status_display = "<div class='text-warning'>Sending</div>";
|
||||
} elseif($email_status == 2) {
|
||||
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary'>$email_failed_at</small>";
|
||||
} else {
|
||||
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary'>$email_sent_at</small>";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td class="pr-0 bg-light">
|
||||
<?php if ($email_status !== 3) { ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="email_ids[]" value="<?php echo $email_id ?>">
|
||||
</div>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td><?php echo $email_queued_at; ?></td>
|
||||
<td><?php echo "$email_from<br><small class='text-secondary'>$email_from_name</small>"?></td>
|
||||
<td><?php echo "$email_recipient<br><small class='text-secondary'>$email_recipient_name</small>"?></td>
|
||||
<td><?php echo $email_subject; ?></td>
|
||||
<td><?php echo $email_status_display; ?></td>
|
||||
<td><?php echo $email_attempts; ?></td>
|
||||
<td class="text-center">
|
||||
<a class="btn btn-sm btn-secondary ajax-modal" href="#"
|
||||
data-modal-size="lg"
|
||||
data-modal-url="modals/mail_queue/mail_queue_message_view.php?id=<?= $email_id ?>">
|
||||
<i class="fas fa-fw fa-eye"></i>
|
||||
</a>
|
||||
|
||||
<!-- Show force resend if all retries have failed -->
|
||||
<?php if ($email_status == 2 && $email_attempts > 3) { ?>
|
||||
<a class="btn btn-sm btn-success" href="post.php?send_failed_mail=<?php echo $email_id; ?>"><i class="fas fa-fw fa-paper-plane"></i></a>
|
||||
<?php } ?>
|
||||
|
||||
<!-- Allow cancelling a message if it hasn't yet been picked up (e.g. stuck/bugged) -->
|
||||
<?php if ($email_status !== 3) { ?>
|
||||
<a class="btn btn-sm btn-danger confirm-link" href="post.php?cancel_mail=<?php echo $email_id; ?>"><i class="fas fa-fw fa-trash"></i></a>
|
||||
<?php } ?>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php require_once "../includes/filter_footer.php"; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/bulk_actions.js"></script>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Add AI Model</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Provider <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="provider" required>
|
||||
<option value="">- Select an AI Provider -</option>
|
||||
<?php
|
||||
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
|
||||
while ($row = mysqli_fetch_array($sql_ai_providers)) {
|
||||
$ai_provider_id = intval($row['ai_provider_id']);
|
||||
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Model Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="model" placeholder="ex gpt-4">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Use Case <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="use_case">
|
||||
<option>General</option>
|
||||
<option>Tickets</option>
|
||||
<option>Documentation</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$model_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models WHERE ai_model_id = $model_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$ai_model_ai_provider_id = intval($row['ai_model_ai_provider_id']);
|
||||
$model_id = intval($row['ai_model_id']);
|
||||
$model_name = nullable_htmlentities($row['ai_model_name']);
|
||||
$use_case = nullable_htmlentities($row['ai_model_use_case']);
|
||||
$prompt = nullable_htmlentities($row['ai_model_prompt']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $model_name; ?></strong></h5>
|
||||
<button type="button" class="close text-light" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="model_id" value="<?php echo $model_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Provider <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="provider" required>
|
||||
<option value="">- Select an AI Provider -</option>
|
||||
<?php
|
||||
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
|
||||
while ($row = mysqli_fetch_array($sql_ai_providers)) {
|
||||
$ai_provider_id = intval($row['ai_provider_id']);
|
||||
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
|
||||
|
||||
?>
|
||||
<option <?php if ($ai_provider_id = $ai_model_ai_provider_id) { echo "selected"; } ?> value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Model Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="model" value="<?php echo $model_name; ?>" placeholder="ex gpt-4">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Use Case <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="use_case">
|
||||
<option <?php if ($use_case == 'General') { echo "selected"; } ?>>General</option>
|
||||
<option <?php if ($use_case == 'Tickets') { echo "selected"; } ?>>Tickets</option>
|
||||
<option <?php if ($use_case == 'Documentation') { echo "selected"; } ?>>Documentation</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"><?php echo $prompt; ?></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>New AI Provider</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Provider Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="provider" placeholder="ex OpenAI">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>URL <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
</div>
|
||||
<input type="url" class="form-control" name="url" placeholder="ex https://ai.company.ext/api">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>API Key</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="api_key" placeholder="Enter API key here">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$provider_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers WHERE ai_provider_id = $provider_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$provider_name = nullable_htmlentities($row['ai_provider_name']);
|
||||
$url = nullable_htmlentities($row['ai_provider_api_url']);
|
||||
$key = nullable_htmlentities($row['ai_provider_api_key']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $provider_name; ?></strong></h5>
|
||||
<button type="button" class="close text-light" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="provider_id" value="<?php echo $provider_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Provider Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="provider" value="<?php echo $provider_name; ?>" placeholder="ex OpenAI">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>URL <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
</div>
|
||||
<input type="url" class="form-control" name="url" value="<?php echo $url; ?>" placeholder="ex https://ai.company.ext/api">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>API Key</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="api_key" value="<?php echo $key; ?>" placeholder="Enter API key here">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$key = randomString(156);
|
||||
$decryptPW = randomString(160);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>New Key</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-api-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-api-keys">Keys</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-api-details">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="key" value="<?php echo $key ?>">
|
||||
<input type="hidden" name="password" value="<?php echo $decryptPW ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sticky-note"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Key Name" maxlength="255" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Expiration Date <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="date" class="form-control" name="expire" min="<?php echo date('Y-m-d')?>" max="2999-12-31" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Client Access <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="client" required>
|
||||
<option value="0"> ALL CLIENTS </option>
|
||||
<?php
|
||||
$sql = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']); ?>
|
||||
<option value="<?php echo $client_id; ?>"><?php echo "$client_name (Client ID: $client_id)"; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-api-keys">
|
||||
<div class="form-group">
|
||||
<label>API Key <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" value="<?php echo $key ?>" required disabled>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?php echo $key; ?>"><i class="fa fa-fw fa-copy"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Login credential decryption password <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-unlock-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" value="<?php echo $decryptPW ?>" required disabled>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?php echo $decryptPW; ?>"><i class="fa fa-fw fa-copy"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="form-group">
|
||||
<label>I have made a copy of the key(s)<strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<input type="checkbox" name="ack" value="1" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_api_key" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$category = nullable_htmlentities($_GET['category'] ?? '');
|
||||
|
||||
$category_types_array = ['Expense', 'Income', 'Referral', 'Ticket'];
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>New <strong><?= nullable_htmlentities($category) ?></strong> Category</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<?php if ($category) { ?>
|
||||
<input type="hidden" name="type" value="<?= $category ?>">
|
||||
<?php } else { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Select Type -</option>
|
||||
<?php foreach ($category_types_array as $type_select) { ?>
|
||||
<option><?= $type_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Category name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Category</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$category_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_id = $category_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
$category_color = nullable_htmlentities($row['category_color']);
|
||||
$category_type = nullable_htmlentities($row['category_type']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>Editing category: <strong><?php echo $category_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="category_id" value="<?php echo $category_id; ?>">
|
||||
<input type="hidden" name="type" value="<?php echo $category_type; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $category_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" value="<?php echo $category_color; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
|
||||
$contract_types_array = ['Fully Managed', 'Partialy Managed', 'Break/Fix'];
|
||||
$renewal_frequency_array = ['Manual', 'Annually', '2 Year', '3 Year', '5 Year', '7 Year'];
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract mr-2"></i>New Contract Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
|
||||
<!-- Tabs Navigation -->
|
||||
<ul class="modal-header nav nav-pills nav-justified">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="general-tab" data-toggle="tab" href="#general" role="tab">General Info</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="sla-tab" data-toggle="tab" href="#sla" role="tab">SLA</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="rates-tab" data-toggle="tab" href="#rates" role="tab">Rates & Support</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="details-tab" data-toggle="tab" href="#details" role="tab">Details</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
<div class="tab-content" id="contractTemplateTabContent">
|
||||
|
||||
<!-- General Info Tab -->
|
||||
<div class="tab-pane fade show active" id="general" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-file-contract"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Contract Template Name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Description <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description"
|
||||
placeholder="Contract Template Description" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Contract Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Select Type -</option>
|
||||
<?php foreach ($contract_types_array as $type) { ?>
|
||||
<option><?= $type ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Renewal Frequency</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sync-alt"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="renewal_frequency">
|
||||
<option value="">- Select Frequency -</option>
|
||||
<?php foreach ($renewal_frequency_array as $renewal_frequency) { ?>
|
||||
<option><?= $renewal_frequency ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SLA Tab -->
|
||||
<div class="tab-pane fade" id="sla" role="tabpanel">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>Low Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_low_response_time" placeholder="e.g., 24">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Low Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_low_resolution_time" placeholder="e.g., 48">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>Medium Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_medium_response_time" placeholder="e.g., 12">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Medium Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_medium_resolution_time" placeholder="e.g., 24">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>High Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-bolt"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_high_response_time" placeholder="e.g., 1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>High Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_high_resolution_time" placeholder="e.g., 4">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rates & Support Tab -->
|
||||
<div class="tab-pane fade" id="rates" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>Standard Hourly Rate</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-dollar-sign"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="rate_standard" placeholder="e.g., 100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>After Hours Hourly Rate</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-moon"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="rate_after_hours" placeholder="e.g., 150">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Hours</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="support_hours" placeholder="e.g., Mon-Fri 9am-5pm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Net Terms</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-file-invoice-dollar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="net_terms" placeholder="e.g., Net 30">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details Tab -->
|
||||
<div class="tab-pane fade" id="details" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymce" rows="6" name="details" placeholder="Enter Contract Details"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_contract_template" class="btn btn-primary text-bold">
|
||||
<i class="fa fa-check mr-2"></i>Create Template
|
||||
</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal">
|
||||
<i class="fa fa-times mr-2"></i>Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
?>
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
<?php
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$contract_template_id = intval($_GET['id']);
|
||||
|
||||
$contract_types_array = ['Fully Managed', 'Partialy Managed', 'Break/Fix'];
|
||||
$update_frequency_array = ['Manual', 'Annually', '2 Year', '3 Year', '5 Year', '7 Year'];
|
||||
|
||||
// Fetch existing template
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM contract_templates WHERE contract_template_id = $contract_template_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
|
||||
// Assign locals
|
||||
$name = nullable_htmlentities($row['contract_template_name']);
|
||||
$description = nullable_htmlentities($row['contract_template_description']);
|
||||
$type = nullable_htmlentities($row['contract_template_type']);
|
||||
$renewal_frequency = nullable_htmlentities($row['contract_template_renewal_frequency']);
|
||||
$sla_low_resp = intval($row['contract_template_sla_low_response_time']);
|
||||
$sla_med_resp = intval($row['contract_template_sla_medium_response_time']);
|
||||
$sla_high_resp = intval($row['contract_template_sla_high_response_time']);
|
||||
$sla_low_res = intval($row['contract_template_sla_low_resolution_time']);
|
||||
$sla_med_res = intval($row['contract_template_sla_medium_resolution_time']);
|
||||
$sla_high_res = intval($row['contract_template_sla_high_resolution_time']);
|
||||
$hourly_rate = intval($row['contract_template_rate_standard']);
|
||||
$after_hours = intval($row['contract_template_rate_after_hours']);
|
||||
$support_hours = nullable_htmlentities($row['contract_template_support_hours']);
|
||||
$net_terms = intval($row['contract_template_net_terms']);
|
||||
$details = nullable_htmlentities($row['contract_template_details']);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract mr-2"></i>Edit Contract Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
|
||||
<!-- Tabs Navigation -->
|
||||
<ul class="modal-header nav nav-pills nav-justified">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="general-tab" data-toggle="tab" href="#general" role="tab">General Info</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="sla-tab" data-toggle="tab" href="#sla" role="tab">SLA</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="rates-tab" data-toggle="tab" href="#rates" role="tab">Rates & Support</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="details-tab" data-toggle="tab" href="#details" role="tab">Details</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="contract_template_id" value="<?php echo $contract_template_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="tab-content" id="contractTemplateTabContent">
|
||||
|
||||
<!-- General Info Tab -->
|
||||
<div class="tab-pane fade show active" id="general" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-file-contract"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name"
|
||||
placeholder="Contract Template Name" maxlength="200" required autofocus
|
||||
value="<?= $name ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Description <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description"
|
||||
placeholder="Contract Template Description" maxlength="200" required
|
||||
value="<?= $description ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Contract Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Select Type -</option>
|
||||
<?php foreach ($contract_types_array as $type_select) { ?>
|
||||
<option <?php if ($type == $type_select) { echo "selected"; } ?>><?= $type_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Renewal Frequency</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sync-alt"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="renewal_frequency">
|
||||
<option value="">- Select Frequency -</option>
|
||||
<?php foreach ($renewal_frequency_array as $renewal_frequency_select) { ?>
|
||||
<option <?php if ($renewal_frequency == $renewal_frequency_select) { echo "selected"; } ?>><?= $renewal_frequency_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SLA Tab -->
|
||||
<div class="tab-pane fade" id="sla" role="tabpanel">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>Low Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_low_response_time" placeholder="e.g., 24"
|
||||
value="<?= $sla_low_resp ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Low Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_low_resolution_time" placeholder="e.g., 48"
|
||||
value="<?= $sla_low_res ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>Medium Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_medium_response_time" placeholder="e.g., 12"
|
||||
value="<?= $sla_med_resp ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Medium Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_medium_resolution_time" placeholder="e.g., 24"
|
||||
value="<?= $sla_med_res ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>High Priority Response (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-bolt"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_high_response_time" placeholder="e.g., 1"
|
||||
value="<?= $sla_high_resp ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>High Priority Resolution (hrs)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="sla_high_resolution_time" placeholder="e.g., 4"
|
||||
value="<?= $sla_high_res ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rates & Support Tab -->
|
||||
<div class="tab-pane fade" id="rates" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>Standard Hourly Rate</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-dollar-sign"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="rate_standard" placeholder="e.g., 100"
|
||||
value="<?= $rate_standard ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>After Hours Hourly Rate</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-moon"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="rate_after_hours" placeholder="e.g., 150"
|
||||
value="<?= $rate_after_hours ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Hours</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="support_hours" placeholder="e.g., Mon-Fri 9am-5pm"
|
||||
value="<?= $support_hours ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Net Terms</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-file-invoice-dollar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="net_terms" placeholder="e.g., Net 30"
|
||||
value="<?= $net_terms ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details Tab -->
|
||||
<div class="tab-pane fade" id="details" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>Contract Details</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
</div>
|
||||
<textarea class="form-control tinymce" rows="6" name="details"
|
||||
placeholder="Enter Contract Details"><?= $details ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_contract_template" class="btn btn-primary text-bold">
|
||||
<i class="fa fa-check mr-2"></i>Save Changes
|
||||
</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal">
|
||||
<i class="fa fa-times mr-2"></i>Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
?>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<div class="modal" id="createCustomFieldModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Create <?php echo nullable_htmlentities($table); ?> field</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="table" value="<?php echo nullable_htmlentities($table); ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Label <strong class="text-danger">*</strong></label>
|
||||
<input type="text" class="form-control" name="label" placeholder="Enter a custom field label" maxlength="255" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Select a field type -</option>
|
||||
<option>Text</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="create_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<div class="modal" id="editCustomFieldModal<?php echo $custom_field_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Editing custom field: <strong><?php echo $custom_field_label; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="custom_field_id" value="<?php echo $custom_field_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Label <strong class="text-danger">*</strong></label>
|
||||
<input type="text" class="form-control" name="label" maxlength="255" value="<?php echo $custom_field_label; ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Select a field type -</option>
|
||||
<option <?php if ($custom_field_type == 'text') { echo "selected"; } ?> value="text">Text</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>New Custom Link</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Link name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Order</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="new_tab" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Icon</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="location" required>
|
||||
<option value="1">Main Side Nav</option>
|
||||
<option value="2">Top Nav (Icon Required)</option>
|
||||
<option value="3">Client Portal Nav</option>
|
||||
<option value="4">Admin Nav</option>
|
||||
<option value="5">Reports Nav</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$custom_link_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM custom_links WHERE custom_link_id = $custom_link_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
|
||||
$custom_link_uri = nullable_htmlentities($row['custom_link_uri']);
|
||||
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
|
||||
$custom_link_new_tab = intval($row['custom_link_new_tab']);
|
||||
$custom_link_order = intval($row['custom_link_order']);
|
||||
$custom_link_location = intval($row['custom_link_location']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Editing link: <strong><?php echo $custom_link_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="custom_link_id" value="<?php echo $custom_link_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" value="<?php echo $custom_link_name; ?>" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Order</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?php echo $custom_link_order; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" value="<?php echo $custom_link_uri; ?>" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="new_tab" value="1" <?php if ($custom_link_new_tab == 1) { echo "checked"; } ?>>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Icon</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200" value="<?php echo $custom_link_icon; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="location" required>
|
||||
<option value="1" <?php if ($custom_link_location === 1) { echo "selected"; } ?> >Main Side Nav</option>
|
||||
<option value="2" <?php if ($custom_link_location === 2) { echo "selected"; } ?> >Top Nav (Icon Required)</option>
|
||||
<option value="3" <?php if ($custom_link_location === 3) { echo "selected"; } ?> >Client Portal Nav</option>
|
||||
<option value="4" <?php if ($custom_link_location === 4) { echo "selected"; } ?> >Admin Nav</option>
|
||||
<option value="5" <?php if ($custom_link_location === 5) { echo "selected"; } ?> >Reports Nav</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Creating Document Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Enter a prompt for the type of IT documentation you want to generate:</label>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="aiPrompt" placeholder="e.g. 'A network troubleshooting guide for junior technicians'">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-info" type="button" id="generateAIContent">
|
||||
<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TinyMCE Content -->
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymce" name="content"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="description" placeholder="Enter a short summary">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
||||
<button type="submit" name="add_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
|
||||
$('#generateAIContent').on('click', function(){
|
||||
var prompt = $('#aiPrompt').val().trim();
|
||||
if(prompt === '') {
|
||||
alert('Please enter a prompt.');
|
||||
return;
|
||||
}
|
||||
|
||||
$('#generateAIContent').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Generating...');
|
||||
|
||||
$.ajax({
|
||||
url: '/agent/ajax.php?ai_create_document_template', // The PHP script that calls the OpenAI API
|
||||
method: 'POST',
|
||||
data: { prompt: prompt },
|
||||
dataType: 'html',
|
||||
success: function(response) {
|
||||
// Assuming you have exactly one TinyMCE instance on the page
|
||||
// and it's targeting the .tinymce textarea:
|
||||
tinymce.activeEditor.setContent(response);
|
||||
},
|
||||
error: function() {
|
||||
alert('Error generating content. Please try again.');
|
||||
},
|
||||
complete: function() {
|
||||
$('#generateAIContent').prop('disabled', false).html('<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$document_template_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$document_template_name = nullable_htmlentities($row['document_template_name']);
|
||||
$document_template_description = nullable_htmlentities($row['document_template_description']);
|
||||
$document_template_content = nullable_htmlentities($row['document_template_content']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Editing template: <strong><?php echo $document_template_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="document_template_id" value="<?php echo $document_template_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $document_template_name; ?>" placeholder="Name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymce" name="content"><?php echo $document_template_content; ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="description" value="<?php echo $document_template_description; ?>" placeholder="Short summary">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
if (!isset($session_is_admin) || !$session_is_admin) {
|
||||
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
|
||||
}
|
||||
|
||||
$email_id = intval($_GET['id']);
|
||||
|
||||
//Initialize the HTML Purifier to prevent XSS
|
||||
require "../../../plugins/htmlpurifier/HTMLPurifier.standalone.php";
|
||||
|
||||
$purifier_config = HTMLPurifier_Config::createDefault();
|
||||
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
|
||||
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
|
||||
$purifier = new HTMLPurifier($purifier_config);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_id = $email_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
|
||||
$email_from = nullable_htmlentities($row['email_from']);
|
||||
$email_from_name = nullable_htmlentities($row['email_from_name']);
|
||||
$email_recipient = nullable_htmlentities($row['email_recipient']);
|
||||
$email_recipient_name = nullable_htmlentities($row['email_recipient_name']);
|
||||
$email_subject = nullable_htmlentities($row['email_subject']);
|
||||
$email_content = $purifier->purify($row['email_content']);
|
||||
$email_attempts = intval($row['email_attempts']);
|
||||
$email_queued_at = nullable_htmlentities($row['email_queued_at']);
|
||||
$email_failed_at = nullable_htmlentities($row['email_failed_at']);
|
||||
$email_sent_at = nullable_htmlentities($row['email_sent_at']);
|
||||
$email_status = intval($row['email_status']);
|
||||
if ($email_status == 0) {
|
||||
$email_status_display = "<div class='text-primary'>Queued</div>";
|
||||
} elseif($email_status == 1) {
|
||||
$email_status_display = "<div class='text-warning'>Sending</div>";
|
||||
} elseif($email_status == 2) {
|
||||
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary'>$email_failed_at</small>";
|
||||
} else {
|
||||
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary'>$email_sent_at</small>";
|
||||
}
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class='fas fa-fw fa-envelope-open mr-2'></i><strong><?php echo $email_subject; ?></strong></h5>
|
||||
<button type="button" class="close text-light" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-1">
|
||||
<span class="text-secondary">From:</span>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<?php echo "<strong>$email_from_name</strong> ($email_from)"; ?>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<div class="row">
|
||||
<div class="col-md-1">
|
||||
<span class="text-secondary">To:</span>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<?php echo "<strong>$email_recipient_name</strong> ($email_recipient)"; ?>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<div class="prettyContent">
|
||||
<?php echo $email_content; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../js/pretty_content.js"></script>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Creating: <strong>Payment Method</strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Payment method name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_payment_method" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$payment_method_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods WHERE payment_method_id = $payment_method_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$payment_method_id = intval($row['payment_method_id']);
|
||||
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
|
||||
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?php echo $payment_method_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="payment_method_id" value="<?= $payment_method_id ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" value="<?php echo $payment_method_name; ?>" placeholder="Payment method name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."><?php echo $payment_method_description; ?></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_payment_method" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Add Payment Provider</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="alert alert-info text-center">
|
||||
<h6>Before Adding a Payment Provider!</h6>
|
||||
We recommend you add an <strong>Account</strong> and <strong>Vendor</strong> based off the Provider name before continuing eg <strong>Stripe</strong>
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-expense">Expense</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Provider <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="provider">
|
||||
<option>Stripe</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Publishable key <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Secret key <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Income / Expense Account <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-piggy-bank"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="account" required>
|
||||
<option value="">- Select an Account -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$account_id = intval($row['account_id']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
?>
|
||||
<option <?php if ($account_name === 'Stripe') { echo "selected"; } ?> value="<?= $account_id ?>"><?= $account_name ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Threshold</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00">
|
||||
</div>
|
||||
<small class="form-text text-muted">Will not show as an option at Checkout if invoice amount is above this number, 0 disables the threshold check.</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-expense">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="enable_expense" checked value="1" id="enableExpenseSwitch">
|
||||
<label class="custom-control-label" for="enableExpenseSwitch">Enable Expense</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Payment Provider Vendor <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="expense_vendor" required>
|
||||
<option value="0">Expense Disabled</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE vendor_client_id = 0 AND vendor_archived_at IS NULL ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
?>
|
||||
<option <?php if ($vendor_name === 'Stripe') { echo "selected"; } ?> value="<?= $vendor_id ?>"><?= $vendor_name ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Expense Category <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="expense_category" required>
|
||||
<option value="">- Select a Category -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Expense' AND category_archived_at IS NULL ORDER BY category_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$category_id = intval($row['category_id']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($category_name === 'Processing Fee') { echo "selected"; } ?> value="<?= $category_id ?>"><?= $category_name ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary ajax-modal" type="button"
|
||||
data-modal-url="../admin/modals/category/category_add.php?category=Expense">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Percentage Fee to expense</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" placeholder="Enter Percentage">
|
||||
</div>
|
||||
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Flat Fee to expense</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" placeholder="0.030">
|
||||
</div>
|
||||
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Add</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$provider_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers WHERE payment_provider_id = $provider_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$provider_name = nullable_htmlentities($row['payment_provider_name']);
|
||||
$public_key = nullable_htmlentities($row['payment_provider_public_key']);
|
||||
$private_key = nullable_htmlentities($row['payment_provider_private_key']);
|
||||
$account_id = intval($row['payment_provider_account']);
|
||||
$threshold = floatval($row['payment_provider_threshold']);
|
||||
$vendor_id = intval($row['payment_provider_expense_vendor']);
|
||||
$category_id = intval($row['payment_provider_expense_category']);
|
||||
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
|
||||
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?= $provider_name ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="provider_id" value="<?= $provider_id ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-expense">Expense</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Publishable key <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)" value="<?= $public_key ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Secret key <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)" value="<?= $private_key ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Income / Expense Account <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-piggy-bank"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="account" required>
|
||||
<option value="">- Select an Account -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$account_id_select = intval($row['account_id']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
?>
|
||||
<option <?php if ($account_id === $account_id_select) { echo "selected"; } ?> value="<?= $account_id_select ?>"><?= $account_name ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Threshold</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00" value="<?php echo $threshold; ?>">
|
||||
</div>
|
||||
<small class="form-text text-muted">Will not show as an option at Checkout if above this number</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-expense">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Payment Provider Vendor <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="expense_vendor" required>
|
||||
<option value="0">Expense Disabled</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE vendor_client_id = 0 AND vendor_archived_at IS NULL ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$vendor_id_select = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
?>
|
||||
<option <?php if ($vendor_id === $vendor_id_select) { echo "selected"; } ?>
|
||||
value="<?= $vendor_id_select ?>"><?= $vendor_name ?>
|
||||
</option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Expense Category <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="expense_category" required>
|
||||
<option value="">- Select a Category -</option>
|
||||
<?php
|
||||
|
||||
$sql_category = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Expense' AND category_archived_at IS NULL ORDER BY category_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_category)) {
|
||||
$category_id_select = intval($row['category_id']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($category_id === $category_id_select) { echo "selected"; } ?> value="<?= $category_id_select ?>"><?= $category_name ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary ajax-modal" type="button"
|
||||
data-modal-url="../admin/modals/category/category_add.php?category=Expense">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Percentage Fee to expense</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" value="<?php echo $percent_fee; ?>" placeholder="Enter Percentage">
|
||||
</div>
|
||||
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Flat Fee to expense</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" value="<?php echo $flat_fee; ?>" placeholder="0.030">
|
||||
</div>
|
||||
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Creating Project Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Project Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$project_template_id = intval($_GET['project_template_id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_id = $project_template_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$project_template_name = nullable_htmlentities($row['project_template_name']);
|
||||
$project_template_description = nullable_htmlentities($row['project_template_description']);
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Editing Project Template: <strong><?php echo $project_template_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Project Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" value="<?php echo $project_template_name; ?>" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description" value="<?php echo $project_template_description; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$project_template_id = intval($_GET['project_template_id']);
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-life-ring mr-2"></i>Adding Ticket Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Ticket Template <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="ticket_template_id" required>
|
||||
<option value="">- Select a Ticket Template -</option>
|
||||
<?php
|
||||
|
||||
$sql_ticket_templates_select = mysqli_query($mysqli, "SELECT ticket_template_id, ticket_template_name FROM ticket_templates
|
||||
WHERE ticket_template_id NOT IN (
|
||||
SELECT ticket_template_id FROM project_template_ticket_templates
|
||||
WHERE project_template_id = $project_template_id
|
||||
)
|
||||
AND ticket_template_archived_at IS NULL
|
||||
ORDER BY ticket_template_name ASC"
|
||||
);
|
||||
while ($row = mysqli_fetch_array($sql_ticket_templates_select)) {
|
||||
$ticket_template_id_select = intval($row['ticket_template_id']);
|
||||
$ticket_template_name_select = nullable_htmlentities($row['ticket_template_name']);
|
||||
?>
|
||||
<option value="<?php echo $ticket_template_id_select; ?>"><?php echo $ticket_template_name_select; ?></option>
|
||||
<?php
|
||||
}
|
||||
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Order</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="order" value="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_ticket_template_to_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Add</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>New Role</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="modal-body">
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-chevron-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Admin Access <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tools"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role_is_admin" required>
|
||||
<option value="0">No - edit after creation to set permissions</option>
|
||||
<option value="1">Yes - this role should have full admin access</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$role_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_id = $role_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$role_name = nullable_htmlentities($row['role_name']);
|
||||
$role_description = nullable_htmlentities($row['role_description']);
|
||||
$role_admin = intval($row['role_is_admin']);
|
||||
|
||||
// Count number of users that have each role
|
||||
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
|
||||
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
|
||||
|
||||
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
|
||||
// Initialize an empty array to hold user names
|
||||
$user_names = [];
|
||||
|
||||
// Fetch each row and store the user_name in the array
|
||||
while($row = mysqli_fetch_assoc($sql_users)) {
|
||||
$user_names[] = nullable_htmlentities($row['user_name']);
|
||||
}
|
||||
|
||||
// Convert the array of user names to a comma-separated string
|
||||
$user_names_string = implode(",", $user_names) ;
|
||||
|
||||
if (empty($user_names_string)) {
|
||||
$user_names_string = "-";
|
||||
}
|
||||
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Editing role:
|
||||
<strong><?php echo $role_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="role_id" value="<?php echo $role_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-role-details<?php echo $role_id; ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-role-access<?php echo $role_id; ?>">Access</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-role-details<?php echo $role_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" value="<?php echo $role_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-chevron-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" value="<?php echo $role_description; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Admin Access <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tools"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role_is_admin" required>
|
||||
<option value="1" <?php if ($role_admin) { echo 'selected'; } ?> >Yes - this role should have full admin access</option>
|
||||
<option value="0" <?php if (!$role_admin) { echo 'selected'; } ?>>No - use permissions on the next tab</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-role-access<?php echo $role_id; ?>">
|
||||
|
||||
<?php if ($role_admin) { ?>
|
||||
<div class="alert alert-warning"><strong>Module permissions do not apply to Admins.</strong></div>
|
||||
<?php } ?>
|
||||
|
||||
<?php
|
||||
|
||||
// Enumerate modules
|
||||
$sql_modules = mysqli_query($mysqli, "SELECT * FROM modules");
|
||||
while ($row_modules = mysqli_fetch_array($sql_modules)) {
|
||||
$module_id = intval($row_modules['module_id']);
|
||||
$module_name = nullable_htmlentities($row_modules['module_name']);
|
||||
$module_name_display = ucfirst(str_replace("module_","",$module_name));
|
||||
$module_description = nullable_htmlentities($row_modules['module_description']);
|
||||
|
||||
// Get permission level for module
|
||||
$module_permission_row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT user_role_permission_level FROM user_role_permissions WHERE module_id = $module_id AND user_role_id = $role_id LIMIT 1"));
|
||||
$module_permission = 0;
|
||||
if ($module_permission_row) {
|
||||
$module_permission = $module_permission_row['user_role_permission_level'];
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="form-group">
|
||||
<label> <?php echo $module_name_display ?> <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<select class="form-control select2" name="<?php echo "$module_id##$module_name" ?>" required>
|
||||
<option value="0" <?php if ($module_permission == 0) { echo 'selected'; } ?> >None</option>
|
||||
<option value="1" <?php if ($module_permission == 1) { echo 'selected'; } ?> >Read</option>
|
||||
<option value="2" <?php if ($module_permission == 2) { echo 'selected'; } ?>>Modify (Read, Edit, Archive)</option>
|
||||
<option value="3" <?php if ($module_permission == 3) { echo 'selected'; } ?>>Full (Read, Edit, Archive, Delete)</option>
|
||||
</select>
|
||||
</div>
|
||||
<small class="form-text text-muted"><?php echo $module_description ?></small>
|
||||
|
||||
</div>
|
||||
|
||||
<?php } // End while ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>New License Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Version</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Short description">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Type -</option>
|
||||
<?php foreach($software_types_array as $software_type) { ?>
|
||||
<option><?php echo $software_type; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>License Type</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="license_type">
|
||||
<option value="">- Select a License Type -</option>
|
||||
<?php foreach($license_types_array as $license_type) { ?>
|
||||
<option><?php echo $license_type; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$software_template_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM software_templates WHERE software_template_id = $software_template_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$software_name = nullable_htmlentities($row['software_template_name']);
|
||||
$software_version = nullable_htmlentities($row['software_template_version']);
|
||||
$software_description = nullable_htmlentities($row['software_template_description']);
|
||||
$software_type = nullable_htmlentities($row['software_template_type']);
|
||||
$software_license_type = nullable_htmlentities($row['software_template_license_type']);
|
||||
$software_notes = nullable_htmlentities($row['software_template_notes']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>Editing template: <strong><?php echo $software_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="software_template_id" value="<?php echo $software_template_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" value="<?php echo $software_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Version</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200" value="<?php echo $software_version; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Short description" value="<?php echo $software_description; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<?php foreach($software_types_array as $software_type_select) { ?>
|
||||
<option <?php if($software_type == $software_type_select) { echo "selected"; } ?>><?php echo $software_type_select; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>License Type</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="license_type">
|
||||
<option value="">- Select a License Type -</option>
|
||||
<?php foreach($license_types_array as $license_type_select) { ?>
|
||||
<option <?php if($license_type_select == $software_license_type){ echo "selected"; } ?>><?php echo $license_type_select; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $software_notes; ?></textarea>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$type_display = '';
|
||||
|
||||
if (isset($_GET['type'])) {
|
||||
$type = intval($_GET['type']);
|
||||
|
||||
if ($type === 1) {
|
||||
$type_display = "Client";
|
||||
} elseif($type === 2) {
|
||||
$type_display = "Location";
|
||||
} elseif ($type === 3) {
|
||||
$type_display = "Contact";
|
||||
} elseif ($type === 4) {
|
||||
$type_display = "Credential";
|
||||
} elseif ($type === 5) {
|
||||
$type_display = "Asset";
|
||||
}
|
||||
}
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>New <strong><?= $type_display ?></strong> Tag</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="type" value="<?php echo $type; ?>">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Tag name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['type'])) { ?>
|
||||
|
||||
<input type="hidden" name="type" value="<?= $type ?>">
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Type -</option>
|
||||
<option value="1">Client Tag</option>
|
||||
<option value="2">Location Tag</option>
|
||||
<option value="3">Contact Tag</option>
|
||||
<option value="4">Credential Tag</option>
|
||||
<option value="5">Asset Tag</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Icon</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create Tag</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$tag_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_id = $tag_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$tag_name = nullable_htmlentities($row['tag_name']);
|
||||
$tag_type = intval($row['tag_type']);
|
||||
$tag_color = nullable_htmlentities($row['tag_color']);
|
||||
$tag_icon = nullable_htmlentities($row['tag_icon']);
|
||||
|
||||
if ($tag_type == 1) {
|
||||
$tag_type_display = "Client";
|
||||
} elseif ( $tag_type == 2) {
|
||||
$tag_type_display = "Location";
|
||||
} elseif ( $tag_type == 3) {
|
||||
$tag_type_display = "Contact";
|
||||
} elseif ( $tag_type == 4) {
|
||||
$tag_type_display = "Credential";
|
||||
} elseif ( $tag_type == 5) {
|
||||
$tag_type_display = "Asset";
|
||||
} else {
|
||||
$tag_type_display = "Unknown";
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i><?= $tag_type_display ?> Tag: <strong><?php echo $tag_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="tag_id" value="<?php echo $tag_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $tag_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" value="<?php echo $tag_color; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Icon</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" value="<?php echo $tag_icon; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save changes</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>New Tax</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<input type="text" class="form-control" name="name" placeholder="Tax name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Percent <strong class="text-danger">*</strong></label>
|
||||
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_tax" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$tax_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM taxes WHERE tax_id = $tax_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$tax_name = nullable_htmlentities($row['tax_name']);
|
||||
$tax_percent = floatval($row['tax_percent']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>Editing tax: <strong><?php echo $tax_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="tax_id" value="<?php echo $tax_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $tax_name; ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Percent <strong class="text-danger">*</strong></label>
|
||||
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent" value="<?php echo $tax_percent; ?>">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_tax" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
require_once '../../../includes/modal_header.php';
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>New Ticket Status</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-info-circle"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Ticket Status name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$ticket_status_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM ticket_statuses WHERE ticket_status_id = $ticket_status_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$ticket_status_name = nullable_htmlentities($row['ticket_status_name']);
|
||||
$ticket_status_color = nullable_htmlentities($row['ticket_status_color']);
|
||||
$ticket_status_order = intval($row['ticket_status_order']);
|
||||
$ticket_status_active = intval($row['ticket_status_active']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>Editing Ticket Status: <strong><?php echo $ticket_status_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="ticket_status_id" value="<?php echo $ticket_status_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_status_name; ?>" required <?php if ($ticket_status_id <= 5) { echo "readonly"; } ?>>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" value="<?php echo $ticket_status_color; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Order</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?php echo $ticket_status_order; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Status <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-info-circle"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="status" required>
|
||||
<option <?php if ($ticket_status_active == 1) { echo "selected"; } ?> value="1">Active</option>
|
||||
<option <?php if ($ticket_status_active == 0) { echo "selected"; } ?> value="0" <?php if ($ticket_status_id <= 5) { echo "disabled"; } ?>>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>New Ticket Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="subject" placeholder="Subject" maxlength="500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymceTicket" name="details"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Short description">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Add it to a Project Template?</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="project_template">
|
||||
<option value="0">- No -</option>
|
||||
<?php
|
||||
|
||||
$sql_project_templates = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_archived_at IS NULL ORDER BY project_template_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_project_templates)) {
|
||||
$project_template_id_select = intval($row['project_template_id']);
|
||||
$project_template_name_select = nullable_htmlentities($row['project_template_name']); ?>
|
||||
<option value="<?php echo $project_template_id_select; ?>"><?php echo $project_template_name_select; ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<div class="modal" id="editTicketTemplateModal" tabindex="-1">
|
||||
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Editing Ticket Template: <?php echo $ticket_template_name; ?></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Template Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_template_name; ?>" placeholder="Template name" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="subject" maxlength="500" value="<?php echo $ticket_template_subject; ?>" placeholder="Subject">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control tinymceTicket" name="details"><?php echo $ticket_template_details; ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" value="<?php echo $ticket_template_description; ?>" placeholder="Short description">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$task_template_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_id = $task_template_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$task_template_name = nullable_htmlentities($row['task_template_name']);
|
||||
$task_template_order = intval($row['task_template_order']);
|
||||
$task_template_completion_estimate = intval($row['task_template_completion_estimate']);
|
||||
//$task_template_description = nullable_htmlentities($row['task_template_description']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-tasks mr-2"></i>Editing task</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="task_template_id" value="<?php echo $task_template_id; ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Name the task" maxlength="255" value="<?php echo $task_template_name; ?>" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Estimated Completion Time <span class="text-secondary">(Minutes)</span></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="completion_estimate" placeholder="Estimated time to complete task in mins" value="<?php echo $task_template_completion_estimate; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_ticket_template_task" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus mr-2"></i>New User</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-user-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-user-access">Restrict Access</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-user-details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Full Name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Password <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" data-toggle="password" name="password" id="password" placeholder="Enter a Password" autocomplete="new-password" minlength="8" required>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<option value="">- Role -</option>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$role_id = intval($row['role_id']);
|
||||
$role_name = nullable_htmlentities($row['role_name']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group" <?php if(empty($config_smtp_host)) { echo "hidden"; } ?>>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="sendEmailCheckBox" name="send_email" value="" checked>
|
||||
<label for="sendEmailCheckBox" class="custom-control-label">
|
||||
Send user e-mail with login details?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox" name="force_mfa" value=1>
|
||||
<label for="forceMFACheckBox" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-user-access">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Check boxes to authorize user client access. No boxes grant full client access. Admin users are unaffected.
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.client-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Restrict Access to Clients</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php
|
||||
|
||||
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_client_select)) {
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input client-checkbox" name="clients[]" value="<?php echo $client_id; ?>">
|
||||
<label class="form-check-label ml-3"><?php echo $client_name; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
function generatePassword() {
|
||||
jQuery.get(
|
||||
"/agent/ajax.php", {
|
||||
get_readable_pass: 'true'
|
||||
},
|
||||
function(data) {
|
||||
const password = JSON.parse(data);
|
||||
document.getElementById("password").value = password;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-body">
|
||||
<div class="mb-4" style="text-align: center;">
|
||||
<i class="far fas fa-10x fa-skull-crossbones text-danger mb-3 mt-3"></i>
|
||||
<h2>Incident Response: Agent Password Reset</h2>
|
||||
<br>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<b>This is a potentially destructive function.<br>It is intended to be used as part of a potential security incident.</b>
|
||||
</div>
|
||||
<h6 class="mb-4 text-secondary"><b>All ITFlow agent passwords will be reset and shown to you </b><i>(except yours - change yours first!)</i>.<br/><br/>You should communicate temporary passwords to agents out of band (e.g. via a phone call) and require they are changed ASAP.</h6>
|
||||
<form action="post.php" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="row col-7 offset-4">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<input type="password" class="form-control" placeholder="Enter your account password to continue" name="admin_password" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-danger" type="submit" name="ir_reset_user_password"><i class="fas fa-fw fa-key mr-2"></i>Reset passwords</button>
|
||||
</form>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-lg px-5 mr-4" data-dismiss="modal">Cancel</button>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$user_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE users.user_id = $user_id LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$user_name = nullable_htmlentities($row['user_name']);
|
||||
$user_email = nullable_htmlentities($row['user_email']);
|
||||
$user_avatar = nullable_htmlentities($row['user_avatar']);
|
||||
$user_initials = nullable_htmlentities(initials($user_name));
|
||||
|
||||
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets
|
||||
WHERE ticket_assigned_to = $user_id AND ticket_resolved_at IS NULL AND ticket_closed_at IS NULL");
|
||||
|
||||
$ticket_count = mysqli_num_rows($sql_related_tickets);
|
||||
|
||||
// Related Recurring Tickets Query
|
||||
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_assigned_to = $user_id");
|
||||
|
||||
$recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-slash mr-2"></i>Archiving user:
|
||||
<strong><?php echo $user_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
|
||||
<center class="mb-3">
|
||||
<?php if (!empty($user_avatar)) { ?>
|
||||
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-4x">
|
||||
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
|
||||
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</center>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Reassign <?= $ticket_count ?> Open Tickets and <?= $recurring_ticket_count ?> Recurring Tickets To:</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="ticket_assign" required>
|
||||
<option value="0">No one</option>
|
||||
<?php
|
||||
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_type = 1 AND user_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_users)) {
|
||||
$user_id_select = intval($row['user_id']);
|
||||
$user_name_select = nullable_htmlentities($row['user_name']);
|
||||
|
||||
?>
|
||||
<option value="<?= $user_id_select ?>"><?= $user_name_select ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="archive_user" class="btn btn-danger text-bold"><i class="fas fa-archive mr-2"></i>Archive</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$user_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users
|
||||
LEFT JOIN user_settings ON users.user_id = user_settings.user_id
|
||||
WHERE users.user_id = $user_id LIMIT 1"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$user_name = nullable_htmlentities($row['user_name']);
|
||||
$user_email = nullable_htmlentities($row['user_email']);
|
||||
$user_avatar = nullable_htmlentities($row['user_avatar']);
|
||||
$user_token = nullable_htmlentities($row['user_token']);
|
||||
$user_config_force_mfa = intval($row['user_config_force_mfa']);
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
$user_initials = nullable_htmlentities(initials($user_name));
|
||||
|
||||
// Get User Client Access Permissions
|
||||
$user_client_access_sql = mysqli_query($mysqli,"SELECT client_id FROM user_client_permissions WHERE user_id = $user_id");
|
||||
$client_access_array = [];
|
||||
while ($row = mysqli_fetch_assoc($user_client_access_sql)) {
|
||||
$client_access_array[] = intval($row['client_id']);
|
||||
}
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-edit mr-2"></i>Editing user:
|
||||
<strong><?php echo $user_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-user-details<?php echo $user_id; ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-user-access<?php echo $user_id; ?>">Restrict Access</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-user-details<?php echo $user_id; ?>">
|
||||
|
||||
<center class="mb-3">
|
||||
<?php if (!empty($user_avatar)) { ?>
|
||||
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-4x">
|
||||
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
|
||||
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</center>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Full Name" maxlength="200"
|
||||
value="<?php echo $user_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200"
|
||||
value="<?php echo $user_email; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" data-toggle="password" name="new_password" id="password"
|
||||
placeholder="Leave Blank For No Password Change" autocomplete="new-password">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$role_id = intval($row['role_id']);
|
||||
$role_name = nullable_htmlentities($row['role_name']);
|
||||
|
||||
?>
|
||||
<option <?php if ($role_id == $user_role_id) {echo "selected";} ?> value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox<?php echo $user_id; ?>" name="force_mfa" value="1" <?php if($user_config_force_mfa == 1){ echo "checked"; } ?>>
|
||||
<label for="forceMFACheckBox<?php echo $user_id; ?>" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($user_token)) { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>2FA</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-id-card"></i></span>
|
||||
</div>
|
||||
<select class="form-control" name="2fa">
|
||||
<option value="">Keep enabled</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-user-access<?php echo $user_id; ?>">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Check boxes to authorize user client access. No boxes grant full client access. Admin users are unaffected.
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.client-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Restrict Access to Clients</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php
|
||||
|
||||
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_client_select)) {
|
||||
$client_id_select = intval($row['client_id']);
|
||||
$client_name_select = nullable_htmlentities($row['client_name']);
|
||||
|
||||
?>
|
||||
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input client-checkbox" name="clients[]" value="<?php echo $client_id_select; ?>" <?php if (in_array($client_id_select, $client_access_array)) { echo "checked"; } ?>>
|
||||
<label class="form-check-label ml-2"><?php echo $client_name_select; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
function generatePassword() {
|
||||
// Send a GET request to ajax.php as ajax.php?get_readable_pass=true
|
||||
jQuery.get(
|
||||
"/agent/ajax.php", {
|
||||
get_readable_pass: 'true'
|
||||
},
|
||||
function(data) {
|
||||
//If we get a response from post.php, parse it as JSON
|
||||
const password = JSON.parse(data);
|
||||
document.getElementById("password").value = password;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Users to CSV</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="export_users_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus"></i>Invite User</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<option value="">- Role -</option>
|
||||
<!-- //TODO: Pull from roles -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="invite_user" class="btn btn-primary text-bold"><i class="fas fa-paper-plane mr-2"></i>Send Invite</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$user_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $user_id AND user_archived_at IS NOT NULL LIMIT 1");
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$user_name = str_replace(" (archived)", "", $row['user_name']); //Removed (archived) from user_name
|
||||
$user_name = nullable_htmlentities($user_name);
|
||||
$user_email = nullable_htmlentities($row['user_email']);
|
||||
$user_avatar = nullable_htmlentities($row['user_avatar']);
|
||||
$user_initials = initials($user_name);
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-redo-alt mr-2"></i>Restoring user:
|
||||
<strong><?php echo $user_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
|
||||
<center class="mb-3">
|
||||
<?php if (!empty($user_avatar)) { ?>
|
||||
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-4x">
|
||||
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
|
||||
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</center>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Set a New Password</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" data-toggle="password" name="new_password"
|
||||
placeholder="Enter a new password" autocomplete="new-password" required>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$role_id = intval($row['role_id']);
|
||||
$role_name = nullable_htmlentities($row['role_name']);
|
||||
|
||||
?>
|
||||
<option <?php if ($role_id == $user_role_id) {echo "selected";} ?> value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="restore_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Restore</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once "../../../includes/modal_footer.php";
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>New Vendor Template</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-support">Support</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-notes">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Vendor Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Account Number</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Account Manager</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="contact_name" placeholder="Account manager's name" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-support">
|
||||
|
||||
<label>Support Phone / <span class="text-secondary">Extension</span></label>
|
||||
<div class="form-row">
|
||||
<div class="col-9">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
|
||||
</div>
|
||||
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
|
||||
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="extension" placeholder="ext." maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Hours</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="hours" placeholder="Support Hours" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Email</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="email" placeholder="Support Email" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Website URL</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Pin/Code</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>SLA</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes">
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_vendor_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
$vendor_template_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM vendor_templates WHERE vendor_template_id = $vendor_template_id LIMIT 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_template_name']);
|
||||
$vendor_description = nullable_htmlentities($row['vendor_template_description']);
|
||||
$vendor_account_number = nullable_htmlentities($row['vendor_template_account_number']);
|
||||
$vendor_contact_name = nullable_htmlentities($row['vendor_template_contact_name']);
|
||||
$vendor_phone_country_code = intval($row['vendor_template_phone_country_code']);
|
||||
$vendor_phone = formatPhoneNumber($row['vendor_template_phone'], $vendor_phone_country_code);
|
||||
$vendor_extension = nullable_htmlentities($row['vendor_template_extension']);
|
||||
$vendor_email = nullable_htmlentities($row['vendor_template_email']);
|
||||
$vendor_website = nullable_htmlentities($row['vendor_template_website']);
|
||||
$vendor_hours = nullable_htmlentities($row['vendor_template_hours']);
|
||||
$vendor_sla = nullable_htmlentities($row['vendor_template_sla']);
|
||||
$vendor_code = nullable_htmlentities($row['vendor_template_code']);
|
||||
$vendor_notes = nullable_htmlentities($row['vendor_template_notes']);
|
||||
|
||||
// Generate the HTML form content using output buffering.
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>Editing vendor template: <strong><?php echo $vendor_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="vendor_template_id" value="<?php echo $vendor_template_id; ?>">
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-details<?php echo $vendor_template_id; ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-support<?php echo $vendor_template_id; ?>">Support</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-notes<?php echo $vendor_template_id; ?>">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="alert alert-info">Check the fields you would like to update globally</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details<?php echo $vendor_template_id; ?>">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Vendor Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" value="<?php echo "$vendor_name"; ?>" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_name" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description" maxlength="200" value="<?php echo $vendor_description; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_description" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Account Number</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200" value="<?php echo $vendor_account_number; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_account_number" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Account Manager</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="contact_name" maxlength="200" value="<?php echo $vendor_contact_name; ?>" placeholder="Vendor contact name">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_contact_name" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="updateVendorsCheckbox<?php echo $vendor_template_id; ?>" name="update_base_vendors" value="1" >
|
||||
<label class="custom-control-label" for="updateVendorsCheckbox<?php echo $vendor_template_id; ?>">Update All Base Vendors</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-support<?php echo $vendor_template_id; ?>">
|
||||
|
||||
<label>Support Phone</label>
|
||||
<div class="form-row">
|
||||
<div class="col-8">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
|
||||
</div>
|
||||
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4" value="<?php echo $vendor_phone_country_code; ?>">
|
||||
<input type="tel" class="form-control" name="phone" value="<?php echo $vendor_phone; ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200" value="<?php echo $vendor_extension; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_phone" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Hours</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="hours" placeholder="Support Hours" maxlength="200" value="<?php echo $vendor_hours; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_hours" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Email</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<input type="email" class="form-control" name="email" placeholder="Support Email" maxlength="200" value="<?php echo $vendor_email; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_email" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Support Website URL</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200" value="<?php echo $vendor_website; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_website" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>SLA</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200" value="<?php echo $vendor_sla; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_sla" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Pin/Code</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200" value="<?php echo $vendor_code; ?>">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="checkbox" name="global_update_vendor_code" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes<?php echo $vendor_template_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $vendor_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Update Notes Globally?</label>
|
||||
<input type="checkbox" name="global_update_vendor_notes" value="1">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary text-bold" name="edit_vendor_template"><i class="fa fa-check mr-2"></i>Update Template</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "module_name";
|
||||
$order = "DESC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM modules
|
||||
WHERE (module_name LIKE '%$q%' OR module_description LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-puzzle-piece mr-2"></i>Access Modules</h3>
|
||||
<div class="card-tools">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/module/module_add.php">
|
||||
<i class="fas fa-fw fa-plus mr-2"></i>New Module
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="mb-4" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(nullable_htmlentities($q));} ?>" placeholder="Search Modules">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=module_name&order=<?php echo $disp; ?>">
|
||||
Module <?php if ($sort == 'module_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$module_id = intval($row['module_id']);
|
||||
$module_name = nullable_htmlentities($row['module_name']);
|
||||
$module_description = nullable_htmlentities($row['module_description']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#" <?php if ($module_id > 6) { ?> class="ajax-modal" data-modal-url="modals/modules/module_edit.php?id=<?= $module_id ?>" <?php } ?>>
|
||||
<strong class="text-dark"><?= $module_name ?></strong>
|
||||
</a>
|
||||
<div class="text-secondary"><?= $module_description ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($module_id > 6) { ?>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/module/module_edit.php?id=<?= $module_id ?>">
|
||||
<i class="fas fa-fw fa-user-edit mr-2"></i>Edit
|
||||
</a>
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_module=<?= $module_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Delete
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { echo "<p class='text-center'>N/A Predefined</p>"; } ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "../includes/filter_footer.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "payment_method_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods ORDER BY $sort $order");
|
||||
|
||||
$num_rows = mysqli_num_rows($sql);
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Methods</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_method/payment_method_add.php"><i class="fas fa-plus mr-2"></i>Add Payment Method</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_name&order=<?php echo $disp; ?>">
|
||||
Method <?php if ($sort == 'payment_method_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_description&order=<?php echo $disp; ?>">
|
||||
Description <?php if ($sort == 'payment_method_description') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_created_at&order=<?php echo $disp; ?>">
|
||||
Created at <?php if ($sort == 'payment_method_created_at') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$payment_method_id = intval($row['payment_method_id']);
|
||||
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
|
||||
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
|
||||
$payment_method_created_at = nullable_htmlentities($row['payment_method_created_at']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark text-bold ajax-modal" href="#"
|
||||
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
|
||||
<?php echo $payment_method_name; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $payment_method_description; ?></td>
|
||||
<td><?php echo $payment_method_created_at; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_method=<?php echo $payment_method_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
if ($num_rows == 0) {
|
||||
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "payment_provider_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "includes/inc_all_admin.php";
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers
|
||||
LEFT JOIN accounts ON payment_provider_account = account_id
|
||||
LEFT JOIN vendors ON payment_provider_expense_vendor = vendor_id
|
||||
LEFT JOIN categories ON payment_provider_expense_category = category_id
|
||||
ORDER BY $sort $order"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_num_rows($sql);
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Providers</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_provider/payment_provider_add.php"><i class="fas fa-plus mr-2"></i>Add Provider</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_name&order=<?php echo $disp; ?>">
|
||||
Provider <?php if ($sort == 'payment_provider_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=account_name&order=<?php echo $disp; ?>">
|
||||
Expense / Income Account <?php if ($sort == 'account_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_threshold&order=<?php echo $disp; ?>">
|
||||
Threshold <?php if ($sort == 'payment_provider_threshold') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">
|
||||
Expense Vendor <?php if ($sort == 'vendor_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">
|
||||
Expense Category <?php if ($sort == 'category_name') { echo $order_icon; } ?>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="text-dark">Expensed Fee</a>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<a class="text-dark">Saved Payment Methods</a>
|
||||
</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$provider_id = intval($row['payment_provider_id']);
|
||||
$provider_name = nullable_htmlentities($row['payment_provider_name']);
|
||||
$provider_description = nullable_htmlentities($row['payment_provider_description']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
$threshold = floatval($row['payment_provider_threshold']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name'] ?? "Expense Disabled");
|
||||
$category = nullable_htmlentities($row['category_name']);
|
||||
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
|
||||
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('saved_payment_id') AS saved_payment_count FROM client_saved_payment_methods WHERE saved_payment_provider_id = $provider_id"));
|
||||
$saved_payment_count = intval($row['saved_payment_count']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark text-bold ajax-modal" href="#"
|
||||
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
|
||||
<?php echo $provider_name; ?>
|
||||
</a>
|
||||
<span class="text-secondary"><?php echo $provider_description; ?></span>
|
||||
</td>
|
||||
<td><?php echo $account_name; ?></td>
|
||||
<td><?php echo numfmt_format_currency($currency_format, $threshold, $session_company_currency); ?></td>
|
||||
<td><?php echo $vendor_name; ?></td>
|
||||
<td><?php echo $category; ?></td>
|
||||
<td><?php echo $percent_fee; ?>% + <?php echo numfmt_format_currency($currency_format, $flat_fee, $session_company_currency); ?></td>
|
||||
<td class="text-center">
|
||||
<a class="badge badge-dark badge-pill p-2" href="saved_payment_method.php"><?= $saved_payment_count ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_provider=<?= $provider_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i><strong>Delete Provider and</strong>
|
||||
<ul class="text-xs">
|
||||
<li>Related Recurring Payments</li>
|
||||
<li>Related Saved cards</li>
|
||||
<li>Client Provider Relations</li>
|
||||
</ul>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
if ($num_rows == 0) {
|
||||
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "../includes/footer.php";
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - Admin GET/POST request handler
|
||||
*/
|
||||
|
||||
require_once "../config.php";
|
||||
require_once "../functions.php";
|
||||
require_once "../includes/check_login.php";
|
||||
|
||||
// Define a variable that we can use to only allow running post files via inclusion (prevents people/bots poking them)
|
||||
define('FROM_POST_HANDLER', true);
|
||||
|
||||
// Determine which files we should load
|
||||
|
||||
// Parse URL & get the path
|
||||
$path = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH);
|
||||
|
||||
// Get the base name (the page name)
|
||||
$module = explode(".", basename($path))[0];
|
||||
|
||||
// Strip off any _details bits
|
||||
$module = str_ireplace('_details', '', $module);
|
||||
|
||||
// Dynamically load admin-related module POST logic
|
||||
if (isset($session_is_admin) && $session_is_admin) {
|
||||
// As (almost) every admin setting is only changed from 1 page, we can dynamically load the relevant logic inside this single admin check IF statement
|
||||
// To add a new admin POST request handler, add a file named after the admin page
|
||||
// e.g. changes made on the page http://itflow/admin_ticket_statues.php will load the page admin/post/admin_ticket_statues.php to handle the changes
|
||||
|
||||
include_once "post/$module.php";
|
||||
|
||||
}
|
||||
|
||||
// Logout is the same for user and admin
|
||||
require_once "../post/logout.php";
|
||||
|
||||
// TODO: Find a home for these
|
||||
require_once "../post/misc.php";
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for AI Models ('ai_model')
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_ai_model'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider_id = intval($_POST['provider']);
|
||||
$model = sanitizeInput($_POST['model']);
|
||||
$prompt = sanitizeInput($_POST['prompt']);
|
||||
$use_case = sanitizeInput($_POST['use_case']);
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_ai_provider_id = $provider_id");
|
||||
|
||||
$ai_model_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("AI Model", "Create", "$session_name created AI Model $model");
|
||||
|
||||
flash_alert("AI Model <strong>$model</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_ai_model'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$model_id = intval($_POST['model_id']);
|
||||
$model = sanitizeInput($_POST['model']);
|
||||
$prompt = sanitizeInput($_POST['prompt']);
|
||||
$use_case = sanitizeInput($_POST['use_case']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case' WHERE ai_model_id = $model_id");
|
||||
|
||||
logAction("AI Model", "Edit", "$session_name edited AI Model $model");
|
||||
|
||||
flash_alert("AI Model <strong>$model</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_ai_model'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$model_id = intval($_GET['delete_ai_model']);
|
||||
|
||||
$model_name = sanitizeInput(getFieldById('ai_models', $model_id, 'ai_model_name'));
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM ai_models WHERE ai_model_id = $model_id");
|
||||
|
||||
logAction("AI Model", "Delete", "$session_name deleted AI Model $model_name");
|
||||
|
||||
flash_alert("AI Model <strong>$model_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for AI Providers ('ai_provider')
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_ai_provider'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider = sanitizeInput($_POST['provider']);
|
||||
$url = sanitizeInput($_POST['url']);
|
||||
$model = sanitizeInput($_POST['model']);
|
||||
$api_key = sanitizeInput($_POST['api_key']);
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key'");
|
||||
|
||||
$ai_provider_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("AI Provider", "Create", "$session_name created AI Provider $provider");
|
||||
|
||||
flash_alert("AI Model <strong>$provider</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_ai_provider'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider_id = intval($_POST['provider_id']);
|
||||
$provider = sanitizeInput($_POST['provider']);
|
||||
$url = sanitizeInput($_POST['url']);
|
||||
$api_key = sanitizeInput($_POST['api_key']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key' WHERE ai_provider_id = $provider_id");
|
||||
|
||||
logAction("AI Provider", "Edit", "$session_name edited AI Provider $provider");
|
||||
|
||||
flash_alert("AI Model <strong>$provider</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_ai_provider'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$provider_id = intval($_GET['delete_ai_provider']);
|
||||
|
||||
$provider_name = sanitizeInput(getFieldById('ai_providers', $provider_id, 'ai_provider_name'));
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM ai_providers WHERE ai_provider_id = $provider_id");
|
||||
|
||||
logAction("AI Provider", "Delete", "$session_name deleted AI Provider $provider_name", 'error');
|
||||
|
||||
flash_alert("AI Provider <strong>$provider_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for API settings
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_api_key'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$expire = sanitizeInput($_POST['expire']);
|
||||
$client_id = intval($_POST['client']);
|
||||
$secret = sanitizeInput($_POST['key']); // API Key
|
||||
|
||||
// Credential decryption password
|
||||
$password = password_hash(trim($_POST['password']), PASSWORD_DEFAULT);
|
||||
$apikey_specific_encryption_ciphertext = encryptUserSpecificKey(trim($_POST['password']));
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO api_keys SET api_key_name = '$name', api_key_secret = '$secret', api_key_decrypt_hash = '$apikey_specific_encryption_ciphertext', api_key_expire = '$expire', api_key_client_id = $client_id");
|
||||
|
||||
$api_key_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("API Key", "Create", "$session_name created API key $name set to expire on $expire", $client_id, $api_key_id);
|
||||
|
||||
flash_alert("API Key <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_api_key'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$api_key_id = intval($_GET['delete_api_key']);
|
||||
|
||||
// Get API Key Name
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT api_key_name, api_key_client_id FROM api_keys WHERE api_key_id = $api_key_id"));
|
||||
$api_key_name = sanitizeInput($row['api_key_name']);
|
||||
$client_id = intval($row['api_key_client_id']);
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM api_keys WHERE api_key_id = $api_key_id");
|
||||
|
||||
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
|
||||
|
||||
flash_alert("API Key <strong>$name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['bulk_delete_api_keys'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
if (isset($_POST['api_key_ids'])) {
|
||||
|
||||
$count = count($_POST['api_key_ids']);
|
||||
|
||||
// Cycle through array and delete each record
|
||||
foreach ($_POST['api_key_ids'] as $api_key_id) {
|
||||
|
||||
$api_key_id = intval($api_key_id);
|
||||
|
||||
// Get API Key Name
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT api_key_name, api_key_client_id FROM api_keys WHERE api_key_id = $api_key_id"));
|
||||
$api_key_name = sanitizeInput($row['api_key_name']);
|
||||
$client_id = intval($row['api_key_client_id']);
|
||||
|
||||
mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_id = $api_key_id");
|
||||
|
||||
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
|
||||
|
||||
}
|
||||
|
||||
logAction("API Key", "Bulk Delete", "$session_name deleted $count API key(s)");
|
||||
|
||||
flash_alert("Deleted <strong>$count</strong> API keys(s)", 'error');
|
||||
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for DB / master key backup
|
||||
* Rewritten with streaming SQL dump, component checksums, safer zipping, and better headers.
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
require_once "../includes/app_version.php";
|
||||
|
||||
// --- Optional performance levers for big backups ---
|
||||
@set_time_limit(0);
|
||||
if (function_exists('ini_set')) {
|
||||
@ini_set('memory_limit', '1024M');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a line to a file handle with newline.
|
||||
*/
|
||||
function fwrite_ln($fh, string $s): void {
|
||||
fwrite($fh, $s);
|
||||
fwrite($fh, PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a SQL dump of schema and data into $sqlFile.
|
||||
* - Tables first (DROP + CREATE + INSERTs)
|
||||
* - Views (DROP VIEW + CREATE VIEW)
|
||||
* - Triggers (DROP TRIGGER + CREATE TRIGGER)
|
||||
*
|
||||
* NOTE: Routines/events are not dumped here. Add if needed.
|
||||
*/
|
||||
function dump_database_streaming(mysqli $mysqli, string $sqlFile): void {
|
||||
$fh = fopen($sqlFile, 'wb');
|
||||
if (!$fh) {
|
||||
http_response_code(500);
|
||||
exit("Cannot open dump file");
|
||||
}
|
||||
|
||||
// Preamble
|
||||
fwrite_ln($fh, "-- UTF-8 + Foreign Key Safe Dump");
|
||||
fwrite_ln($fh, "SET NAMES 'utf8mb4';");
|
||||
fwrite_ln($fh, "SET FOREIGN_KEY_CHECKS = 0;");
|
||||
fwrite_ln($fh, "SET UNIQUE_CHECKS = 0;");
|
||||
fwrite_ln($fh, "SET AUTOCOMMIT = 0;");
|
||||
fwrite_ln($fh, "");
|
||||
|
||||
// Gather tables and views
|
||||
$tables = [];
|
||||
$views = [];
|
||||
|
||||
$res = $mysqli->query("SHOW FULL TABLES");
|
||||
if (!$res) {
|
||||
fclose($fh);
|
||||
error_log("MySQL Error (SHOW FULL TABLES): " . $mysqli->error);
|
||||
http_response_code(500);
|
||||
exit("Error retrieving tables.");
|
||||
}
|
||||
while ($row = $res->fetch_array(MYSQLI_NUM)) {
|
||||
$name = $row[0];
|
||||
$type = strtoupper($row[1] ?? '');
|
||||
if ($type === 'VIEW') {
|
||||
$views[] = $name;
|
||||
} else {
|
||||
$tables[] = $name;
|
||||
}
|
||||
}
|
||||
$res->close();
|
||||
|
||||
// --- TABLES: structure and data ---
|
||||
foreach ($tables as $table) {
|
||||
$createRes = $mysqli->query("SHOW CREATE TABLE `{$mysqli->real_escape_string($table)}`");
|
||||
if (!$createRes) {
|
||||
error_log("MySQL Error (SHOW CREATE TABLE $table): " . $mysqli->error);
|
||||
// continue to next table
|
||||
continue;
|
||||
}
|
||||
$createRow = $createRes->fetch_assoc();
|
||||
$createSQL = array_values($createRow)[1] ?? '';
|
||||
$createRes->close();
|
||||
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "-- Table structure for `{$table}`");
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "DROP TABLE IF EXISTS `{$table}`;");
|
||||
fwrite_ln($fh, $createSQL . ";");
|
||||
fwrite_ln($fh, "");
|
||||
|
||||
// Dump data in a streaming fashion
|
||||
$dataRes = $mysqli->query("SELECT * FROM `{$mysqli->real_escape_string($table)}`", MYSQLI_USE_RESULT);
|
||||
if ($dataRes) {
|
||||
$wroteHeader = false;
|
||||
while ($row = $dataRes->fetch_assoc()) {
|
||||
if (!$wroteHeader) {
|
||||
fwrite_ln($fh, "-- Dumping data for table `{$table}`");
|
||||
$wroteHeader = true;
|
||||
}
|
||||
$cols = array_map(fn($c) => '`' . $mysqli->real_escape_string($c) . '`', array_keys($row));
|
||||
$vals = array_map(
|
||||
function ($v) use ($mysqli) {
|
||||
return is_null($v) ? "NULL" : "'" . $mysqli->real_escape_string($v) . "'";
|
||||
},
|
||||
array_values($row)
|
||||
);
|
||||
fwrite_ln($fh, "INSERT INTO `{$table}` (" . implode(", ", $cols) . ") VALUES (" . implode(", ", $vals) . ");");
|
||||
}
|
||||
$dataRes->close();
|
||||
if ($wroteHeader) fwrite_ln($fh, "");
|
||||
}
|
||||
}
|
||||
|
||||
// --- VIEWS ---
|
||||
foreach ($views as $view) {
|
||||
$escView = $mysqli->real_escape_string($view);
|
||||
$cRes = $mysqli->query("SHOW CREATE VIEW `{$escView}`");
|
||||
if ($cRes) {
|
||||
$row = $cRes->fetch_assoc();
|
||||
$createView = $row['Create View'] ?? '';
|
||||
$cRes->close();
|
||||
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "-- View structure for `{$view}`");
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "DROP VIEW IF EXISTS `{$view}`;");
|
||||
// Ensure statement ends with semicolon
|
||||
if (!str_ends_with($createView, ';')) $createView .= ';';
|
||||
fwrite_ln($fh, $createView);
|
||||
fwrite_ln($fh, "");
|
||||
}
|
||||
}
|
||||
|
||||
// --- TRIGGERS ---
|
||||
$tRes = $mysqli->query("SHOW TRIGGERS");
|
||||
if ($tRes) {
|
||||
while ($t = $tRes->fetch_assoc()) {
|
||||
$triggerName = $t['Trigger'];
|
||||
$escTrig = $mysqli->real_escape_string($triggerName);
|
||||
$crt = $mysqli->query("SHOW CREATE TRIGGER `{$escTrig}`");
|
||||
if ($crt) {
|
||||
$row = $crt->fetch_assoc();
|
||||
$createTrig = $row['SQL Original Statement'] ?? ($row['Create Trigger'] ?? '');
|
||||
$crt->close();
|
||||
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "-- Trigger for `{$triggerName}`");
|
||||
fwrite_ln($fh, "-- ----------------------------");
|
||||
fwrite_ln($fh, "DROP TRIGGER IF EXISTS `{$triggerName}`;");
|
||||
if (!str_ends_with($createTrig, ';')) $createTrig .= ';';
|
||||
fwrite_ln($fh, $createTrig);
|
||||
fwrite_ln($fh, "");
|
||||
}
|
||||
}
|
||||
$tRes->close();
|
||||
}
|
||||
|
||||
// Postamble
|
||||
fwrite_ln($fh, "SET FOREIGN_KEY_CHECKS = 1;");
|
||||
fwrite_ln($fh, "SET UNIQUE_CHECKS = 1;");
|
||||
fwrite_ln($fh, "COMMIT;");
|
||||
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip a folder to $zipFilePath, skipping symlinks and dot-entries.
|
||||
*/
|
||||
function zipFolderStrict(string $folderPath, string $zipFilePath): void {
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
||||
error_log("Failed to open zip file: $zipFilePath");
|
||||
http_response_code(500);
|
||||
exit("Internal Server Error: Cannot open zip archive.");
|
||||
}
|
||||
|
||||
$folderReal = realpath($folderPath);
|
||||
if (!$folderReal || !is_dir($folderReal)) {
|
||||
// Create an empty archive if uploads folder doesn't exist yet
|
||||
$zip->close();
|
||||
return;
|
||||
}
|
||||
|
||||
$files = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($folderReal, FilesystemIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
/** @var SplFileInfo $file */
|
||||
if ($file->isDir()) continue;
|
||||
if ($file->isLink()) continue; // skip symlinks
|
||||
$filePath = $file->getRealPath();
|
||||
if ($filePath === false) continue;
|
||||
|
||||
// ensure path is inside the folder boundary
|
||||
if (strpos($filePath, $folderReal . DIRECTORY_SEPARATOR) !== 0 && $filePath !== $folderReal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relativePath = substr($filePath, strlen($folderReal) + 1);
|
||||
$zip->addFile($filePath, $relativePath);
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
if (isset($_GET['download_backup'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$timestamp = date('YmdHis');
|
||||
$baseName = "itflow_{$timestamp}";
|
||||
$downloadName = $baseName . ".zip";
|
||||
|
||||
// === Scoped cleanup of temp files ===
|
||||
$cleanupFiles = [];
|
||||
$registerTempFileForCleanup = function ($file) use (&$cleanupFiles) {
|
||||
$cleanupFiles[] = $file;
|
||||
};
|
||||
register_shutdown_function(function () use (&$cleanupFiles) {
|
||||
foreach ($cleanupFiles as $file) {
|
||||
if (is_file($file)) { @unlink($file); }
|
||||
}
|
||||
});
|
||||
|
||||
// === Create temp files ===
|
||||
$sqlFile = tempnam(sys_get_temp_dir(), $baseName . "_sql_");
|
||||
$uploadsZip = tempnam(sys_get_temp_dir(), $baseName . "_uploads_");
|
||||
$versionFile = tempnam(sys_get_temp_dir(), $baseName . "_version_");
|
||||
$finalZip = tempnam(sys_get_temp_dir(), $baseName . "_backup_");
|
||||
|
||||
foreach ([$sqlFile, $uploadsZip, $versionFile, $finalZip] as $f) {
|
||||
$registerTempFileForCleanup($f);
|
||||
@chmod($f, 0600);
|
||||
}
|
||||
|
||||
// === Generate SQL Dump (streaming) ===
|
||||
dump_database_streaming($mysqli, $sqlFile);
|
||||
|
||||
// === Zip the uploads folder (strict) ===
|
||||
zipFolderStrict("../uploads", $uploadsZip);
|
||||
|
||||
// === Gather metadata & checksums ===
|
||||
$commitHash = (function_exists('shell_exec') ? trim(shell_exec('git log -1 --format=%H 2>/dev/null')) : '') ?: 'N/A';
|
||||
$gitBranch = (function_exists('shell_exec') ? trim(shell_exec('git rev-parse --abbrev-ref HEAD 2>/dev/null')) : '') ?: 'N/A';
|
||||
|
||||
$dbSha = hash_file('sha256', $sqlFile) ?: 'N/A';
|
||||
$upSha = hash_file('sha256', $uploadsZip) ?: 'N/A';
|
||||
|
||||
$versionContent = "ITFlow Backup Metadata\n";
|
||||
$versionContent .= "-----------------------------\n";
|
||||
$versionContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
|
||||
$versionContent .= "Backup File: " . $downloadName . "\n";
|
||||
$versionContent .= "Generated By: " . ($session_name ?? 'Unknown User') . "\n";
|
||||
$versionContent .= "Host: " . gethostname() . "\n";
|
||||
$versionContent .= "Git Branch: $gitBranch\n";
|
||||
$versionContent .= "Git Commit: $commitHash\n";
|
||||
$versionContent .= "ITFlow Version: " . (defined('APP_VERSION') ? APP_VERSION : 'Unknown') . "\n";
|
||||
$versionContent .= "Database Version: " . (defined('CURRENT_DATABASE_VERSION') ? CURRENT_DATABASE_VERSION : 'Unknown') . "\n";
|
||||
$versionContent .= "Checksums (SHA256):\n";
|
||||
$versionContent .= " db.sql: $dbSha\n";
|
||||
$versionContent .= " uploads.zip: $upSha\n";
|
||||
|
||||
file_put_contents($versionFile, $versionContent);
|
||||
@chmod($versionFile, 0600);
|
||||
|
||||
// === Build final ZIP ===
|
||||
$final = new ZipArchive();
|
||||
if ($final->open($finalZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
||||
error_log("Failed to create final zip: $finalZip");
|
||||
http_response_code(500);
|
||||
exit("Internal Server Error: Unable to create backup archive.");
|
||||
}
|
||||
$final->addFile($sqlFile, "db.sql");
|
||||
$final->addFile($uploadsZip, "uploads.zip");
|
||||
$final->addFile($versionFile, "version.txt");
|
||||
$final->close();
|
||||
|
||||
@chmod($finalZip, 0600);
|
||||
|
||||
// === Serve final ZIP with a stable filename ===
|
||||
header('Content-Type: application/zip');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('Content-Disposition: attachment; filename="' . $downloadName . '"');
|
||||
header('Content-Length: ' . filesize($finalZip));
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
// Push file
|
||||
flush();
|
||||
$fp = fopen($finalZip, 'rb');
|
||||
fpassthru($fp);
|
||||
fclose($fp);
|
||||
|
||||
// Log + UX
|
||||
logAction("System", "Backup Download", ($session_name ?? 'Unknown User') . " downloaded full backup.");
|
||||
flash_alert("Full backup downloaded.");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['backup_master_key'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$password = $_POST['password'];
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $session_user_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
|
||||
if (password_verify($password, $row['user_password'])) {
|
||||
$site_encryption_master_key = decryptUserSpecificKey($row['user_specific_encryption_ciphertext'], $password);
|
||||
|
||||
logAction("Master Key", "Download", "$session_name retrieved the master encryption key");
|
||||
|
||||
appNotify("Master Key", "$session_name retrieved the master encryption key");
|
||||
|
||||
echo "==============================";
|
||||
echo "<br>Master encryption key:<br>";
|
||||
echo "<b>$site_encryption_master_key</b>";
|
||||
echo "<br>==============================";
|
||||
|
||||
} else {
|
||||
logAction("Master Key", "Download", "$session_name attempted to retrieve the master encryption key but failed");
|
||||
|
||||
flash_alert("Incorrect password.", 'error');
|
||||
|
||||
redirect();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for categories ('category')
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_category'])) {
|
||||
|
||||
require_once 'category_model.php';
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO categories SET category_name = '$name', category_type = '$type', category_color = '$color'");
|
||||
|
||||
$category_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("Category", "Create", "$session_name created category $type $name", 0, $category_id);
|
||||
|
||||
flash_alert("Category $type <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_category'])) {
|
||||
|
||||
require_once 'category_model.php';
|
||||
|
||||
$category_id = intval($_POST['category_id']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE categories SET category_name = '$name', category_type = '$type', category_color = '$color' WHERE category_id = $category_id");
|
||||
|
||||
logAction("Category", "Edit", "$session_name edited category $type $name", 0, $category_id);
|
||||
|
||||
flash_alert("Category $type <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['archive_category'])) {
|
||||
|
||||
$category_id = intval($_GET['archive_category']);
|
||||
|
||||
// Get Category Name and Type for logging
|
||||
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$category_name = sanitizeInput($row['category_name']);
|
||||
$category_type = sanitizeInput($row['category_type']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NOW() WHERE category_id = $category_id");
|
||||
|
||||
logAction("Category", "Archive", "$session_name archived category $category_type $category_name", 0, $category_id);
|
||||
|
||||
flash_alert("Category $category_type <strong>$category_name</strong> archived", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['unarchive_category'])) {
|
||||
|
||||
$category_id = intval($_GET['unarchive_category']);
|
||||
|
||||
// Get Category Name and Type for logging
|
||||
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$category_name = sanitizeInput($row['category_name']);
|
||||
$category_type = sanitizeInput($row['category_type']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NULL WHERE category_id = $category_id");
|
||||
|
||||
logAction("Category", "Unarchive", "$session_name unarchived category $category_type $category_name", 0, $category_id);
|
||||
|
||||
flash_alert("Category $category_type <strong>$category_name</strong> unarchived");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_category'])) {
|
||||
|
||||
$category_id = intval($_GET['delete_category']);
|
||||
|
||||
// Get Category Name and Type for logging
|
||||
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$category_name = sanitizeInput($row['category_name']);
|
||||
$category_type = sanitizeInput($row['category_type']);
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM categories WHERE category_id = $category_id");
|
||||
|
||||
logAction("Category", "Delete", "$session_name deleted category $category_type $category_name");
|
||||
|
||||
flash_alert("Category $category_type <strong>$category_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for Contract Templates
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_contract_template'])) {
|
||||
|
||||
// Sanitize text inputs
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
$type = sanitizeInput($_POST['type']);
|
||||
$renewal_frequency = sanitizeInput($_POST['renewal_frequency']);
|
||||
$support_hours = sanitizeInput($_POST['support_hours']);
|
||||
$details = mysqli_escape_string($mysqli, $_POST['details']);
|
||||
|
||||
// Numeric fields cast to integer
|
||||
$sla_low_resp = intval($_POST['sla_low_response_time']);
|
||||
$sla_med_resp = intval($_POST['sla_medium_response_time']);
|
||||
$sla_high_resp = intval($_POST['sla_high_response_time']);
|
||||
$sla_low_res = intval($_POST['sla_low_resolution_time']);
|
||||
$sla_med_res = intval($_POST['sla_medium_resolution_time']);
|
||||
$sla_high_res = intval($_POST['sla_high_resolution_time']);
|
||||
$rate_standard = intval($_POST['rate_standard']);
|
||||
$rate_after_hours = intval($_POST['hourly_rate_after_hours']);
|
||||
$net_terms = intval($_POST['net_terms']);
|
||||
|
||||
// Insert into database (numbers not quoted)
|
||||
mysqli_query($mysqli, "
|
||||
INSERT INTO contract_templates SET
|
||||
contract_template_name = '$name',
|
||||
contract_template_description = '$description',
|
||||
contract_template_details = '$details',
|
||||
contract_template_type = '$type',
|
||||
contract_template_renewal_frequency = '$renewal_frequency',
|
||||
contract_template_sla_low_response_time = $sla_low_resp,
|
||||
contract_template_sla_medium_response_time = $sla_med_resp,
|
||||
contract_template_sla_high_response_time = $sla_high_resp,
|
||||
contract_template_sla_low_resolution_time = $sla_low_res,
|
||||
contract_template_sla_medium_resolution_time = $sla_med_res,
|
||||
contract_template_sla_high_resolution_time = $sla_high_res,
|
||||
contract_template_rate_standard = $rate_standard,
|
||||
contract_template_rate_after_hours = $rate_after_hours,
|
||||
contract_template_support_hours = '$support_hours',
|
||||
contract_template_net_terms = $net_terms
|
||||
");
|
||||
|
||||
$contract_template_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Log action
|
||||
logAction("Contract Template", "Create", "$session_name created contract template $name", 0, $contract_template_id);
|
||||
|
||||
// Flash message
|
||||
flash_alert("Contract Template <strong>$name</strong> created");
|
||||
|
||||
// Redirect back
|
||||
redirect();
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_contract_template'])) {
|
||||
|
||||
$contract_template_id = intval($_POST['contract_template_id']);
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
$type = sanitizeInput($_POST['type']);
|
||||
$renewal_frequency= sanitizeInput($_POST['renewal_frequency']);
|
||||
$support_hours = sanitizeInput($_POST['support_hours']);
|
||||
$details = mysqli_escape_string($mysqli, $_POST['details']);
|
||||
$sla_low_resp = intval($_POST['sla_low_response_time']);
|
||||
$sla_med_resp = intval($_POST['sla_medium_response_time']);
|
||||
$sla_high_resp = intval($_POST['sla_high_response_time']);
|
||||
$sla_low_res = intval($_POST['sla_low_resolution_time']);
|
||||
$sla_med_res = intval($_POST['sla_medium_resolution_time']);
|
||||
$sla_high_res = intval($_POST['sla_high_resolution_time']);
|
||||
$rate_standard = intval($_POST['rate_standard']);
|
||||
$rate_after_hours = intval($_POST['rate_after_hours']);
|
||||
$net_terms = intval($_POST['net_terms']);
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
UPDATE contract_templates SET
|
||||
contract_template_name = '$name',
|
||||
contract_template_description = '$description',
|
||||
contract_template_details = '$details',
|
||||
contract_template_type = '$type',
|
||||
contract_template_renewal_frequency = '$renewal_frequency',
|
||||
contract_template_sla_low_response_time = $sla_low_resp,
|
||||
contract_template_sla_medium_response_time = $sla_med_resp,
|
||||
contract_template_sla_high_response_time = $sla_high_resp,
|
||||
contract_template_sla_low_resolution_time = $sla_low_res,
|
||||
contract_template_sla_medium_resolution_time = $sla_med_res,
|
||||
contract_template_sla_high_resolution_time = $sla_high_res,
|
||||
contract_template_rate_standard = $rate_standard,
|
||||
contract_template_rate_after_hours = $rate_after_hours,
|
||||
contract_template_support_hours = '$support_hours',
|
||||
contract_template_net_terms = $net_terms
|
||||
WHERE contract_template_id = $contract_template_id
|
||||
");
|
||||
|
||||
// Log action
|
||||
logAction("Contract Template", "Update", "$session_name updated contract template $name", 0, $contract_template_id);
|
||||
|
||||
// Flash + redirect
|
||||
flash_alert("Contract Template <strong>$name</strong> updated");
|
||||
redirect();
|
||||
}
|
||||
|
||||
if (isset($_GET['archive_contract_template'])) {
|
||||
$contract_template_id = intval($_GET['archive_contract_template']);
|
||||
|
||||
$name = getFieldById('contract_templates', $contract_template_id, 'contract_template_name');
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
UPDATE contract_templates SET contract_template_archived_at = NOW()
|
||||
WHERE contract_template_id = $contract_template_id
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
logAction("Contract Template", "Archive", "$session_name archived contract template $name", 0, $contract_template_id);
|
||||
flash_alert("Contract Template <strong>$name</strong> archived", "danger");
|
||||
redirect();
|
||||
}
|
||||
|
||||
if (isset($_GET['restore_contract_template'])) {
|
||||
$contract_template_id = intval($_GET['restore_contract_template']);
|
||||
|
||||
$name = getFieldById('contract_templates', $contract_template_id, 'contract_template_name');
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
UPDATE contract_templates SET contract_template_archived_at = NULL
|
||||
WHERE contract_template_id = $contract_template_id
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
logAction("Contract Template", "Restore", "$session_name restored contract template $name", 0, $contract_template_id);
|
||||
flash_alert("Contract Template <strong>$name</strong> restored");
|
||||
redirect();
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_contract_template'])) {
|
||||
$contract_template_id = intval($_GET['delete_contract_template']);
|
||||
|
||||
$name = getFieldById('contract_templates', $contract_template_id, 'contract_template_name');
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
DELETE FROM contract_templates
|
||||
WHERE contract_template_id = $contract_template_id
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
logAction("Contract Template", "Delete", "$session_name deleted contract template $name", 0, $contract_template_id);
|
||||
flash_alert("Contract Template <strong>$name</strong> deleted", "danger");
|
||||
redirect();
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for custom fields
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if(isset($_POST['create_custom_field'])){
|
||||
|
||||
require_once 'custom_field_model.php';
|
||||
|
||||
$table = sanitizeInput($_POST['table']);
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO custom_fields SET custom_field_table = '$table', custom_field_label = '$label', custom_field_type = '$type'");
|
||||
|
||||
$custom_field_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("Custom Field", "Create", "$session_name created custom field $label", 0, $custom_field_id);
|
||||
|
||||
flash_alert("Custom field <strong>$label</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if(isset($_POST['edit_custom_field'])){
|
||||
|
||||
require_once 'custom_field_model.php';
|
||||
|
||||
$custom_field_id = intval($_POST['custom_field_id']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE custom_fields SET custom_field_label = '$label', custom_field_type = '$type' WHERE custom_field_id = $custom_field_id");
|
||||
|
||||
logAction("Custom Field", "Edit", "$session_name edited custom field $label", 0, $custom_field_id);
|
||||
|
||||
flash_alert("Custom field <strong>$label</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if(isset($_GET['delete_custom_field'])){
|
||||
|
||||
$custom_field_id = intval($_GET['delete_custom_field']);
|
||||
|
||||
$label = sanitizeInput(getFieldById('custom_fields', $custom_field_id, 'custom_field_label'));
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM custom_fields WHERE custom_field_id = $custom_field_id");
|
||||
|
||||
logAction("Custom Field", "Delete", "$session_name deleted custom field $label");
|
||||
|
||||
flash_alert("Custom field <strong>$label</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for showing custom links on navbars
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_custom_link'])) {
|
||||
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$uri = sanitizeInput($_POST['uri']);
|
||||
$new_tab = intval($_POST['new_tab'] ?? 0);
|
||||
$icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon']));
|
||||
$order = intval($_POST['order'] ?? 0);
|
||||
$location = intval($_POST['location']);
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_new_tab = $new_tab, custom_link_icon = '$icon', custom_link_order = $order, custom_link_location = $location");
|
||||
|
||||
$custom_link_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("Custom Link", "Create", "$session_name created custom link $name -> $uri", 0, $custom_link_id);
|
||||
|
||||
flash_alert("Custom link <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_custom_link'])) {
|
||||
|
||||
$custom_link_id = intval($_POST['custom_link_id']);
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$uri = sanitizeInput($_POST['uri']);
|
||||
$new_tab = intval($_POST['new_tab'] ?? 0);
|
||||
$icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon']));
|
||||
$order = intval($_POST['order'] ?? 0);
|
||||
$location = intval($_POST['location']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_new_tab = $new_tab, custom_link_icon = '$icon', custom_link_order = $order, custom_link_location = $location WHERE custom_link_id = $custom_link_id");
|
||||
|
||||
logAction("Custom Link", "Edit", "$session_name edited custom link $name -> $uri", 0, $custom_link_id);
|
||||
|
||||
flash_alert("Custom Link <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_custom_link'])) {
|
||||
|
||||
$custom_link_id = intval($_GET['delete_custom_link']);
|
||||
|
||||
// Get Custom Link name and uri for logging
|
||||
$sql = mysqli_query($mysqli,"SELECT custom_link_name, custom_link_uri FROM custom_links WHERE custom_link_id = $custom_link_id");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$custom_link_name = sanitizeInput($row['custom_link_name']);
|
||||
$custom_link_uri = sanitizeInput($row['custom_link_uri']);
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM custom_links WHERE custom_link_id = $custom_link_id");
|
||||
|
||||
logAction("Custom Link", "Delete", "$session_name deleted custom link $custom_link_name -> $custom_link_uri");
|
||||
|
||||
flash_alert("Custom Link <strong>$name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
// Doc Templates
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_document_template'])) {
|
||||
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '', document_template_created_by = $session_user_id");
|
||||
|
||||
$document_template_id = mysqli_insert_id($mysqli);
|
||||
|
||||
$processed_content = mysqli_escape_string(
|
||||
$mysqli,
|
||||
saveBase64Images(
|
||||
$_POST['content'],
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/uploads/document_templates/",
|
||||
"uploads/document_templates/",
|
||||
$document_template_id
|
||||
)
|
||||
);
|
||||
|
||||
// Document template update content
|
||||
mysqli_query($mysqli,"UPDATE document_templates SET document_template_content = '$processed_content' WHERE document_template_id = $document_template_id");
|
||||
|
||||
logAction("Document Template", "Create", "$session_name created document template $name", 0, $document_template_id);
|
||||
|
||||
flash_alert("Document template <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_document_template'])) {
|
||||
|
||||
$document_template_id = intval($_POST['document_template_id']);
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
|
||||
$processed_content = saveBase64Images(
|
||||
$_POST['content'],
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/uploads/document_templates/",
|
||||
"uploads/document_templates/",
|
||||
$document_template_id
|
||||
);
|
||||
|
||||
$processed_content_escaped = mysqli_escape_string($mysqli, $processed_content);
|
||||
|
||||
// CLEAN UP unused images
|
||||
cleanupUnusedImages(
|
||||
$processed_content,
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/uploads/document_templates/" . $document_template_id,
|
||||
"/uploads/document_templates/" . $document_template_id
|
||||
);
|
||||
|
||||
// Document edit query
|
||||
mysqli_query($mysqli,"UPDATE document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '$processed_content_escaped', document_template_updated_by = $session_user_id WHERE document_template_id = $document_template_id");
|
||||
|
||||
logAction("Document Template", "Edit", "$session_name edited document template $name", 0, $document_template_id);
|
||||
|
||||
flash_alert("Document Template <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_document_template'])) {
|
||||
|
||||
$document_template_id = intval($_GET['delete_document_template']);
|
||||
|
||||
$document_template_name = sanitizeInput(getFieldById('document_templates', $document_template_id, 'document_template_name'));
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM document_templates WHERE document_template_id = $document_template_id");
|
||||
|
||||
// Delete uploads/document_templates/$document_template_id if exists
|
||||
removeDirectory($_SERVER['DOCUMENT_ROOT'] . "/uploads/document_templates/" . $document_template_id);
|
||||
|
||||
logAction("Document Template", "Delete", "$session_name deleted document template $document_template_name");
|
||||
|
||||
flash_alert("Document Template <strong>$document_template_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_identity_provider'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$azure_client_id = sanitizeInput($_POST['azure_client_id']);
|
||||
$azure_client_secret = sanitizeInput($_POST['azure_client_secret']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_azure_client_id = '$azure_client_id', config_azure_client_secret = '$azure_client_secret' WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited identity provider settings");
|
||||
|
||||
flash_alert("Identity Provider Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_GET['send_failed_mail'])) {
|
||||
|
||||
$email_id = intval($_GET['send_failed_mail']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 0, email_attempts = 3 WHERE email_id = $email_id");
|
||||
|
||||
logAction("Email", "Send", "$session_name attempted to force send email id: $email_id in the mail queue", 0, $email_id);
|
||||
|
||||
flash_alert("Email Force Sent, give it a minute to resend");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['cancel_mail'])) {
|
||||
|
||||
$email_id = intval($_GET['cancel_mail']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
|
||||
|
||||
logAction("Email", "Send", "$session_name canceled send email id: $email_id in the mail queue", 0, $email_id);
|
||||
|
||||
flash_alert("Email cancelled and marked as failed.", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['bulk_cancel_emails'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
if (isset($_POST['email_ids'])) {
|
||||
|
||||
$count = count($_POST['email_ids']);
|
||||
|
||||
// Cycle through array and mark each email as failed
|
||||
foreach ($_POST['email_ids'] as $email_id) {
|
||||
|
||||
$email_id = intval($email_id);
|
||||
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
|
||||
|
||||
logAction("Email", "Cancel", "$session_name cancelled email id: $email_id in the mail queue", 0, $email_id);
|
||||
|
||||
}
|
||||
|
||||
logAction("Email", "Bulk Cancel", "$session_name cancelled $count email(s) in the mail queue");
|
||||
|
||||
flash_alert("Cancelled <strong>$count</strong> email(s)", 'error');
|
||||
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['bulk_delete_emails'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
if (isset($_POST['email_ids'])) {
|
||||
|
||||
$count = count($_POST['email_ids']);
|
||||
|
||||
// Cycle through array and delete each email
|
||||
foreach ($_POST['email_ids'] as $email_id) {
|
||||
|
||||
$email_id = intval($email_id);
|
||||
mysqli_query($mysqli,"DELETE FROM email_queue WHERE email_id = $email_id");
|
||||
|
||||
logAction("Email", "Delete", "$session_name deleted email id: $email_id from the mail queue");
|
||||
|
||||
}
|
||||
|
||||
logAction("Email", "Bulk Delete", "$session_name deleted $count email(s) from the mail queue");
|
||||
|
||||
flash_alert("Deleted <strong>$count</strong> email(s)", 'error');
|
||||
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_payment_method'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$name = cleanInput($_POST['name']);
|
||||
$description = cleanInput($_POST['description']);
|
||||
|
||||
$query = mysqli_prepare(
|
||||
$mysqli, "INSERT INTO payment_methods
|
||||
SET payment_method_name = ?, payment_method_description = ?"
|
||||
);
|
||||
|
||||
mysqli_stmt_bind_param($query, "ss", $name, $description);
|
||||
|
||||
mysqli_stmt_execute($query);
|
||||
|
||||
logAction("Payment Method", "Create", "$session_name created Payment Method $name");
|
||||
|
||||
flash_alert("Payment Method <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_payment_method'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$payment_method_id = intval($_POST['payment_method_id']);
|
||||
$name = cleanInput($_POST['name']);
|
||||
$description = cleanInput($_POST['description']);
|
||||
|
||||
$query = mysqli_prepare(
|
||||
$mysqli,
|
||||
"UPDATE payment_methods
|
||||
SET payment_method_name = ?, payment_method_description = ?
|
||||
WHERE payment_method_id = ?"
|
||||
);
|
||||
|
||||
mysqli_stmt_bind_param($query, "ssi", $name, $description, $payment_method_id);
|
||||
|
||||
mysqli_stmt_execute($query);
|
||||
|
||||
logAction("Payment Method", "Edit", "$session_name edited Payment Method $name");
|
||||
|
||||
flash_alert("Payment Method <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_payment_method'])) {
|
||||
|
||||
$payment_method_id = intval($_GET['delete_payment_method']);
|
||||
|
||||
$payment_method_name = sanitizeInput(getFieldById('payment_methods', $payment_method_is, 'payment_method_name'));
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM payment_methods WHERE payment_method_id = $payment_method_id");
|
||||
|
||||
logAction("Payment Method", "Delete", "$session_name deleted Payment Method $payment_method_name");
|
||||
|
||||
flash_alert("Payment Method <strong>$payment_method_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_payment_provider'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider = sanitizeInput($_POST['provider']);
|
||||
$public_key = sanitizeInput($_POST['public_key']);
|
||||
$private_key = sanitizeInput($_POST['private_key']);
|
||||
$threshold = floatval($_POST['threshold']);
|
||||
$account = intval($_POST['account']);
|
||||
$expense_vendor = intval($_POST['expense_vendor']) ?? 0;
|
||||
$expense_category = intval($_POST['expense_category']) ?? 0;
|
||||
$percentage_fee = floatval($_POST['percentage_fee']) / 100 ?? 0;
|
||||
$flat_fee = floatval($_POST['flat_fee']) ?? 0;
|
||||
|
||||
// Check to ensure provider isn't added twice
|
||||
$sql = mysqli_query($mysqli, "SELECT 1 FROM payment_providers WHERE payment_provider_name = '$provider' LIMIT 1");
|
||||
if (mysqli_num_rows($sql) > 0) {
|
||||
flash_alert("Payment Provider <strong>$provider</strong> already exists", 'error');
|
||||
redirect();
|
||||
}
|
||||
|
||||
mysqli_query($mysqli,"INSERT INTO payment_providers SET payment_provider_name = '$provider', payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_account = $account, payment_provider_expense_vendor = $expense_vendor, payment_provider_expense_category = $expense_category, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee");
|
||||
|
||||
$provider_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("Payment Provider", "Create", "$session_name created AI Provider $provider");
|
||||
|
||||
flash_alert("Payment provider <strong>$provider</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_payment_provider'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider_id = intval($_POST['provider_id']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
$public_key = sanitizeInput($_POST['public_key']);
|
||||
$private_key = sanitizeInput($_POST['private_key']);
|
||||
$threshold = floatval($_POST['threshold']);
|
||||
$account = intval($_POST['account']);
|
||||
$expense_vendor = intval($_POST['expense_vendor']) ?? 0;
|
||||
$expense_category = intval($_POST['expense_category']) ?? 0;
|
||||
$percentage_fee = floatval($_POST['percentage_fee']) / 100;
|
||||
$flat_fee = floatval($_POST['flat_fee']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE payment_providers SET payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_account = $account, payment_provider_expense_vendor = $expense_vendor, payment_provider_expense_category = $expense_category, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee WHERE payment_provider_id = $provider_id");
|
||||
|
||||
logAction("Payment Provider", "Edit", "$session_name edited Payment Provider $provider");
|
||||
|
||||
flash_alert("Payment Provider <strong>$provider</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_payment_provider'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$provider_id = intval($_GET['delete_payment_provider']);
|
||||
|
||||
// When deleted it cascades deletes
|
||||
// all Recurring paymentes related to payment provider
|
||||
// Delete all Saved Cards related
|
||||
// Delete Client Payment Provider Releation
|
||||
|
||||
$provider_name = sanitizeInput(getFieldById('payment_providers', $provider_id, 'provider_name'));
|
||||
|
||||
// Delete provider
|
||||
mysqli_query($mysqli,"DELETE FROM payment_providers WHERE payment_provider_id = $provider_id");
|
||||
|
||||
logAction("Payment Provider", "Delete", "$session_name deleted Payment Provider $provider_name");
|
||||
|
||||
flash_alert("Payment Provider <strong>$provider_name</strong> deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_project_template'])) {
|
||||
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO project_templates SET project_template_name = '$name', project_template_description = '$description'");
|
||||
|
||||
$project_template_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("Project Template", "Create", "$session_name created project template $name", 0, $project_template_id);
|
||||
|
||||
flash_alert("Project Template <strong>$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_project_template'])) {
|
||||
|
||||
$project_template_id = intval($_POST['project_template_id']);
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$description = sanitizeInput($_POST['description']);
|
||||
|
||||
mysqli_query($mysqli, "UPDATE project_templates SET project_template_name = '$name', project_template_description = '$description' WHERE project_template_id = $project_template_id");
|
||||
|
||||
logAction("Project Template", "Edit", "$session_name edited project template $name", 0, $project_template_id);
|
||||
|
||||
flash_alert("Project Template <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_ticket_template_order'])) {
|
||||
|
||||
$ticket_template_id = intval($_POST['ticket_template_id']);
|
||||
$project_template_id = intval($_POST['project_template_id']);
|
||||
$order = intval($_POST['order']);
|
||||
|
||||
mysqli_query($mysqli, "UPDATE project_template_ticket_templates SET ticket_template_order = $order WHERE ticket_template_id = $ticket_template_id AND project_template_id = $project_template_id");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['add_ticket_template_to_project_template'])) {
|
||||
|
||||
$project_template_id = intval($_POST['project_template_id']);
|
||||
$ticket_template_id = intval($_POST['ticket_template_id']);
|
||||
$order = intval($_POST['order']);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO project_template_ticket_templates SET project_template_id = $project_template_id, ticket_template_id = $ticket_template_id, ticket_template_order = $order");
|
||||
|
||||
logAction("Project Template", "Edit", "$session_name added ticket template to project_template", 0, $project_template_id);
|
||||
|
||||
flash_alert("Ticket template added");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['remove_ticket_template_from_project_template'])) {
|
||||
|
||||
validateTechRole();
|
||||
$ticket_template_id = intval($_POST['ticket_template_id']);
|
||||
$project_template_id = intval($_POST['project_template_id']);
|
||||
|
||||
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id AND ticket_template_id = $ticket_template_id");
|
||||
|
||||
logAction("Project Template", "Edit", "$session_name removed ticket template from project template", 0, $project_template_id);
|
||||
|
||||
flash_alert("Ticket template removed", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_project_template'])) {
|
||||
|
||||
$project_template_id = intval($_GET['delete_project_template']);
|
||||
|
||||
$project_template_name = sanitizeInput(getFieldById('project_templates', $project_template_id, 'project_template_name'));
|
||||
|
||||
mysqli_query($mysqli, "DELETE FROM project_templates WHERE project_template_id = $project_template_id");
|
||||
|
||||
// Remove Associated Ticket Templates
|
||||
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id");
|
||||
|
||||
logAction("Project Template", "Delete", "$session_name deleted project template $project_template_name and its associated ticket templates and tasks");
|
||||
|
||||
flash_alert("Project Template <strong>$project_template_name</strong> and its associated ticket templates and tasks deleted", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - GET/POST request handler for roles
|
||||
*/
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['add_role'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$name = sanitizeInput($_POST['role_name']);
|
||||
$description = sanitizeInput($_POST['role_description']);
|
||||
$admin = intval($_POST['role_is_admin']);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO user_roles SET role_name = '$name', role_description = '$description', role_is_admin = $admin");
|
||||
|
||||
$role_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAction("User Role", "Create", "$session_name created user role $name", 0, $role_id);
|
||||
|
||||
flash_alert("User Role <strong$name</strong> created");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_role'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$role_id = intval($_POST['role_id']);
|
||||
$name = sanitizeInput($_POST['role_name']);
|
||||
$description = sanitizeInput($_POST['role_description']);
|
||||
$admin = intval($_POST['role_is_admin']);
|
||||
|
||||
mysqli_query($mysqli, "UPDATE user_roles SET role_name = '$name', role_description = '$description', role_is_admin = $admin WHERE role_id = $role_id");
|
||||
|
||||
// Update role access levels
|
||||
mysqli_query($mysqli, "DELETE FROM user_role_permissions WHERE user_role_id = $role_id");
|
||||
foreach ($_POST as $key => $value) {
|
||||
if (str_contains($key, '##module_')){
|
||||
$module_id = intval(explode('##', $key)[0]);
|
||||
$access_level = intval($value);
|
||||
|
||||
if ($access_level > 0) {
|
||||
mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = $role_id, module_id = $module_id, user_role_permission_level = $access_level");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logAction("User Role", "Edit", "$session_name edited user role $name", 0, $role_id);
|
||||
|
||||
flash_alert("User Role <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['archive_role'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$role_id = intval($_GET['archive_role']);
|
||||
|
||||
// Check role isn't in use
|
||||
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
|
||||
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
|
||||
if ($role_user_count != 0) {
|
||||
flash_alert("Role must not in use to archive it", 'error');
|
||||
|
||||
redirect();
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE user_roles SET role_archived_at = NOW() WHERE role_id = $role_id");
|
||||
|
||||
$role_name = sanitizeInput(getFieldById('roles', $role_id, 'role_name'));
|
||||
|
||||
logAction("User Role", "Archive", "$session_name archived user role $role_name", 0, $role_id);
|
||||
|
||||
flash_alert("User Role <strong>$role_name</strong> archived", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_GET['delete_saved_payment'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
$saved_payment_id = intval($_GET['delete_saved_payment']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "
|
||||
SELECT
|
||||
client_saved_payment_methods.saved_payment_id,
|
||||
client_saved_payment_methods.saved_payment_client_id,
|
||||
client_saved_payment_methods.saved_payment_provider_id,
|
||||
client_saved_payment_methods.saved_payment_provider_method,
|
||||
client_saved_payment_methods.saved_payment_description,
|
||||
client_payment_provider.payment_provider_client,
|
||||
payment_providers.payment_provider_name,
|
||||
payment_providers.payment_provider_private_key
|
||||
FROM client_saved_payment_methods
|
||||
LEFT JOIN client_payment_provider
|
||||
ON client_payment_provider.client_id = client_saved_payment_methods.saved_payment_client_id
|
||||
AND client_payment_provider.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
|
||||
LEFT JOIN payment_providers
|
||||
ON payment_providers.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
|
||||
WHERE client_saved_payment_methods.saved_payment_id = $saved_payment_id"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$client_id = intval($row['saved_payment_client_id']);
|
||||
$provider_id = intval($row['saved_payment_provider_id']);
|
||||
$payment_provider_name = nullable_htmlentities($row['payment_provider_name']);
|
||||
$saved_payment_description = nullable_htmlentities($row['saved_payment_description']);
|
||||
$provider_client = nullable_htmlentities($row['payment_provider_client']);
|
||||
$payment_method = $row['saved_payment_provider_method'];
|
||||
|
||||
$private_key = $row['payment_provider_private_key'];
|
||||
|
||||
// Separate logic for each Payment Provider
|
||||
if ($payment_provider_name == 'Stripe') {
|
||||
|
||||
try {
|
||||
// Initialize stripe
|
||||
require_once '../plugins/stripe-php/init.php';
|
||||
$stripe = new \Stripe\StripeClient($private_key);
|
||||
|
||||
// Detach PM
|
||||
$stripe->paymentMethods->detach($payment_method, []);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
|
||||
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remove payment method from ITFlow. This will also cascade delete related recurring payments setup
|
||||
mysqli_query($mysqli, "DELETE FROM client_saved_payment_methods WHERE saved_payment_id = $saved_payment_id");
|
||||
|
||||
// SQL Cascade delete will Remove All Associated Auto Payment Methods on recurring invoices in the recurring payments table.
|
||||
|
||||
logAction("Payment Provider", "Update", "$session_name deleted saved payment method $saved_payment_description (PM: $payment_method)", $client_id);
|
||||
|
||||
flash_alert("Payment method <strong>$saved_payment_description</strong> removed", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_ai_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$provider = sanitizeInput($_POST['provider']);
|
||||
if($provider){
|
||||
$ai_enable = 1;
|
||||
} else {
|
||||
$ai_enable = 0;
|
||||
}
|
||||
$model = sanitizeInput($_POST['model']);
|
||||
$url = sanitizeInput($_POST['url']);
|
||||
$api_key = sanitizeInput($_POST['api_key']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_ai_enable = $ai_enable, config_ai_provider = '$provider', config_ai_model = '$model', config_ai_url = '$url', config_ai_api_key = '$api_key' WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited AI settings");
|
||||
|
||||
flash_alert("AI Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_company'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$name = sanitizeInput($_POST['name']);
|
||||
$address = sanitizeInput($_POST['address']);
|
||||
$city = sanitizeInput($_POST['city']);
|
||||
$state = sanitizeInput($_POST['state']);
|
||||
$zip = sanitizeInput($_POST['zip']);
|
||||
$country = sanitizeInput($_POST['country']);
|
||||
$phone_country_code = preg_replace("/[^0-9]/", '',$_POST['phone_country_code']);
|
||||
$phone = preg_replace("/[^0-9]/", '',$_POST['phone']);
|
||||
$email = sanitizeInput($_POST['email']);
|
||||
$website = sanitizeInput($_POST['website']);
|
||||
$tax_id = sanitizeInput($_POST['tax_id']);
|
||||
|
||||
$sql = mysqli_query($mysqli,"SELECT company_logo FROM companies WHERE company_id = 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$existing_file_name = sanitizeInput($row['company_logo']);
|
||||
|
||||
// Company logo
|
||||
if (isset($_FILES['file']['tmp_name'])) {
|
||||
if ($new_file_name = checkFileUpload($_FILES['file'], array('jpg', 'jpeg', 'png'))) {
|
||||
$file_tmp_path = $_FILES['file']['tmp_name'];
|
||||
|
||||
// directory in which the uploaded file will be moved
|
||||
$upload_file_dir = "../uploads/settings/";
|
||||
$dest_path = $upload_file_dir . $new_file_name;
|
||||
|
||||
move_uploaded_file($file_tmp_path, $dest_path);
|
||||
|
||||
// Delete old file
|
||||
unlink("../uploads/settings/$existing_file_name");
|
||||
|
||||
// Set Logo
|
||||
mysqli_query($mysqli,"UPDATE companies SET company_logo = '$new_file_name' WHERE company_id = 1");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mysqli_query($mysqli,"UPDATE companies SET company_name = '$name', company_address = '$address', company_city = '$city', company_state = '$state', company_zip = '$zip', company_country = '$country', company_phone_country_code = '$phone_country_code', company_phone = '$phone', company_email = '$email', company_website = '$website', company_tax_id = '$tax_id' WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited company details");
|
||||
|
||||
flash_alert("Company <strong>$name</strong> edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_GET['remove_company_logo'])) {
|
||||
|
||||
$sql = mysqli_query($mysqli,"SELECT company_logo FROM companies");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$company_logo = $row['company_logo']; // FileSystem Operation Logo is already sanitized
|
||||
|
||||
unlink("../uploads/settings/$company_logo");
|
||||
|
||||
mysqli_query($mysqli,"UPDATE companies SET company_logo = NULL WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name deleted company logo");
|
||||
|
||||
flash_alert("Removed company logo", 'error');
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_default_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$start_page = sanitizeInput($_POST['start_page']);
|
||||
$expense_account = intval($_POST['expense_account']);
|
||||
$payment_account = intval($_POST['payment_account']);
|
||||
$payment_method = sanitizeInput($_POST['payment_method']);
|
||||
$expense_payment_method = sanitizeInput($_POST['expense_payment_method']);
|
||||
$transfer_from_account = intval($_POST['transfer_from_account']);
|
||||
$transfer_to_account = intval($_POST['transfer_to_account']);
|
||||
$calendar = intval($_POST['calendar']);
|
||||
$net_terms = intval($_POST['net_terms']);
|
||||
$hourly_rate = floatval($_POST['hourly_rate']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_start_page = '$start_page', config_default_expense_account = $expense_account, config_default_payment_account = $payment_account, config_default_payment_method = '$payment_method', config_default_expense_payment_method = '$expense_payment_method', config_default_transfer_from_account = $transfer_from_account, config_default_transfer_to_account = $transfer_to_account, config_default_calendar = $calendar, config_default_net_terms = $net_terms, config_default_hourly_rate = $hourly_rate WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited default settings");
|
||||
|
||||
flash_alert("Default settings edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_invoice_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_invoice_prefix = sanitizeInput($_POST['config_invoice_prefix']);
|
||||
$config_invoice_next_number = intval($_POST['config_invoice_next_number']);
|
||||
$config_invoice_footer = sanitizeInput($_POST['config_invoice_footer']);
|
||||
$config_invoice_show_tax_id = intval($_POST['config_invoice_show_tax_id'] ?? 0);
|
||||
$config_invoice_late_fee_enable = intval($_POST['config_invoice_late_fee_enable'] ?? 0);
|
||||
$config_invoice_late_fee_percent = floatval($_POST['config_invoice_late_fee_percent']);
|
||||
$config_recurring_invoice_prefix = sanitizeInput($_POST['config_recurring_invoice_prefix']);
|
||||
$config_recurring_invoice_next_number = intval($_POST['config_recurring_invoice_next_number']);
|
||||
$config_invoice_paid_notification_email = '';
|
||||
if (filter_var($_POST['config_invoice_paid_notification_email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$config_invoice_paid_notification_email = sanitizeInput($_POST['config_invoice_paid_notification_email']);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_invoice_prefix = '$config_invoice_prefix', config_invoice_next_number = $config_invoice_next_number, config_invoice_footer = '$config_invoice_footer', config_invoice_show_tax_id = $config_invoice_show_tax_id, config_invoice_late_fee_enable = $config_invoice_late_fee_enable, config_invoice_late_fee_percent = $config_invoice_late_fee_percent, config_invoice_paid_notification_email = '$config_invoice_paid_notification_email', config_recurring_invoice_prefix = '$config_recurring_invoice_prefix', config_recurring_invoice_next_number = $config_recurring_invoice_next_number WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited invoice settings");
|
||||
|
||||
flash_alert("Invoice Settings edited");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_localization'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$locale = sanitizeInput($_POST['locale']);
|
||||
$currency_code = sanitizeInput($_POST['currency_code']);
|
||||
$timezone = sanitizeInput($_POST['timezone']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE companies SET company_locale = '$locale', company_currency = '$currency_code' WHERE company_id = 1");
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited localization settings");
|
||||
|
||||
flash_alert("Company localization updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_mail_smtp_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_smtp_provider = sanitizeInput($_POST['config_smtp_provider']);
|
||||
$config_smtp_host = sanitizeInput($_POST['config_smtp_host']);
|
||||
$config_smtp_port = intval($_POST['config_smtp_port'] ?? 0);
|
||||
$config_smtp_encryption = sanitizeInput($_POST['config_smtp_encryption']);
|
||||
$config_smtp_username = sanitizeInput($_POST['config_smtp_username']);
|
||||
$config_smtp_password = sanitizeInput($_POST['config_smtp_password']);
|
||||
|
||||
// Shared OAuth fields
|
||||
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
|
||||
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
|
||||
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
|
||||
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
|
||||
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
UPDATE settings SET
|
||||
config_smtp_provider = '$config_smtp_provider',
|
||||
config_smtp_host = '$config_smtp_host',
|
||||
config_smtp_port = $config_smtp_port,
|
||||
config_smtp_encryption = '$config_smtp_encryption',
|
||||
config_smtp_username = '$config_smtp_username',
|
||||
config_smtp_password = '$config_smtp_password',
|
||||
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
|
||||
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
|
||||
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
|
||||
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
|
||||
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
|
||||
WHERE company_id = 1
|
||||
");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited SMTP settings");
|
||||
|
||||
flash_alert("SMTP Mail Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_mail_imap_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_imap_provider = sanitizeInput($_POST['config_imap_provider']);
|
||||
$config_imap_host = sanitizeInput($_POST['config_imap_host']);
|
||||
$config_imap_port = intval($_POST['config_imap_port'] ?? 0);
|
||||
$config_imap_encryption = sanitizeInput($_POST['config_imap_encryption']);
|
||||
$config_imap_username = sanitizeInput($_POST['config_imap_username']);
|
||||
$config_imap_password = sanitizeInput($_POST['config_imap_password']);
|
||||
|
||||
// Shared OAuth fields
|
||||
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
|
||||
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
|
||||
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
|
||||
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
|
||||
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
|
||||
|
||||
mysqli_query($mysqli, "
|
||||
UPDATE settings SET
|
||||
config_imap_provider = '$config_imap_provider',
|
||||
config_imap_host = '$config_imap_host',
|
||||
config_imap_port = $config_imap_port,
|
||||
config_imap_encryption = '$config_imap_encryption',
|
||||
config_imap_username = '$config_imap_username',
|
||||
config_imap_password = '$config_imap_password',
|
||||
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
|
||||
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
|
||||
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
|
||||
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
|
||||
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
|
||||
WHERE company_id = 1
|
||||
");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited IMAP settings");
|
||||
|
||||
flash_alert("IMAP Mail Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_mail_from_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_mail_from_email = sanitizeInput(filter_var($_POST['config_mail_from_email'], FILTER_VALIDATE_EMAIL));
|
||||
$config_mail_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_mail_from_name']));
|
||||
|
||||
$config_invoice_from_email = sanitizeInput(filter_var($_POST['config_invoice_from_email'], FILTER_VALIDATE_EMAIL));
|
||||
$config_invoice_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_invoice_from_name']));
|
||||
|
||||
$config_quote_from_email = sanitizeInput(filter_var($_POST['config_quote_from_email'], FILTER_VALIDATE_EMAIL));
|
||||
$config_quote_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_quote_from_name']));
|
||||
|
||||
$config_ticket_from_email = sanitizeInput(filter_var($_POST['config_ticket_from_email'], FILTER_VALIDATE_EMAIL));
|
||||
$config_ticket_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_ticket_from_name']));
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_mail_from_email = '$config_mail_from_email', config_mail_from_name = '$config_mail_from_name', config_invoice_from_email = '$config_invoice_from_email', config_invoice_from_name = '$config_invoice_from_name', config_quote_from_email = '$config_quote_from_email', config_quote_from_name = '$config_quote_from_name', config_ticket_from_email = '$config_ticket_from_email', config_ticket_from_name = '$config_ticket_from_name' WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited mail from settings");
|
||||
|
||||
flash_alert("Mail From Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['test_email_smtp'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$test_email = intval($_POST['test_email']);
|
||||
|
||||
if($test_email == 1) {
|
||||
$email_from = sanitizeInput($config_mail_from_email);
|
||||
$email_from_name = sanitizeInput($config_mail_from_name);
|
||||
} elseif ($test_email == 2) {
|
||||
$email_from = sanitizeInput($config_invoice_from_email);
|
||||
$email_from_name = sanitizeInput($config_invoice_from_name);
|
||||
} elseif ($test_email == 3) {
|
||||
$email_from = sanitizeInput($config_quote_from_email);
|
||||
$email_from_name = sanitizeInput($config_quote_from_name);
|
||||
} else {
|
||||
$email_from = sanitizeInput($config_ticket_from_email);
|
||||
$email_from_name = sanitizeInput($config_ticket_from_name);
|
||||
}
|
||||
|
||||
$email_to = sanitizeInput($_POST['email_to']);
|
||||
$subject = "Test email from ITFlow";
|
||||
$body = "This is a test email from ITFlow. If you are reading this, it worked!";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $email_from,
|
||||
'from_name' => $email_from_name,
|
||||
'recipient' => $email_to,
|
||||
'recipient_name' => 'Chap',
|
||||
'subject' => $subject,
|
||||
'body' => $body
|
||||
]
|
||||
];
|
||||
|
||||
$mail = addToMailQueue($data);
|
||||
|
||||
if ($mail === true) {
|
||||
flash_alert("Test email queued! <a class='text-bold text-light' href='mail_queue.php'>Check Admin > Mail queue</a>");
|
||||
} else {
|
||||
flash_alert("Failed to add test mail to queue", 'error');
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['test_email_imap'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$host = $config_imap_host;
|
||||
$port = (int) $config_imap_port;
|
||||
$encryption = strtolower(trim($config_imap_encryption)); // e.g. "ssl", "tls", "none"
|
||||
$username = $config_imap_username;
|
||||
$password = $config_imap_password;
|
||||
|
||||
// Build remote socket (implicit SSL vs plain TCP)
|
||||
$transport = 'tcp';
|
||||
if ($encryption === 'ssl') {
|
||||
$transport = 'ssl';
|
||||
}
|
||||
|
||||
$remote_socket = $transport . '://' . $host . ':' . $port;
|
||||
|
||||
// Stream context (you can tighten these if you want strict validation)
|
||||
$contextOptions = [];
|
||||
if (in_array($encryption, ['ssl', 'tls'], true)) {
|
||||
$contextOptions['ssl'] = [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
'allow_self_signed' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$context = stream_context_create($contextOptions);
|
||||
|
||||
try {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
|
||||
// 10-second timeout, adjust as needed
|
||||
$fp = @stream_socket_client(
|
||||
$remote_socket,
|
||||
$errno,
|
||||
$errstr,
|
||||
10,
|
||||
STREAM_CLIENT_CONNECT,
|
||||
$context
|
||||
);
|
||||
|
||||
if (!$fp) {
|
||||
throw new Exception("Could not connect to IMAP server: [$errno] $errstr");
|
||||
}
|
||||
|
||||
stream_set_timeout($fp, 10);
|
||||
|
||||
// Read server greeting (IMAP servers send something like: * OK Dovecot ready)
|
||||
$greeting = fgets($fp, 1024);
|
||||
if ($greeting === false || strpos($greeting, '* OK') !== 0) {
|
||||
fclose($fp);
|
||||
throw new Exception("Invalid IMAP greeting: " . trim((string) $greeting));
|
||||
}
|
||||
|
||||
// If you really want STARTTLS for "tls" (port 143), you can do it here
|
||||
if ($encryption === 'tls' && stripos($greeting, 'STARTTLS') !== false) {
|
||||
// Request STARTTLS
|
||||
fwrite($fp, "A0001 STARTTLS\r\n");
|
||||
$line = fgets($fp, 1024);
|
||||
if ($line === false || stripos($line, 'A0001 OK') !== 0) {
|
||||
fclose($fp);
|
||||
throw new Exception("STARTTLS failed: " . trim((string) $line));
|
||||
}
|
||||
|
||||
// Enable crypto on the stream
|
||||
if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
|
||||
fclose($fp);
|
||||
throw new Exception("Unable to enable TLS encryption on IMAP connection.");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Do LOGIN command ---
|
||||
$tag = 'A0002';
|
||||
|
||||
// Simple quoting; this may fail with some special chars in username/password.
|
||||
$loginCmd = sprintf(
|
||||
"%s LOGIN \"%s\" \"%s\"\r\n",
|
||||
$tag,
|
||||
addcslashes($username, "\\\""),
|
||||
addcslashes($password, "\\\"")
|
||||
);
|
||||
|
||||
fwrite($fp, $loginCmd);
|
||||
|
||||
$success = false;
|
||||
$errorLine = '';
|
||||
|
||||
while (!feof($fp)) {
|
||||
$line = fgets($fp, 2048);
|
||||
if ($line === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for tagged response for our LOGIN
|
||||
if (strpos($line, $tag . ' ') === 0) {
|
||||
if (stripos($line, $tag . ' OK') === 0) {
|
||||
$success = true;
|
||||
} else {
|
||||
$errorLine = trim($line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Always logout / close
|
||||
fwrite($fp, "A0003 LOGOUT\r\n");
|
||||
fclose($fp);
|
||||
|
||||
if ($success) {
|
||||
flash_alert("Connected successfully");
|
||||
} else {
|
||||
if (!$errorLine) {
|
||||
$errorLine = 'Unknown IMAP authentication error';
|
||||
}
|
||||
throw new Exception($errorLine);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
flash_alert("<strong>IMAP connection failed:</strong> " . htmlspecialchars($e->getMessage()), 'error');
|
||||
}
|
||||
|
||||
redirect();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_module_settings'])) {
|
||||
|
||||
$config_module_enable_itdoc = intval($_POST['config_module_enable_itdoc'] ?? 0);
|
||||
$config_module_enable_ticketing = intval($_POST['config_module_enable_ticketing'] ?? 0);
|
||||
$config_module_enable_accounting = intval($_POST['config_module_enable_accounting'] ?? 0);
|
||||
$config_client_portal_enable = intval($_POST['config_client_portal_enable'] ?? 0);
|
||||
$config_whitelabel_key = sanitizeInput($_POST['config_whitelabel_key']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_module_enable_itdoc = $config_module_enable_itdoc, config_module_enable_ticketing = $config_module_enable_ticketing, config_module_enable_accounting = $config_module_enable_accounting, config_client_portal_enable = $config_client_portal_enable WHERE company_id = 1");
|
||||
|
||||
// Validate white label key
|
||||
if (!empty($config_whitelabel_key && validateWhitelabelKey($config_whitelabel_key))) {
|
||||
mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 1, config_whitelabel_key = '$config_whitelabel_key' WHERE company_id = 1");
|
||||
} else {
|
||||
mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 0, config_whitelabel_key = '' WHERE company_id = 1");
|
||||
}
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited module settings");
|
||||
|
||||
flash_alert("Module Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_notification_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_enable_cron = intval($_POST['config_enable_cron'] ?? 0);
|
||||
$config_enable_alert_domain_expire = intval($_POST['config_enable_alert_domain_expire'] ?? 0);
|
||||
$config_send_invoice_reminders = intval($_POST['config_send_invoice_reminders'] ?? 0);
|
||||
$config_recurring_auto_send_invoice = intval($_POST['config_recurring_auto_send_invoice'] ?? 0);
|
||||
$config_ticket_client_general_notifications = intval($_POST['config_ticket_client_general_notifications'] ?? 0);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_send_invoice_reminders = $config_send_invoice_reminders, config_recurring_auto_send_invoice = $config_recurring_auto_send_invoice, config_enable_cron = $config_enable_cron, config_enable_alert_domain_expire = $config_enable_alert_domain_expire, config_ticket_client_general_notifications = $config_ticket_client_general_notifications WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited notification settings");
|
||||
|
||||
flash_alert("Notification Settings updated");
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_POST['edit_online_payment_settings'])) {
|
||||
|
||||
validateCSRFToken($_POST['csrf_token']);
|
||||
|
||||
$config_stripe_enable = intval($_POST['config_stripe_enable'] ?? 0);
|
||||
$config_stripe_publishable = sanitizeInput($_POST['config_stripe_publishable']);
|
||||
$config_stripe_secret = sanitizeInput($_POST['config_stripe_secret']);
|
||||
$config_stripe_account = intval($_POST['config_stripe_account']);
|
||||
$config_stripe_expense_vendor = intval($_POST['config_stripe_expense_vendor']);
|
||||
$config_stripe_expense_category = intval($_POST['config_stripe_expense_category']);
|
||||
$config_stripe_percentage_fee = floatval($_POST['config_stripe_percentage_fee']) / 100;
|
||||
$config_stripe_flat_fee = floatval($_POST['config_stripe_flat_fee']);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE settings SET config_stripe_enable = $config_stripe_enable, config_stripe_publishable = '$config_stripe_publishable', config_stripe_secret = '$config_stripe_secret', config_stripe_account = $config_stripe_account, config_stripe_expense_vendor = $config_stripe_expense_vendor, config_stripe_expense_category = $config_stripe_expense_category, config_stripe_percentage_fee = $config_stripe_percentage_fee, config_stripe_flat_fee = $config_stripe_flat_fee WHERE company_id = 1");
|
||||
|
||||
logAction("Settings", "Edit", "$session_name edited online payment settings");
|
||||
|
||||
if ($config_stripe_enable && $config_stripe_account == 0) {
|
||||
flash_alert("Stripe payment account must be specified!", 'error');
|
||||
} else {
|
||||
flash_alert("Online Payment Settings updated");
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue