Skip to content

Commit 1209b20

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into css-more-cleanup
2 parents 617d7ef + c1b98e3 commit 1209b20

File tree

7 files changed

+171
-6
lines changed

7 files changed

+171
-6
lines changed

src/components/Header.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { availableHotkeys } from "../configs/hotkeysConfig";
1414
import { studioURL } from "../configs/generalConfig";
1515
import { hasAccess } from "../utils/utils";
1616
import RegistrationModal from "./shared/RegistrationModal";
17+
import TermsOfUseModal from "./shared/TermsOfUseModal";
1718
import HotKeyCheatSheet from "./shared/HotKeyCheatSheet";
1819
import { useHotkeys } from "react-hotkeys-hook";
1920
import { useAppDispatch, useAppSelector } from "../store";
@@ -51,6 +52,7 @@ const Header = () => {
5152
const errorCounter = useAppSelector(state => getErrorCount(state));
5253
const user = useAppSelector(state => getUserInformation(state));
5354
const orgProperties = useAppSelector(state => getOrgProperties(state));
55+
const displayTerms = (orgProperties["org.opencastproject.admin.display_terms"] || "false").toLowerCase() === "true";
5456

5557
const loadHealthStatus = async () => {
5658
await dispatch(fetchHealthStatus());
@@ -265,6 +267,9 @@ const Header = () => {
265267
{/* Adopters Registration Modal */}
266268
<RegistrationModal modalRef={registrationModalRef}/>
267269

270+
{/* Terms of use for all non-admin users */}
271+
{displayTerms && !user.roles.includes("ROLE_ADMIN") && <TermsOfUseModal />}
272+
268273
{/* Hotkey Cheat Sheet */}
269274
<HotKeyCheatSheet modalRef={hotKeyCheatSheetModalRef}/>
270275
</>

src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ const EventDetailsSchedulingTab = ({
9292

9393
const sourceStartDate = new Date(source.start.date);
9494
const endStartDate = new Date(source.start.date);
95-
9695
useEffect(() => {
9796
dispatch(removeNotificationWizardForm());
9897
dispatch(checkConflicts({
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { useEffect, useState } from "react";
2+
import { useTranslation } from "react-i18next";
3+
import { Field, Formik } from "formik";
4+
import cn from "classnames";
5+
import axios from "axios";
6+
import i18n from "../../i18n/i18n";
7+
import DOMPurify from "dompurify";
8+
9+
// Generate URL for terms based on the languae
10+
const getURL = (language: string) => {
11+
return `/ui/config/admin-ui/terms.${language}.html`;
12+
};
13+
14+
const TermsOfUseModal = () => {
15+
const { t } = useTranslation();
16+
const [termsContent, setTermsContent] = useState("");
17+
const [agreedToTerms, setAgreedToTerms] = useState(true);
18+
19+
// Check if already accepted terms
20+
useEffect(() => {
21+
const checkTerms = async () => {
22+
try {
23+
type FetchUserSettings = {
24+
total: number,
25+
offset: number,
26+
limit: number,
27+
results: {
28+
id: number,
29+
value: string,
30+
key: string
31+
}[]
32+
};
33+
const response = await axios.get<FetchUserSettings>("/admin-ng/user-settings/settings.json");
34+
const agreedResult = response.data.results.find(result => result.key === "agreedToTerms");
35+
const isAgreed = agreedResult?.value === "true";
36+
setAgreedToTerms(isAgreed);
37+
} catch (error) {
38+
console.error("Error while retrieving data: ", error);
39+
setAgreedToTerms(false);
40+
}
41+
};
42+
43+
checkTerms();
44+
}, []);
45+
46+
// Fetch terms
47+
useEffect(() => {
48+
axios.get<string>(getURL(i18n.language))
49+
.then(response => {
50+
setTermsContent(response.data);
51+
})
52+
.catch(() => {
53+
axios.get<string>(getURL(typeof i18n.options.fallbackLng === "string" ? i18n.options.fallbackLng : "en-US"))
54+
.then(response => {
55+
setTermsContent(response.data);
56+
})
57+
.catch(error => {
58+
console.error("Error while fetching data:", error);
59+
setTermsContent(t("TERMS.NOCONTENT"));
60+
});
61+
});
62+
// eslint-disable-next-line react-hooks/exhaustive-deps
63+
}, [agreedToTerms]); // Listen to changes in agreedToTerms
64+
65+
// Set terms to user settings
66+
const handleSubmit = async (values: {agreedToTerms: boolean}) => {
67+
const body = new URLSearchParams();
68+
body.append("key", "agreedToTerms");
69+
body.append("value", values.agreedToTerms ? "true" : "false");
70+
71+
await axios.post("/admin-ng/user-settings/setting", body, {
72+
headers: {
73+
"Content-Type": "application/x-www-form-urlencoded",
74+
},
75+
});
76+
setAgreedToTerms(true);
77+
};
78+
79+
// If already accepted terms, dont display anything
80+
if (agreedToTerms) {
81+
return null;
82+
}
83+
84+
// Else display terms
85+
return (
86+
<>
87+
<div className="modal-animation modal-overlay" />
88+
<section id="registration-modal" className="modal active modal-open modal-animation">
89+
<header>
90+
<h2>{t("TERMS.TITLE")}</h2>
91+
</header>
92+
93+
<div className="modal-content" style={{ display: "block" }}>
94+
<div className="modal-body">
95+
<div>
96+
<div className="row">
97+
<div className="scrollbox">
98+
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(termsContent) }} ></div>
99+
</div>
100+
</div>
101+
</div>
102+
</div>
103+
</div>
104+
105+
<Formik
106+
initialValues={{ agreedToTerms: false }}
107+
enableReinitialize
108+
onSubmit={handleSubmit}
109+
>
110+
{formik => (<>
111+
<div className="modal-content" style={{ display: "block" }}>
112+
<div className="modal-body">
113+
<div>
114+
<fieldset>
115+
<legend>{t("TERMS.TITLE")}</legend>
116+
<div className="form-group form-group-checkbox">
117+
<Field
118+
type="checkbox"
119+
name="agreedToTerms"
120+
id="agreedToTerms"
121+
className="form-control"
122+
/>
123+
<label htmlFor="agreedToTerms">
124+
<span>{t("TERMS.AGREE")}</span>
125+
</label>
126+
</div>
127+
</fieldset>
128+
</div>
129+
</div>
130+
</div>
131+
132+
<footer>
133+
<div className="pull-right">
134+
<button
135+
disabled={
136+
!(formik.isValid && formik.values.agreedToTerms)
137+
}
138+
onClick={() => formik.handleSubmit()}
139+
className={cn("submit", {
140+
active:
141+
formik.isValid && formik.values.agreedToTerms,
142+
inactive: !(
143+
formik.isValid && formik.values.agreedToTerms
144+
),
145+
})}
146+
>
147+
{t("SUBMIT")}
148+
</button>
149+
</div>
150+
</footer>
151+
</>)}
152+
</Formik>
153+
</section>
154+
</>
155+
);
156+
};
157+
158+
export default TermsOfUseModal;

src/i18n/org/opencastproject/adminui/languages/lang-en_US.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,5 +2155,10 @@
21552155
"IMPRINT": "Imprint",
21562156
"PRIVACY": "Privacy statement",
21572157
"NOCONTENT": "Content not available"
2158+
},
2159+
"TERMS": {
2160+
"TITLE": "Terms of use",
2161+
"NOCONTENT": "Content not available",
2162+
"AGREE": "I have read and agree to the terms of use"
21582163
}
21592164
}

src/slices/eventDetailsSlice.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,6 @@ export const saveSchedulingInfo = createAppAsyncThunk("eventDetails/saveScheduli
11111111
endDate: Date
11121112
}, { dispatch, getState }) => {
11131113
const { eventId, values, startDate, endDate } = params;
1114-
11151114
const state = getState();
11161115
const oldSource = getSchedulingSource(state);
11171116
const captureAgents = getRecordings(state);

src/slices/eventSlice.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ export const postNewEvent = (params: {
437437
values: {
438438
policies: TransformedAcl[],
439439
configuration: { [key: string]: unknown },
440-
deviceInputs?: string[],
440+
inputs?: string[],
441441
processingWorkflow: string,
442442
repeatOn: string[],
443443
scheduleDurationHours: string,
@@ -456,7 +456,6 @@ export const postNewEvent = (params: {
456456
extendedMetadata: MetadataCatalog[],
457457
}): AppThunk => (dispatch, getState) => {
458458
const { values, metadataInfo, extendedMetadata } = params;
459-
460459
// get asset upload options from redux store
461460
const state = getState();
462461
const uploadAssetOptions = getAssetUploadOptions(state);
@@ -544,7 +543,7 @@ export const postNewEvent = (params: {
544543
metadata: {
545544
start: startDate,
546545
device: values.location,
547-
inputs: values.deviceInputs ? values.deviceInputs.join(",") : "",
546+
inputs: values.inputs ? values.inputs.join(",") : "",
548547
end: endDate,
549548
duration: duration.toString(),
550549
},

src/styles/views/modals/_registration.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@
208208

209209
footer {
210210
button {
211-
margin: 15px 5px 15px 25px;
211+
margin: 10px 5px 0px 25px;
212212
&.inactive {
213213
opacity: 0.5;
214214
cursor: default;

0 commit comments

Comments
 (0)