diff --git a/assets/js/Components/Admin/AdminApp.js b/assets/js/Components/Admin/AdminApp.js index 56aa65e59..17eb749a1 100644 --- a/assets/js/Components/Admin/AdminApp.js +++ b/assets/js/Components/Admin/AdminApp.js @@ -14,21 +14,26 @@ import '../../../css/udoit4-theme.css' export default function AdminApp(initialData) { // If there are multiple accounts available, the first account is the selected accountId - let accountId = initialData.settings?.accountId - if(initialData.settings?.accounts) { - const accountIds = Object.keys(initialData.settings.accounts) + let accountId = initialData.accountId + if(initialData.accounts) { + const accountIds = Object.keys(initialData.accounts) accountId = accountIds.shift() } let initialFilters = { accountId: accountId, - termId: initialData.settings.defaultTerm, + termId: initialData.termInfo.defaultTerm, includeSubaccounts: true, courseId: null } const [messages, setMessages] = useState(initialData.messages || []) - const [settings, setSettings] = useState(initialData.settings || null) + const [preferences, setPreferences] = useState(initialData.preferences ?? {}) + const [instanceInfo, setInstanceInfo] = useState(initialData.instanceInfo ?? {}) + const [termInfo, setTermInfo] = useState(initialData.termInfo || {}) + const [labels, setLabels] = useState(initialData.labels ?? []) + const [accounts, setAccounts] = useState(initialData.accounts) + const [courses, setCourses] = useState({}) const [selectedCourse, setSelectedCourse] = useState(null) const [filters, setFilters] = useState({...initialFilters}) @@ -40,13 +45,13 @@ export default function AdminApp(initialData) { const [trayOpen, setTrayOpen] = useState(false) const t = useCallback((key) => { - return (settings.labels[key]) ? settings.labels[key] : key - }, [settings.labels]) + return (labels[key]) ? labels[key] : key + }, [labels]) const loadCourses = (filters) => { setLoadingCourses(true) - const api = new Api(settings) + const api = new Api(instanceInfo) api.getAdminCourses(filters) .then((response) => response.json()) .then((data) => { @@ -121,10 +126,9 @@ export default function AdminApp(initialData) { return (
+ className={`flex-column flex-grow-1 ${preferences.fontSize || 'font-medium'} ${preferences.fontSize || 'sans-serif'} ${preferences.darkMode ? 'dark-mode' : ''}`}> @@ -133,7 +137,9 @@ export default function AdminApp(initialData) { { (navigation !== 'reports' && navigation !== 'dashboard') && @@ -188,7 +194,7 @@ export default function AdminApp(initialData) { {('users' === navigation) && { - switch (settings?.user?.roles?.font_size) { + switch (preferences.fontSize) { case 'font-small': return 40; case 'font-normal': diff --git a/assets/js/Components/Admin/AdminFilters.js b/assets/js/Components/Admin/AdminFilters.js index f2efb8b92..ff7a7e12a 100644 --- a/assets/js/Components/Admin/AdminFilters.js +++ b/assets/js/Components/Admin/AdminFilters.js @@ -5,7 +5,9 @@ import '../Widgets/FixIssuesFilters.css' export default function AdminFilters({ t, - settings, + preferences, + accounts, + termInfo, filters, handleFilter, loadingContent, @@ -16,26 +18,31 @@ export default function AdminFilters({ const [accountOptions, setAccountOptions] = useState([]) const [termOptions, setTermOptions] = useState([]) - // When the "settings" are loaded, create the Account and Term dropdown options + // When "termInfo" and "accounts" are loaded, create the Account and Term dropdown options useEffect(() => { - let tempAccountOptions = [] - for (const acct of Object.values(settings.accounts)) { - tempAccountOptions.push({ - id: acct.id, - name: acct.name - }) + if (accounts) { + let tempAccountOptions = [] + for (const acct of Object.values(accounts)) { + tempAccountOptions.push({ + id: acct.id, + name: acct.name + }) + } + + setAccountOptions(tempAccountOptions) } - setAccountOptions(tempAccountOptions) - let tempTermOptions = [] - for (const [key, val] of Object.entries(settings.terms)) { - tempTermOptions.push({ - id: key, - name: val, - }) + if (termInfo) { + let tempTermOptions = [] + for (const [key, val] of Object.entries(termInfo.terms)) { + tempTermOptions.push({ + id: key, + name: val, + }) + } + setTermOptions(tempTermOptions) } - setTermOptions(tempTermOptions) - }, [settings]) + }, [termInfo, accounts]) const handleAccountSelect = (newValue) => { handleFilter({ accountId: newValue }) diff --git a/assets/js/Components/Admin/AdminHeader.js b/assets/js/Components/Admin/AdminHeader.js index 65fd75039..53f94a42d 100644 --- a/assets/js/Components/Admin/AdminHeader.js +++ b/assets/js/Components/Admin/AdminHeader.js @@ -7,7 +7,6 @@ import '../Header.css' export default function AdminHeader({ t, - settings, navigation, handleNavigation }) { diff --git a/assets/js/Components/Admin/CoursesPage.js b/assets/js/Components/Admin/CoursesPage.js index a4fb3b5e6..db90bd062 100644 --- a/assets/js/Components/Admin/CoursesPage.js +++ b/assets/js/Components/Admin/CoursesPage.js @@ -6,7 +6,7 @@ import ReportIcon from '../Icons/ReportIcon' export default function CoursePage({ t, - settings, + instanceInfo, courses, searchTerm, handleCourseUpdate, @@ -163,7 +163,7 @@ export default function CoursePage({ } const handleScanClick = (course) => { - let api = new Api(settings) + let api = new Api(instanceInfo) setIsAnyScanning(true) // For unscanned courses, course.id will be the LMS course ID (string/number) @@ -283,7 +283,7 @@ export default function CoursePage({ const checkForReport = (course) => { const newReportInterval = 5000 - let api = new Api(settings) + let api = new Api(instanceInfo) const courseId = course.udoitId || course.id const originalId = course.originalId // Save the original ID for removal diff --git a/assets/js/Components/Admin/ReportsPage.js b/assets/js/Components/Admin/ReportsPage.js index 2c003be82..1f61221dd 100644 --- a/assets/js/Components/Admin/ReportsPage.js +++ b/assets/js/Components/Admin/ReportsPage.js @@ -7,10 +7,11 @@ import IssuesTable from '../Reports/IssuesTable' import ProgressIcon from '../Icons/ProgressIcon' import '../ReportsPage.css' import { analyzeReport } from '../../Services/Report' +import { ISSUE_STATE } from '../../Services/Constants' export default function ReportsPage({ t, - settings, + instanceInfo, filters, selectedCourse }) { @@ -18,17 +19,8 @@ const [groupedReports, setGroupedReports] = useState(null) const [issues, setIssues] = useState(null) const [instructors, setInstructors] = useState([]) -const ISSUE_STATE = { - UNCHANGED: 0, - SAVING: 1, - RESOLVING: 2, - SAVED: 3, - RESOLVED: 4, - ERROR: 5, -} - const getReportHistory = () => { - const api = new Api(settings); + const api = new Api(instanceInfo); api.getAdminReportHistory(filters) .then((responseStr) => responseStr.json()) .then((response) => { diff --git a/assets/js/Components/Admin/UsersPage.js b/assets/js/Components/Admin/UsersPage.js index 96f8d4b7b..c60d9788b 100644 --- a/assets/js/Components/Admin/UsersPage.js +++ b/assets/js/Components/Admin/UsersPage.js @@ -5,7 +5,7 @@ import ProgressIcon from '../Icons/ProgressIcon' export default function UsersPage({ t, - settings, + instanceInfo, accountId, searchTerm, termId @@ -30,7 +30,7 @@ export default function UsersPage({ }) const getUsers = () => { - const api = new Api(settings) + const api = new Api(instanceInfo) api.getAdminUser() .then((responseStr) => responseStr.json()) .then((response) => { @@ -46,20 +46,20 @@ export default function UsersPage({ }, []) const handleUserDeauthorize = (user) => { - const api = new Api(settings) + const api = new Api(instanceInfo) user.hasApiKey = false - api.updateUser(user) - .then((responseStr) => responseStr.json()) - .then((response) => { - let tempUsers = Object.assign({}, users) - if (response && response.id) { - const ind = users.findIndex((el) => { el.id === response.id }) - tempUsers[ind] = response - setUsers(tempUsers) - } - }) + // api.updateUser(user) + // .then((responseStr) => responseStr.json()) + // .then((response) => { + // let tempUsers = Object.assign({}, users) + // if (response && response.id) { + // const ind = users.findIndex((el) => { el.id === response.id }) + // tempUsers[ind] = response + // setUsers(tempUsers) + // } + // }) } const handleTableSettings = (setting) => { diff --git a/assets/js/Components/App.js b/assets/js/Components/App.js index cc14fe1ea..d29cc0844 100644 --- a/assets/js/Components/App.js +++ b/assets/js/Components/App.js @@ -11,7 +11,8 @@ import SettingsPage from './SettingsPage' import Api from '../Services/Api' import MessageTray from './Widgets/MessageTray' import { analyzeReport } from '../Services/Report' -import { ISSUE_STATE, WIDGET_STATE, ISSUE_FILTER, FILE_FILTER, FILE_TYPES, FILE_TYPE_MAP, DEFAULT_USER_SETTINGS, UFIXIT_OPTIONS} from '../Services/Settings' +import { DEFAULT_USER_SETTINGS } from '../Services/Settings' +import { ISSUE_STATE } from '../Services/Constants' export default function App(initialData) { @@ -19,18 +20,11 @@ export default function App(initialData) { const [nextMessage, setNextMessage] = useState('') const [untranslatedMessage, setUntranslatedMessage] = useState('') const [report, setReport] = useState(initialData.report || null) - const [settings, setSettings] = useState(Object.assign({}, - initialData?.settings || {}, - { ISSUE_STATE }, - { WIDGET_STATE }, - { ISSUE_FILTER }, - { FILE_FILTER }, - { FILE_TYPES }, - { FILE_TYPE_MAP }, - { DEFAULT_USER_SETTINGS }, - { UFIXIT_OPTIONS }, - )) - const [textSpacing, setTextSpacing] = useState(settings?.user?.roles && ('text_spacing' in settings.user.roles) ? settings.user.roles.text_spacing: settings.DEFAULT_USER_SETTINGS.TEXT_SPACING) + const [labels, setLabels] = useState(initialData.labels ?? []) + const [instanceInfo, setInstanceInfo] = useState(initialData.instanceInfo ?? {}) + const [preferences, setPreferences] = useState(initialData.preferences ?? {}) + const [formOptions, setFormOptions] = useState(initialData.formOptions ?? {}) + const [textSpacing, setTextSpacing] = useState(preferences.textSpacing ?? DEFAULT_USER_SETTINGS.TEXT_SPACING) const [sections, setSections] = useState([]) const [navigation, setNavigation] = useState('summary') @@ -45,9 +39,9 @@ export default function App(initialData) { const [modalActive, setModalActive] = useState(false) // `t` is used for text/translation. It will return the translated string if it exists - // in the settings.labels object. + // in the labels object. const t = useCallback((key, values = {}) => { - let translatedText = (settings.labels[key] && settings.labels[key] !== '') ? settings.labels[key] : key + let translatedText = (labels[key] && labels[key] !== '') ? labels[key] : key if (values && Object.keys(values).length > 0) { Object.keys(values).forEach((key) => { translatedText = translatedText.replace(`{${key}}`, values[key]) @@ -55,16 +49,16 @@ export default function App(initialData) { } return translatedText - }, [settings]) + }, [labels]) const scanCourse = useCallback(() => { - let api = new Api(settings) - return api.scanCourse(settings.course.id) + let api = new Api(instanceInfo) + return api.scanCourse(instanceInfo.course.id) }, []) const fullRescan = useCallback(() => { - let api = new Api(settings) - return api.fullRescan(settings.course.id) + let api = new Api(instanceInfo) + return api.fullRescan(instanceInfo.course.id) }, []) // When user settings are updated and the language changes, we need to send alerts, but also wait a tick for the settings to update. @@ -88,29 +82,23 @@ export default function App(initialData) { } }, [untranslatedMessage]) - const updateUserSettings = (newUserSetting) => { - let oldSettings = JSON.parse(JSON.stringify(settings)) - let newRoles = Object.assign({}, settings.user.roles, newUserSetting) - let newUser = Object.assign({}, settings.user, { 'roles': newRoles}) - let newSettings = Object.assign({}, settings, { user: newUser }) - setSettings(newSettings) + const updateUserPreferences = (newUserPreferences) => { + const oldPreferences = structuredClone(preferences) + setPreferences(old => ({...old, ...newUserPreferences})) - let api = new Api(settings) - api.updateUser(newUser) + let api = new Api(instanceInfo) + api.updatePreferences(newUserPreferences) .then((response) => response.json()) .then((data) => { if(data.user) { - newSettings.user = data.user - if(data?.labels?.lang) { - let newLanguageSettings = Object.assign({}, newSettings) - newLanguageSettings.lang = data?.language || newSettings.lang - newLanguageSettings.labels = data.labels - setSettings(newLanguageSettings) + instanceInfo.user = data.user + if(data?.labels) { + setLabels(data.labels) } // setUntranslatedMessage({ message: 'msg.settings.updated', severity: 'success', visible: true }) } else { - setSettings(oldSettings) + setPreferences(oldPreferences) setUntranslatedMessage({ message: 'msg.settings.update_failed', severity: 'error', visible: true }) } }) @@ -121,7 +109,7 @@ export default function App(initialData) { // Each issue has an id and state: { id: issueId, state: 2 } // The valid states are set and read in the FixIssuesPage component. const updateSessionIssue = (issueId, issueState = null, contentItemId = null) => { - if(issueState === null || issueState === settings.ISSUE_STATE.UNCHANGED) { + if(issueState === null || issueState === ISSUE_STATE.UNCHANGED) { let newSessionIssues = Object.assign({}, sessionIssues) if(newSessionIssues[issueId]) { delete newSessionIssues[issueId] @@ -139,7 +127,7 @@ export default function App(initialData) { } const updateSessionFiles = (fileId, fileState = null, contentItemId = null) => { - if(fileState === null || fileState === settings.ISSUE_STATE.UNCHANGED) { + if(fileState === null || fileState === ISSUE_STATE.UNCHANGED) { let newSessionFiles = Object.assign({}, sessionFiles) if(newSessionFiles[fileId]) { delete newSessionFiles[fileId] @@ -157,10 +145,10 @@ export default function App(initialData) { } const processNewReport = (rawReport) => { - const tempReport = analyzeReport(rawReport, settings.ISSUE_STATE) + const tempReport = analyzeReport(rawReport, ISSUE_STATE) setReport(tempReport) - let api = new Api(settings) + let api = new Api(instanceInfo) api.setReportData(tempReport.id, {'scanCounts': tempReport.scanCounts, 'scanRules': tempReport.scanRules}) .then((response) => response.json()) .then((data) => { @@ -370,21 +358,22 @@ export default function App(initialData) {
+ + `${preferences.fontSize || DEFAULT_USER_SETTINGS.FONT_SIZE} ` + + `${preferences.fontFamily || DEFAULT_USER_SETTINGS.FONT_FAMILY} ` + + `${preferences.darkMode ? 'dark-mode' : ''}`} + lang={preferences.lang || DEFAULT_USER_SETTINGS.LANGUAGE}> { !welcomeClosed ? ( ) : ( <>
@@ -451,8 +443,9 @@ export default function App(initialData) { {('settings' === navigation) &&
diff --git a/assets/js/Components/FixIssuesPage.js b/assets/js/Components/FixIssuesPage.js index 24e2f4600..e25cd60f6 100644 --- a/assets/js/Components/FixIssuesPage.js +++ b/assets/js/Components/FixIssuesPage.js @@ -12,6 +12,7 @@ import * as Html from '../Services/Html' import Api from '../Services/Api' import './FixIssuesPage.css' +import { ISSUE_STATE, WIDGET_STATE, ISSUE_FILTER as FILTER } from '../Services/Constants' /** The data for this component can be a bit confusing, so here's a breakdown: * - report: The main report object is generated by the scanner. @@ -30,7 +31,9 @@ import './FixIssuesPage.css' export default function FixIssuesPage({ t, - settings, + instanceInfo, + formOptions, + preferences, initialSeverity = '', initialSearchTerm = '', contentItemCache, @@ -46,8 +49,6 @@ export default function FixIssuesPage({ { // Define the kinds of filters that will be available to the user - const FILTER = settings.ISSUE_FILTER - const defaultFilters = { [FILTER.TYPE.SEVERITY]: FILTER.ALL, [FILTER.TYPE.PUBLISHED]: FILTER.PUBLISHED, @@ -64,8 +65,6 @@ export default function FixIssuesPage({ [FILTER.TYPE.MODULE]: FILTER.ALL, } - const WIDGET_STATE = settings.WIDGET_STATE - const dialogId = "udoit-issue-dialog" const [activeIssue, setActiveIssue] = useState(null) @@ -83,7 +82,7 @@ export default function FixIssuesPage({ const [unfilteredIssues, setUnfilteredIssues] = useState([]) const [filteredIssues, setFilteredIssues] = useState([]) const [groupedList, setGroupedList] = useState([]) - const [widgetState, setWidgetState] = useState(settings.WIDGET_STATE.LOADING) + const [widgetState, setWidgetState] = useState(WIDGET_STATE.LOADING) const [liveUpdateToggle, setLiveUpdateToggle] = useState(true) const [clickedInfo, setClickedInfo] = useState({}) @@ -170,7 +169,7 @@ export default function FixIssuesPage({ issueResolution = FILTER.FIXEDANDRESOLVED } - let currentState = settings.ISSUE_STATE.UNCHANGED + let currentState = ISSUE_STATE.UNCHANGED if(sessionIssues && sessionIssues[issue.id]) { currentState = sessionIssues[issue.id] } @@ -291,7 +290,7 @@ export default function FixIssuesPage({ setFilteredIssues(tempFilteredContent) setGroupedList(groupList(tempFilteredContent)) - setWidgetState(settings.WIDGET_STATE.LIST) + setWidgetState(WIDGET_STATE.LIST) }, [activeFilters, searchTerm]) @@ -357,7 +356,7 @@ export default function FixIssuesPage({ } if(holdoverActiveIssue === null) { - setWidgetState(settings.WIDGET_STATE.LIST) + setWidgetState(WIDGET_STATE.LIST) } } @@ -497,7 +496,7 @@ export default function FixIssuesPage({ } // Check to see if the user ONLY wants to see issues from published content - if(settings?.user?.roles?.view_only_published && issue.issueData) { + if(preferences.viewOnlyPublished && issue.issueData) { let tempContentItem = getContentById(issue.issueData.contentItemId) if(tempContentItem && tempContentItem.published === false) { continue @@ -523,16 +522,16 @@ export default function FixIssuesPage({ const updateActiveSessionIssue = (issueId, state = null, contentItemId = null) => { if(state === null) { - state = settings.ISSUE_STATE.UNCHANGED + state = ISSUE_STATE.UNCHANGED } // This updates the counter for the daily progress updateSessionIssue(issueId, state, contentItemId) // Only update the whole list if the issue is saved, resolved, or marked as unresolved. - if(state === settings.ISSUE_STATE.SAVED - || state === settings.ISSUE_STATE.RESOLVED - || state === settings.ISSUE_STATE.UNCHANGED) { + if(state === ISSUE_STATE.SAVED + || state === ISSUE_STATE.RESOLVED + || state === ISSUE_STATE.UNCHANGED) { let tempUnfilteredIssues = unfilteredIssues.map((issue) => { if(issue.id === issueId) { @@ -591,7 +590,7 @@ export default function FixIssuesPage({ } console.log("Test") - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.SAVING) + updateActiveSessionIssue(issue.id, ISSUE_STATE.SAVING) addItemToBeingScanned(issue.contentItemId) const specificClassName = `udoit-ignore-${issue.scanRuleId.replaceAll("_", "-")}` @@ -626,14 +625,14 @@ export default function FixIssuesPage({ activeContentItem.body = fullPageHtml // Save the updated issue using the LMS API - let api = new Api(settings) + let api = new Api(instanceInfo) try { api.saveIssue(issue, fullPageHtml, markAsReviewed) .then((responseStr) => { // Check for HTTP errors before parsing JSON if (!responseStr.ok) { processServerError(responseStr) - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) removeItemFromBeingScanned(issue.contentItemId) return null } @@ -643,7 +642,7 @@ export default function FixIssuesPage({ // If the save falied, show the relevant error message if (response.data.failed) { - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) removeItemFromBeingScanned(issue.contentItemId) response.messages.forEach((msg) => addMessage(msg)) @@ -675,7 +674,7 @@ export default function FixIssuesPage({ const newIssue = Object.assign({}, issue, response.data.issue) // const formattedData = formatIssueData(newIssue) // setActiveIssue(formattedData) - updateActiveSessionIssue(newIssue.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionIssue(newIssue.id, ISSUE_STATE.SAVED) api.scanContent(newIssue.contentItemId) .then((responseStr) => responseStr.json()) @@ -687,14 +686,14 @@ export default function FixIssuesPage({ } else { // setActiveIssue(formatIssueData(issue)) - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionIssue(issue.id, ISSUE_STATE.SAVED) removeItemFromBeingScanned(issue.contentItemId) } } }) } catch (error) { console.error(error) - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) } @@ -708,7 +707,7 @@ export default function FixIssuesPage({ let issue = tempActiveIssue.issueData - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.SAVING) + updateActiveSessionIssue(issue.id, ISSUE_STATE.SAVING) addItemToBeingScanned(issue.contentItemId) const specificClassName = `udoit-ignore-${issue.scanRuleId.replaceAll("_", "-")}` @@ -737,12 +736,12 @@ export default function FixIssuesPage({ issue.xpath = newXpath || '' // Save the updated issue using the LMS API - let api = new Api(settings) + let api = new Api(instanceInfo) try { const saveResponse = await api.saveIssue(issue, fullPageHtml, markAsReviewed) if(!saveResponse.ok){ processServerError(saveResponse) - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) removeItemFromBeingScanned(issue.contentItemId) throw Error('Save returned invalid server response.') } @@ -750,7 +749,7 @@ export default function FixIssuesPage({ const saveResponseJson = await saveResponse.json() if(saveResponseJson?.errors && saveResponseJson.errors.length > 0) { - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) removeItemFromBeingScanned(issue.contentItemId) saveResponseJson.messages.forEach((msg) => addMessage(msg)) @@ -773,7 +772,7 @@ export default function FixIssuesPage({ // If there isn't a new issue created, we're done. if(!saveResponseJson?.data?.issue) { - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionIssue(issue.id, ISSUE_STATE.SAVED) removeItemFromBeingScanned(issue.contentItemId) return } @@ -781,7 +780,7 @@ export default function FixIssuesPage({ if(saveResponseJson?.data?.issue) { // Update the report object by rescanning the content const newIssue = Object.assign({}, issue, saveResponseJson.data.issue) - updateActiveSessionIssue(newIssue.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionIssue(newIssue.id, ISSUE_STATE.SAVED) const scanResponse = await api.scanContent(newIssue.contentItemId) @@ -796,12 +795,12 @@ export default function FixIssuesPage({ } } - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionIssue(issue.id, ISSUE_STATE.SAVED) removeItemFromBeingScanned(issue.contentItemId) } catch (error) { console.warn(error) - updateActiveSessionIssue(issue.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionIssue(issue.id, ISSUE_STATE.ERROR) } } @@ -873,7 +872,7 @@ export default function FixIssuesPage({ return ( <> - { widgetState === settings.WIDGET_STATE.LOADING ? ( + { widgetState === WIDGET_STATE.LOADING ? ( <> ) : (
{t('fix.label.barriers_shown_count', { shown: filteredIssues?.length || 0, total: unfilteredIssues?.length || 0 })}
@@ -934,8 +930,6 @@ export default function FixIssuesPage({ <> setShowLearnMore(false)} @@ -943,7 +937,7 @@ export default function FixIssuesPage({ 0 && ( diff --git a/assets/js/Components/Forms/BlockquoteForm.js b/assets/js/Components/Forms/BlockquoteForm.js index 83ac10bd9..7efef98ab 100644 --- a/assets/js/Components/Forms/BlockquoteForm.js +++ b/assets/js/Components/Forms/BlockquoteForm.js @@ -4,10 +4,10 @@ import OptionFeedback from '../Widgets/OptionFeedback' import ToggleSwitch from '../Widgets/ToggleSwitch' import * as Text from '../../Services/Text' import * as Html from '../../Services/Html' +import { UFIXIT_OPTIONS } from '../../Services/Constants' export default function BlockquoteForm({ t, - settings, activeIssue, isDisabled, handleActiveIssue, @@ -18,9 +18,9 @@ export default function BlockquoteForm({ }) { const FORM_OPTIONS = { - ADD_TEXT: settings.UFIXIT_OPTIONS.ADD_TEXT, - REMOVE_BLOCKQUOTE: settings.UFIXIT_OPTIONS.DELETE_ELEMENT, - MARK_AS_REVIEWED: settings.UFIXIT_OPTIONS.MARK_AS_REVIEWED + ADD_TEXT: UFIXIT_OPTIONS.ADD_TEXT, + REMOVE_BLOCKQUOTE: UFIXIT_OPTIONS.DELETE_ELEMENT, + MARK_AS_REVIEWED: UFIXIT_OPTIONS.MARK_AS_REVIEWED } const [textInputValue, setTextInputValue] = useState("") diff --git a/assets/js/Components/Forms/ContrastForm.js b/assets/js/Components/Forms/ContrastForm.js index 1249e5c99..962ba2965 100644 --- a/assets/js/Components/Forms/ContrastForm.js +++ b/assets/js/Components/Forms/ContrastForm.js @@ -12,7 +12,7 @@ import * as Contrast from '../../Services/Contrast' export default function ContrastForm({ t, - settings, + formOptions, activeIssue, isDisabled, handleActiveIssue, @@ -71,8 +71,8 @@ export default function ContrastForm({ if (tempBackgroundColors.length === 0) { tempBackgroundColors.push({ originalString: '', - originalColorString: settings.backgroundColor, - hsl: Contrast.toHSL(settings.backgroundColor) + originalColorString: formOptions.backgroundColor, + hsl: Contrast.toHSL(formOptions.backgroundColor) }) } return tempBackgroundColors @@ -93,7 +93,7 @@ export default function ContrastForm({ if (colorEl && colorEl.style && colorEl.style.color) { return Contrast.toHSL(colorEl.style.color); } - return Contrast.toHSL(settings.textColor); + return Contrast.toHSL(formOptions.textColor); } // Heading tags for contrast threshold diff --git a/assets/js/Components/Forms/EmbeddedContentTitleForm.js b/assets/js/Components/Forms/EmbeddedContentTitleForm.js index 9a94714d4..e54f7c5e2 100644 --- a/assets/js/Components/Forms/EmbeddedContentTitleForm.js +++ b/assets/js/Components/Forms/EmbeddedContentTitleForm.js @@ -3,10 +3,10 @@ import RadioSelector from '../Widgets/RadioSelector' import OptionFeedback from '../Widgets/OptionFeedback' import * as Html from '../../Services/Html' import * as Text from '../../Services/Text' +import { UFIXIT_OPTIONS } from '../../Services/Constants' export default function EmbeddedContentTitleForm({ t, - settings, activeIssue, isDisabled, handleActiveIssue, @@ -17,8 +17,8 @@ export default function EmbeddedContentTitleForm({ }) { const FORM_OPTIONS = { - ADD_LABEL: settings.UFIXIT_OPTIONS.ADD_TEXT, - MARK_AS_REVIEWED: settings.UFIXIT_OPTIONS.MARK_AS_REVIEWED + ADD_LABEL: UFIXIT_OPTIONS.ADD_TEXT, + MARK_AS_REVIEWED: UFIXIT_OPTIONS.MARK_AS_REVIEWED } const [textInputValue, setTextInputValue] = useState("") diff --git a/assets/js/Components/Forms/EmphasisForm.js b/assets/js/Components/Forms/EmphasisForm.js index b7ab2f2a8..c2206b490 100644 --- a/assets/js/Components/Forms/EmphasisForm.js +++ b/assets/js/Components/Forms/EmphasisForm.js @@ -4,10 +4,10 @@ import OptionFeedback from '../Widgets/OptionFeedback' import ToggleSwitch from '../Widgets/ToggleSwitch' import * as Html from '../../Services/Html' import * as Contrast from '../../Services/Contrast' +import { UFIXIT_OPTIONS } from '../../Services/Constants' export default function EmphasisForm({ t, - settings, activeIssue, isDisabled, handleActiveIssue, @@ -18,8 +18,8 @@ export default function EmphasisForm({ }) { const FORM_OPTIONS = { - ADD_EMPHASIS: settings.UFIXIT_OPTIONS.ADD_EMPHASIS, - MARK_AS_REVIEWED: settings.UFIXIT_OPTIONS.MARK_AS_REVIEWED + ADD_EMPHASIS: UFIXIT_OPTIONS.ADD_EMPHASIS, + MARK_AS_REVIEWED: UFIXIT_OPTIONS.MARK_AS_REVIEWED } const [useBold, setUseBold] = useState(false) diff --git a/assets/js/Components/Forms/FileForm.js b/assets/js/Components/Forms/FileForm.js index 023466b80..91baec847 100644 --- a/assets/js/Components/Forms/FileForm.js +++ b/assets/js/Components/Forms/FileForm.js @@ -12,7 +12,6 @@ import CloseIcon from '../Icons/CloseIcon' export default function FileForm ({ t, - settings, activeFile, sessionFiles, uploadedFile, diff --git a/assets/js/Components/Forms/FormExternalLink.js b/assets/js/Components/Forms/FormExternalLink.js index 031a6478d..bc9eb7c15 100644 --- a/assets/js/Components/Forms/FormExternalLink.js +++ b/assets/js/Components/Forms/FormExternalLink.js @@ -4,7 +4,7 @@ import ExternalLinkIcon from '../Icons/ExternalLinkIcon' export default function FormExternalLink({ t, - settings, + instanceInfo, activeIssue }) { @@ -12,7 +12,7 @@ export default function FormExternalLink({ useEffect(() => { if (activeIssue) { - setEditorLink(findEditURLWithIssue(activeIssue, settings)) + setEditorLink(findEditURLWithIssue(activeIssue, instanceInfo)) } else { setEditorLink('') diff --git a/assets/js/Components/Forms/FormReviewOnly.js b/assets/js/Components/Forms/FormReviewOnly.js index bb27efd04..2fd0bed78 100644 --- a/assets/js/Components/Forms/FormReviewOnly.js +++ b/assets/js/Components/Forms/FormReviewOnly.js @@ -4,7 +4,6 @@ import SaveIcon from '../Icons/SaveIcon' export default function FormReviewOnly({ t, - settings, activeIssue, handleIssueSave, isContentLoading, diff --git a/assets/js/Components/Forms/FormSaveOrReview.js b/assets/js/Components/Forms/FormSaveOrReview.js index 331dfec43..df48fe5b8 100644 --- a/assets/js/Components/Forms/FormSaveOrReview.js +++ b/assets/js/Components/Forms/FormSaveOrReview.js @@ -8,7 +8,6 @@ export default function FormSaveOrReview({ t, handleSubmit = null, activeIssue = null, - settings = null, activeContentItem = null, isDisabled = false, formErrors = [], diff --git a/assets/js/Components/Forms/HeadingEmptyForm.js b/assets/js/Components/Forms/HeadingEmptyForm.js index fd9ad69f4..9f40d3961 100644 --- a/assets/js/Components/Forms/HeadingEmptyForm.js +++ b/assets/js/Components/Forms/HeadingEmptyForm.js @@ -3,10 +3,10 @@ import RadioSelector from '../Widgets/RadioSelector' import OptionFeedback from '../Widgets/OptionFeedback' import * as Html from '../../Services/Html' import * as Text from '../../Services/Text' +import { UFIXIT_OPTIONS } from '../../Services/Constants' export default function HeadingEmptyForm({ t, - settings, activeIssue, isDisabled, handleActiveIssue, @@ -17,9 +17,9 @@ export default function HeadingEmptyForm({ }) { const FORM_OPTIONS = { - ADD_TEXT: settings.UFIXIT_OPTIONS.ADD_TEXT, - DELETE_HEADING: settings.UFIXIT_OPTIONS.DELETE_ELEMENT, - MARK_AS_REVIEWED: settings.UFIXIT_OPTIONS.MARK_AS_REVIEWED + ADD_TEXT: UFIXIT_OPTIONS.ADD_TEXT, + DELETE_HEADING: UFIXIT_OPTIONS.DELETE_ELEMENT, + MARK_AS_REVIEWED: UFIXIT_OPTIONS.MARK_AS_REVIEWED } const [textInputValue, setTextInputValue] = useState('') diff --git a/assets/js/Components/Forms/HeadingStyleForm.js b/assets/js/Components/Forms/HeadingStyleForm.js index 0318d982a..7a32cd602 100644 --- a/assets/js/Components/Forms/HeadingStyleForm.js +++ b/assets/js/Components/Forms/HeadingStyleForm.js @@ -4,10 +4,10 @@ import OptionFeedback from '../Widgets/OptionFeedback' import Combobox from '../Widgets/Combobox' import ToggleSwitch from '../Widgets/ToggleSwitch' import * as Html from '../../Services/Html' +import { UFIXIT_OPTIONS } from '../../Services/Constants' export default function HeadingStyleForm ({ t, - settings, activeIssue, isDisabled, handleActiveIssue, @@ -21,8 +21,8 @@ export default function HeadingStyleForm ({ const tagOptions = ["H2", "H3", "H4", "H5", "H6"] const allHeadings = ["H1", "H2", "H3", "H4", "H5", "H6", "h1", "h2", "h3", "h4", "h5", "h6"] const FORM_OPTIONS = { - SELECT_LEVEL: settings.UFIXIT_OPTIONS.SELECT_TAG, - MARK_AS_REVIEWED: settings.UFIXIT_OPTIONS.MARK_AS_REVIEWED + SELECT_LEVEL: UFIXIT_OPTIONS.SELECT_TAG, + MARK_AS_REVIEWED: UFIXIT_OPTIONS.MARK_AS_REVIEWED } const [selectOptions, setSelectOptions] = useState([]) @@ -165,7 +165,6 @@ export default function HeadingStyleForm ({ isDisabled={isDisabled} label='' options={selectOptions} - settings={settings} /> { hasStyling && (
diff --git a/assets/js/Components/Forms/InlineCSSForm.js b/assets/js/Components/Forms/InlineCSSForm.js index 365c30966..fe0ab9a50 100644 --- a/assets/js/Components/Forms/InlineCSSForm.js +++ b/assets/js/Components/Forms/InlineCSSForm.js @@ -6,7 +6,6 @@ import { getIssueHtml } from '../../Services/Html' export default function InlineCSSForm ({ t, - settings, activeIssue, handleIssueSave, isDisabled, @@ -180,7 +179,6 @@ export default function InlineCSSForm ({
diff --git a/assets/js/Components/Forms/LinkForm.js b/assets/js/Components/Forms/LinkForm.js index 4c10544ce..d5734db07 100644 --- a/assets/js/Components/Forms/LinkForm.js +++ b/assets/js/Components/Forms/LinkForm.js @@ -4,7 +4,6 @@ import * as Html from '../../Services/Html' export default function LinkForm({ t, - settings, activeIssue, isDisabled, handleIssueSave, @@ -113,7 +112,6 @@ export default function LinkForm({
diff --git a/assets/js/Components/Forms/QuoteForm.js b/assets/js/Components/Forms/QuoteForm.js index e6e194e3d..efba58b10 100644 --- a/assets/js/Components/Forms/QuoteForm.js +++ b/assets/js/Components/Forms/QuoteForm.js @@ -4,7 +4,6 @@ import * as Html from '../../Services/Html' export default function QuoteForm({ t, - settings, activeIssue, handleIssueSave, isDisabled, @@ -197,7 +196,6 @@ export default function QuoteForm({ )} diff --git a/assets/js/Components/Forms/UfixitReviewOnly.js b/assets/js/Components/Forms/UfixitReviewOnly.js index 2e9ada607..7755374e6 100644 --- a/assets/js/Components/Forms/UfixitReviewOnly.js +++ b/assets/js/Components/Forms/UfixitReviewOnly.js @@ -4,7 +4,6 @@ import FormReviewOnly from './FormReviewOnly' export default function UfixitReviewOnly({ t, - settings, activeIssue, isContentLoading, markAsReviewed, @@ -17,12 +16,10 @@ export default function UfixitReviewOnly({
{t('menu.nav.skip_to_main')} - {t('alt.UDOIT')} + {t('alt.UDOIT')} { - switch (settings?.user?.roles?.font_size) { + switch (preferences.fontSize) { case 'font-small': return 40; case 'font-normal': @@ -57,11 +58,11 @@ export default function HomePage({ useEffect(() => { let totalIssuesFixed = 0 - let dailyGoal = settings?.user?.roles?.daily_goal || 10 + let dailyGoal = preferences.dailyGoal || 10 let percentComplete = 0 if(sessionIssues && Object.keys(sessionIssues).length > 0) { for (const issueState of Object.values(sessionIssues)) { - if(issueState === settings.ISSUE_STATE.SAVED || issueState === settings.ISSUE_STATE.RESOLVED) { + if(issueState === ISSUE_STATE.SAVED || issueState === ISSUE_STATE.RESOLVED) { totalIssuesFixed += 1 } } @@ -79,7 +80,7 @@ export default function HomePage({ total: dailyGoal, percent: percentComplete }) - }, [settings?.user?.roles?.daily_goal]) + }, [preferences.dailyGoal]) useEffect(() => { if (!hasNewReport) return diff --git a/assets/js/Components/ReportsPage.js b/assets/js/Components/ReportsPage.js index 31ad33a61..c22c23a88 100644 --- a/assets/js/Components/ReportsPage.js +++ b/assets/js/Components/ReportsPage.js @@ -12,8 +12,14 @@ import PrintIcon from './Icons/PrintIcon' import RightArrowIcon from './Icons/RightArrowIcon' import SortIcon from './Icons/SortIcon' import './ReportsPage.css' +import { ISSUE_FILTER } from '../Services/Constants' -export default function ReportsPage({t, report, settings, quickSearchTerm}) { +export default function ReportsPage({ + t, + report, + instanceInfo, + quickSearchTerm +}) { const [reports, setReports] = useState([]) const [fetchedReports, setFetchedReports] = useState(false) @@ -21,7 +27,7 @@ export default function ReportsPage({t, report, settings, quickSearchTerm}) { const [showTable, setShowTable] = useState(false) const getReportHistory = () => { - const api = new Api(settings) + const api = new Api(instanceInfo) api.getReportHistory() .then((responseStr) => responseStr.json()) .then((response) => { @@ -112,11 +118,11 @@ export default function ReportsPage({t, report, settings, quickSearchTerm}) { if (!labels.includes(issue.label_display)) { labels.push(issue.label_display) if(issue.type === 'error' || issue.type === 'issue') { - issue.type = () + issue.type = () issue.type_display = t('filter.label.severity.issue') } else if(issue.type === 'potential' || issue.type === 'suggestion') { - issue.type = () + issue.type = () issue.type_display = t('filter.label.severity.potential') } issue.handled = (issue.fixed + issue.resolved > 0 ? 1 : 0) @@ -339,7 +345,6 @@ export default function ReportsPage({t, report, settings, quickSearchTerm}) {
diff --git a/assets/js/Components/ReviewFilesPage.js b/assets/js/Components/ReviewFilesPage.js index d59a57cba..0c10dd33b 100644 --- a/assets/js/Components/ReviewFilesPage.js +++ b/assets/js/Components/ReviewFilesPage.js @@ -9,6 +9,7 @@ import RightArrowIcon from './Icons/RightArrowIcon' import StatusPill from './Widgets/StatusPill' import * as Text from '../Services/Text' import Api from '../Services/Api' +import { FILE_TYPES, FILE_TYPE_MAP, WIDGET_STATE } from '../Services/Constants' import './FixIssuesPage.css' import './ReviewFilesPage.css' @@ -33,11 +34,12 @@ import './ReviewFilesPage.css' import * as Html from '../Services/Html.js' import CloseIcon from './Icons/CloseIcon.js' import LearnMore from './Widgets/LearnMore.js' +import { ISSUE_STATE, FILE_FILTER as FILTER } from '../Services/Constants' export default function ReviewFilesPage({ t, - settings, - + instanceInfo, + preferences, report, sections, processNewReport, @@ -49,8 +51,6 @@ export default function ReviewFilesPage({ { // Define the kinds of filters that will be available to the user - const FILTER = settings.FILE_FILTER - const defaultFilters = { [FILTER.TYPE.UTILIZATION]: FILTER.USED, [FILTER.TYPE.FILE_TYPE]: FILTER.ALL, @@ -58,8 +58,6 @@ export default function ReviewFilesPage({ [FILTER.TYPE.MODULE]: FILTER.ALL, } - const WIDGET_STATE = settings.WIDGET_STATE - const dialogId = "udoit-file-dialog" const headers = [ @@ -121,15 +119,15 @@ export default function ReviewFilesPage({ let keywords = [ fileData.fileName ? fileData.fileName.toLowerCase() : fileData.display_name.toLowerCase() ] // Keywords should include the file type ('MS Word', 'PDF', etc.) - if(settings.FILE_TYPES.includes(fileData.fileType)) { - fileType = settings.FILE_TYPE_MAP[fileData.fileType] + if(FILE_TYPES.includes(fileData.fileType)) { + fileType = FILE_TYPE_MAP[fileData.fileType] fileTypeLabel = t(`label.mime.${fileData.fileType}`) keywords.push[fileTypeLabel.toLowerCase()] } keywords = keywords.join(' ') - let currentState = settings.ISSUE_STATE.UNCHANGED + let currentState = ISSUE_STATE.UNCHANGED if(sessionFiles && sessionFiles[fileId]) { currentState = sessionFiles[fileId] } @@ -326,7 +324,7 @@ export default function ReviewFilesPage({ if(activeIssue.fileData && sessionFiles) { Object.keys(sessionFiles).forEach((key) => { if(key == activeIssue.fileData.id) { - if(sessionFiles[key] === settings.ISSUE_STATE.SAVING || sessionFiles[key] === settings.ISSUE_STATE.RESOLVING) { + if(sessionFiles[key] === ISSUE_STATE.SAVING || sessionFiles[key] === ISSUE_STATE.RESOLVING) { tempIsDisabled = true } } @@ -425,7 +423,6 @@ export default function ReviewFilesPage({ return ( ) @@ -512,16 +509,16 @@ export default function ReviewFilesPage({ // This does NOT change the report object, which updates when the issue's data changes. const updateActiveSessionFile = (fileId, state = null, contentItemId = null) => { if(state === null) { - state = settings.ISSUE_STATE.UNCHANGED + state = ISSUE_STATE.UNCHANGED } // This updates the counter for the daily progress updateSessionFiles(fileId, state, contentItemId) // Only update the whole list if the issue is saved, resolved, or marked as unresolved. - if(state === settings.ISSUE_STATE.SAVED - || state === settings.ISSUE_STATE.RESOLVED - || state === settings.ISSUE_STATE.UNCHANGED) { + if(state === ISSUE_STATE.SAVED + || state === ISSUE_STATE.RESOLVED + || state === ISSUE_STATE.UNCHANGED) { let tempUnfilteredIssues = unfilteredFiles.map((issue) => { if(issue.id === fileId) { @@ -598,7 +595,7 @@ export default function ReviewFilesPage({ const handleFileDelete = async () => { setIsDisabled(true) try{ - let api = new Api(settings) + let api = new Api(instanceInfo) const responseStr = await api.deleteFile(activeIssue.fileData) const response = await responseStr.json() if(response?.errors && response.errors.length > 0){ @@ -658,7 +655,7 @@ export default function ReviewFilesPage({ position: position, itemid: itemId, indent: indent, - courseId: settings.course.lmsCourseId + courseId: instanceInfo.course.lmsCourseId } return sectionIdOption } @@ -690,7 +687,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { const updateAndScanContent = async (postContentItemOptions, postSectionItemOption, fileId) => { const responseStatus = [] try{ - let api = new Api(settings) + let api = new Api(instanceInfo) const responseStr = await api.updateContent(postContentItemOptions, postSectionItemOption, fileId) const response = await responseStr.json() if (response.errors && response.errors.length > 0) { @@ -715,7 +712,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } } if(isLastContent) { - const reportResponseStr = await api.updateAndGetReport(settings.course.id) + const reportResponseStr = await api.updateAndGetReport(instanceInfo.course.id) const reportResponse = await reportResponseStr.json() if(reportResponse){ if(reportResponse.messages[0].severity == 'success'){ @@ -744,15 +741,15 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } const tempFile = Object.assign({}, activeIssue.fileData) - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.SAVING) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.SAVING) try{ // File Upload to Canvas - let api = new Api(settings) + let api = new Api(instanceInfo) const responseStr = await api.postFile(tempFile, newFileData) const response = await responseStr.json() if(response.errors && response.errors.length > 0) { response.errors.forEach((err) => addMessage({ message: t(err), severity: 'error', visible: true })) - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.ERROR) return } @@ -778,7 +775,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions, updatedFileData.id) if(responseStatus && responseStatus[0]?.type == "error"){ responseStatus.forEach((err) => addMessage({message: err.message, severity: 'error', visible:true})) - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.ERROR) return } else if(responseStatus && responseStatus[0]?.status == "success"){ @@ -795,10 +792,10 @@ const getSectionPostOptions = (newFile, sectionReferences) => { if(canMarkReview){ const resolvedReport = await handleFileResolve(tempFile, true, tempReport, true, false) tempReport = resolvedReport ? resolvedReport : tempReport - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.SAVED) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.SAVED) } else{ - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.UNCHANGED) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.UNCHANGED) } // Our file upload process is done at this point so we can add the messages response.messages.forEach((msg) => addMessage(msg)) @@ -806,19 +803,19 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } catch (error) { console.error(error) - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.ERROR) } } const handleFileResolve = async (fileData, getReport = false, copiedReport = report, forceReview = false, replace = false) => { - updateActiveSessionFile(fileData.id, settings.ISSUE_STATE.RESOLVING) + updateActiveSessionFile(fileData.id, ISSUE_STATE.RESOLVING) fileData.reviewed = !(fileData.reviewed) || forceReview if(replace){ fileData.replacement = null fileData.metadata.replacementFileId = -1 } try{ - let api = new Api(settings) + let api = new Api(instanceInfo) const responseStr = await api.reviewFile(fileData, replace) const response = await responseStr.json() @@ -830,10 +827,10 @@ const getSectionPostOptions = (newFile, sectionReferences) => { // Update the local report and activeIssue if(reviewed) { - updateActiveSessionFile(fileData.id, settings.ISSUE_STATE.RESOLVED) + updateActiveSessionFile(fileData.id, ISSUE_STATE.RESOLVED) } else { - updateActiveSessionFile(fileData.id, settings.ISSUE_STATE.UNCHANGED) + updateActiveSessionFile(fileData.id, ISSUE_STATE.UNCHANGED) } const newReport = updateFile(fileData, copiedReport) if(getReport){ @@ -843,7 +840,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } catch(error){ console.warn(error) - updateActiveSessionFile(fileData.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionFile(fileData.id, ISSUE_STATE.ERROR) } } @@ -853,7 +850,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } const handleFileRevert = async (activeFile, contentReferences, sectionReferences) => { - updateActiveSessionFile(activeFile.id, settings.ISSUE_STATE.SAVING) + updateActiveSessionFile(activeFile.id, ISSUE_STATE.SAVING) let tempReport = JSON.parse(JSON.stringify(report)) if(!Array.isArray(tempReport.files)){ tempReport.files = Object.values(tempReport.files) @@ -865,7 +862,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions, activeFile.id) if(responseStatus && responseStatus[0]?.type == "error"){ responseStatus.forEach((err) => addMessage({message: err.message, severity: 'error', visible:true})) - updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR) + updateActiveSessionFile(tempFile.id, ISSUE_STATE.ERROR) return } else if(responseStatus && responseStatus[0]?.status == "success"){ @@ -879,7 +876,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { } let currentFile = tempReport.files.find((file) => file.id == activeIssue.id) const resolvedReport = await handleFileResolve(currentFile, true, tempReport, false, true) - updateActiveSessionFile(currentFile.id, settings.ISSUE_STATE.UNCHANGED) + updateActiveSessionFile(currentFile.id, ISSUE_STATE.UNCHANGED) processNewReport(resolvedReport) } @@ -967,8 +964,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { { <> setShowLearnMore(false)} /> { {filteredFiles.length > 0 && tempActiveIssue && ( { // Set up alert options - let currentAlertTimeout = settings?.user?.roles?.alert_timeout || settings.DEFAULT_USER_SETTINGS.ALERT_TIMEOUT + let currentAlertTimeout = preferences.alertTimeout || DEFAULT_USER_SETTINGS.ALERT_TIMEOUT setAlertOptions([ { value: '5000', name: t('settings.option.alert_timeout.5s'), selected: currentAlertTimeout === '5000' }, { value: '10000', name: t('settings.option.alert_timeout.10s'), selected: currentAlertTimeout === '10000' }, @@ -29,7 +31,7 @@ export default function SettingsPage({ ]) // Set up font size options - let currentFontSize = settings?.user?.roles?.font_size || settings.DEFAULT_USER_SETTINGS.FONT_SIZE + let currentFontSize = preferences.fontSize || DEFAULT_USER_SETTINGS.FONT_SIZE setFontSizeOptions([ { value: 'font-small', name: t('settings.label.font_size.small'), selected: currentFontSize === 'font-small' }, { value: 'font-medium', name: t('settings.label.font_size.medium'), selected: currentFontSize === 'font-medium' }, @@ -38,7 +40,7 @@ export default function SettingsPage({ ]) // Set up font family options - let currentFontFamily = settings?.user?.roles?.font_family || settings.DEFAULT_USER_SETTINGS.FONT_FAMILY + let currentFontFamily = preferences.fontFamily || DEFAULT_USER_SETTINGS.FONT_FAMILY setFontFamilyOptions([ { value: 'sans-serif', name: t('settings.label.font_family.sans_serif'), selected: currentFontFamily === 'sans-serif' }, { value: 'serif', name: t('settings.label.font_family.serif'), selected: currentFontFamily === 'serif' }, @@ -47,20 +49,20 @@ export default function SettingsPage({ ]) // Set up language options - let currentLanguage = settings?.user?.roles?.lang || settings.DEFAULT_USER_SETTINGS.LANGUAGE + let currentLanguage = preferences.lang || DEFAULT_USER_SETTINGS.LANGUAGE setLanguageOptions([ { value: 'en', name: 'English', selected: currentLanguage === 'en' }, { value: 'es', name: 'EspaƱol', selected: currentLanguage === 'es' } ]) - }, [settings]) + }, [preferences, t]) - // For new users, the 'dark_mode' attribute may not be set, so we need to check if it exists before using it + // For new users, the 'darkMode' attribute may not be set, so we need to check if it exists before using it // Because the values might be false, we need to differentiate between undefined and false - const [darkMode, setDarkMode] = useState(settings?.user?.roles && ('dark_mode' in settings.user.roles) ? settings.user.roles.dark_mode : settings.DEFAULT_USER_SETTINGS.DARK_MODE) + const [darkMode, setDarkMode] = useState(preferences.darkMode ?? DEFAULT_USER_SETTINGS.DARK_MODE) const handleDarkModeChange = (newValue) => { setDarkMode(newValue) - updateUserSettings({ "dark_mode": newValue }) + updateUserPreferences({ "darkMode": newValue }) if (newValue) { document.getElementById('app-container').classList.add('dark-mode') } else { @@ -72,10 +74,10 @@ export default function SettingsPage({ if(!id || !value) { return } - if(settings?.user?.roles[id] === value) { + if(preferences[id] === value) { return } - updateUserSettings({ [id]: value }) + updateUserPreferences({ [id]: value }) } const handleTextSpacingSlider = (e) => { @@ -84,11 +86,11 @@ export default function SettingsPage({ if(!id || !value) { return } - if(settings?.user?.roles[id] === value) { + if(preferences[id] === value) { return } setTextSpacing(value) - updateUserSettings({ [id]: value }) + updateUserPreferences({ [id]: value }) } return ( @@ -105,26 +107,26 @@ export default function SettingsPage({ + />
+ />
{t('settings.label.alert_timeout')} + />
@@ -167,7 +169,7 @@ export default function SettingsPage({ id='lang' label='' options={languageOptions} - settings={settings} /> + />
@@ -176,9 +178,9 @@ export default function SettingsPage({

- +

-
{t('welcome.version')} {settings.versionNumber}
+
{t('welcome.version')} {instanceInfo.versionNumber}
{/* {t('settings.label.release_notes')} {t('settings.label.documentation')} */} diff --git a/assets/js/Components/WelcomePage.js b/assets/js/Components/WelcomePage.js index 162146572..4b9fc6127 100644 --- a/assets/js/Components/WelcomePage.js +++ b/assets/js/Components/WelcomePage.js @@ -8,7 +8,8 @@ import './WelcomePage.css' export default function WelcomePage({ t, - settings, + instanceInfo, + preferences, syncComplete, setWelcomeClosed }) { @@ -21,7 +22,7 @@ export default function WelcomePage({

{t('udoit')}
- {t('welcome.version')} {settings.versionNumber} + {t('welcome.version')} {instanceInfo.versionNumber}
@@ -64,7 +65,7 @@ export default function WelcomePage({ {t('welcome.product_tagline')}
- {t('alt.UCF_Open')} + {t('alt.UCF_Open')}

diff --git a/assets/js/Components/Widgets/BarrierInformation.js b/assets/js/Components/Widgets/BarrierInformation.js index 77d0d9ec0..742b341db 100644 --- a/assets/js/Components/Widgets/BarrierInformation.js +++ b/assets/js/Components/Widgets/BarrierInformation.js @@ -7,12 +7,11 @@ import DisabilityVisualIcon from '../Icons/DisabilityVisualIcon' import FormClarification from '../Forms/FormClarification' import { disabilityTypes, disabilitiesFromRule, formNameFromRule } from '../../Services/Ufixit' import './UfixitWidget.css' +import { ISSUE_FILTER } from '../../Services/Constants' export default function BarrierInformation ({ t, - settings, - tempActiveIssue, handleLearnMoreClick }) { @@ -46,7 +45,7 @@ export default function BarrierInformation ({ return } - if(tempActiveIssue.contentType === settings.ISSUE_FILTER.FILE_OBJECT) { + if(tempActiveIssue.contentType === ISSUE_FILTER.FILE_OBJECT) { setFormSummary(t('form.file.summary')) setShowLearnMore(true) } diff --git a/assets/js/Components/Widgets/Combobox.js b/assets/js/Components/Widgets/Combobox.js index 9470f87d3..389be9bc8 100644 --- a/assets/js/Components/Widgets/Combobox.js +++ b/assets/js/Components/Widgets/Combobox.js @@ -14,6 +14,7 @@ import ResolvedIcon from '../Icons/ResolvedIcon' import SortIcon from '../Icons/SortIcon' import FileTypeIcon from '../Icons/FileTypeIcon' import './Combobox.css' +import { FILE_FILTER, ISSUE_FILTER } from '../../Services/Constants' /* This component is adapted from: @@ -29,7 +30,6 @@ export default function Combobox({ id = '', label = '', options = [], - settings, }) { /* Ideally, the options array contains objects in the form of: @@ -84,40 +84,40 @@ export default function Combobox({ let size = 'icon-md' switch(iconString?.toUpperCase()) { - case settings.ISSUE_FILTER.ISSUE: + case ISSUE_FILTER.ISSUE: return - case settings.ISSUE_FILTER.POTENTIAL: - case settings.ISSUE_FILTER.UNREVIEWED: + case ISSUE_FILTER.POTENTIAL: + case ISSUE_FILTER.UNREVIEWED: return - case settings.ISSUE_FILTER.ANNOUNCEMENT: + case ISSUE_FILTER.ANNOUNCEMENT: return - case settings.ISSUE_FILTER.ASSIGNMENT: + case ISSUE_FILTER.ASSIGNMENT: return - case settings.ISSUE_FILTER.DISCUSSION_FORUM: + case ISSUE_FILTER.DISCUSSION_FORUM: return - case settings.ISSUE_FILTER.DISCUSSION_TOPIC: + case ISSUE_FILTER.DISCUSSION_TOPIC: return - case settings.ISSUE_FILTER.FILE: + case ISSUE_FILTER.FILE: return - case settings.ISSUE_FILTER.PAGE: + case ISSUE_FILTER.PAGE: return - case settings.ISSUE_FILTER.QUIZ: + case ISSUE_FILTER.QUIZ: return - case settings.ISSUE_FILTER.SYLLABUS: + case ISSUE_FILTER.SYLLABUS: return - case settings.ISSUE_FILTER.FIXED: - case settings.ISSUE_FILTER.FIXEDANDRESOLVED: + case ISSUE_FILTER.FIXED: + case ISSUE_FILTER.FIXEDANDRESOLVED: return - case settings.ISSUE_FILTER.RESOLVED: - case settings.ISSUE_FILTER.REVIEWED: - case settings.FILE_FILTER.REVIEWED: + case ISSUE_FILTER.RESOLVED: + case ISSUE_FILTER.REVIEWED: + case FILE_FILTER.REVIEWED: return - case settings.FILE_FILTER.FILE_PDF: - case settings.FILE_FILTER.FILE_WORD: - case settings.FILE_FILTER.FILE_POWERPOINT: - case settings.FILE_FILTER.FILE_EXCEL: - case settings.FILE_FILTER.FILE_AUDIO: - case settings.FILE_FILTER.FILE_VIDEO: + case FILE_FILTER.FILE_PDF: + case FILE_FILTER.FILE_WORD: + case FILE_FILTER.FILE_POWERPOINT: + case FILE_FILTER.FILE_EXCEL: + case FILE_FILTER.FILE_AUDIO: + case FILE_FILTER.FILE_VIDEO: return default: return '' diff --git a/assets/js/Components/Widgets/FileFixitWidget.js b/assets/js/Components/Widgets/FileFixitWidget.js index 0e4685e6f..31f55cb39 100644 --- a/assets/js/Components/Widgets/FileFixitWidget.js +++ b/assets/js/Components/Widgets/FileFixitWidget.js @@ -9,7 +9,6 @@ import SeverityIssueIconFilled from '../Icons/SeverityIssueIconFilled' export default function FileFixitWidget({ t, - settings, sessionFiles, tempActiveIssue, uploadedFile, @@ -36,7 +35,6 @@ export default function FileFixitWidget({
@@ -50,7 +48,6 @@ export default function FileFixitWidget({
{ @@ -162,7 +161,6 @@ export default function FixIssuesFilters({ id={detailedFilters[index].value} label={detailedFilters[index].label} options={detailedFilters[index].options} - settings={settings} />
) diff --git a/assets/js/Components/Widgets/FixIssuesList.js b/assets/js/Components/Widgets/FixIssuesList.js index f081788ee..4bf3f02e6 100644 --- a/assets/js/Components/Widgets/FixIssuesList.js +++ b/assets/js/Components/Widgets/FixIssuesList.js @@ -5,11 +5,10 @@ import StatusPill from './StatusPill' import SortIcon from '../Icons/SortIcon' import './FixIssuesList.css' +import { ISSUE_FILTER } from '../../Services/Constants' export default function FixIssuesList({ t, - settings, - groupedList, setActiveIssue }) { @@ -33,13 +32,13 @@ export default function FixIssuesList({ "Known Barrier, Page, 'Welcome to the course', found in: 'Introduction Module' */ const getIssueLabel = (issue) => { let label = '' - if(issue.status === settings.ISSUE_FILTER.ACTIVE) { + if(issue.status === ISSUE_FILTER.ACTIVE) { label += t(`filter.label.severity.${issue.severity.toLowerCase()}_single`) + ', ' } - else if (issue.status === settings.ISSUE_FILTER.FIXED || issue.status == settings.ISSUE_FILTER.FIXEDANDRESOLVED) { + else if (issue.status === ISSUE_FILTER.FIXED || issue.status == ISSUE_FILTER.FIXEDANDRESOLVED) { label += t('filter.label.resolution.fixed_single') + ', ' } - else if (issue.status === settings.ISSUE_FILTER.RESOLVED) { + else if (issue.status === ISSUE_FILTER.RESOLVED) { label += t('filter.label.resolution.resolved_single') + ', ' } @@ -129,7 +128,6 @@ export default function FixIssuesList({ diff --git a/assets/js/Components/Widgets/LearnMore.js b/assets/js/Components/Widgets/LearnMore.js index 018604739..5493afb92 100644 --- a/assets/js/Components/Widgets/LearnMore.js +++ b/assets/js/Components/Widgets/LearnMore.js @@ -6,12 +6,11 @@ import DisabilityMotorIcon from '../Icons/DisabilityMotorIcon' import DisabilityVisualIcon from '../Icons/DisabilityVisualIcon' import { disabilityTypes, disabilitiesFromRule, formNameFromRule } from '../../Services/Ufixit' import './UfixitWidget.css' +import { ISSUE_FILTER } from '../../Services/Constants' export default function LearnMore ({ t, - settings, - tempActiveIssue, showLearnMore, hideLearnMore @@ -27,7 +26,7 @@ export default function LearnMore ({ return } - if(tempActiveIssue.contentType === settings.ISSUE_FILTER.FILE_OBJECT) { + if(tempActiveIssue.contentType === ISSUE_FILTER.FILE_OBJECT) { setFormLearnMore(t(`form.file.${tempActiveIssue.fileData.fileType}.learn_more`)) setDisabilities([disabilityTypes.COGNITIVE, disabilityTypes.VISUAL]) } diff --git a/assets/js/Components/Widgets/Message.js b/assets/js/Components/Widgets/Message.js index b03bd192d..5daa52931 100644 --- a/assets/js/Components/Widgets/Message.js +++ b/assets/js/Components/Widgets/Message.js @@ -8,7 +8,7 @@ import CloseIcon from '../Icons/CloseIcon' export default function Message ({ t, - settings, + preferences, messageObject, pauseTimer, resumeTimer, @@ -44,7 +44,7 @@ export default function Message ({ const handleResume = () => { - let tempTimer = settings?.user?.roles?.alert_timeout || "5000" + let tempTimer = preferences.alertTimeout || "5000" if (tempTimer === 'none') { return } @@ -87,9 +87,9 @@ export default function Message ({ title={t('label.close_message')}> - { settings?.user?.roles?.alert_timeout !== 'none' && ( + { preferences.alertTimeout !== 'none' && (
-
+
) }
diff --git a/assets/js/Components/Widgets/MessageTray.js b/assets/js/Components/Widgets/MessageTray.js index 9452cce99..afd36547c 100644 --- a/assets/js/Components/Widgets/MessageTray.js +++ b/assets/js/Components/Widgets/MessageTray.js @@ -5,7 +5,7 @@ import './MessageTray.css' export default function MessageTray ({ t, - settings, + preferences, nextMessage }) { @@ -56,7 +56,7 @@ export default function MessageTray ({ * matches the most recent timer that was set for a given message. */ const resumeTimer = (messageId) => { - let tempTimerMs = settings?.user?.roles?.alert_timeout || "5000" + let tempTimerMs = preferences.alertTimeout || "5000" if (tempTimerMs === 'none') { setMessageTimers(Object.assign({}, messageTimers, {[messageId]: null}) ) return @@ -150,7 +150,7 @@ export default function MessageTray ({ { diff --git a/assets/js/Components/Widgets/ReviewFilesFilters.js b/assets/js/Components/Widgets/ReviewFilesFilters.js index 0ac09123b..ea12dc417 100644 --- a/assets/js/Components/Widgets/ReviewFilesFilters.js +++ b/assets/js/Components/Widgets/ReviewFilesFilters.js @@ -2,13 +2,14 @@ import React, { useState, useEffect } from 'react' import SearchIcon from '../Icons/SearchIcon' import Combobox from './Combobox' import ToggleSwitch from './ToggleSwitch' +import { FILE_FILTER as FILTER} from '../../Services/Constants' import './FixIssuesFilters.css' +import { DEFAULT_USER_SETTINGS } from '../../Services/Settings' export default function ReviewFilesFilters({ t, - settings, - + preferences, activeFilters, handleSearchTerm, searchTerm, @@ -16,8 +17,6 @@ export default function ReviewFilesFilters({ updateActiveFilters }) { - const FILTER = settings.FILE_FILTER - const filterLabels = { [FILTER.TYPE.UTILIZATION]: t('filter.label.utilization'), [FILTER.TYPE.FILE_TYPE]: t('filter.label.file_type'), @@ -43,7 +42,7 @@ export default function ReviewFilesFilters({ const [usedFilters, setUsedFilters] = useState(null) const [detailedFilters, setDetailedFilters] = useState(null) // For new users, the 'show_filters' attribute may not be set, so we need to check if it exists before using it - const [showFilters, setShowFilters] = useState(settings?.user?.roles && ('show_filters' in settings.user.roles) ? settings.user.roles.show_filters : settings.DEFAULT_USER_SETTINGS.SHOW_FILTERS) + const [showFilters, setShowFilters] = useState(preferences.showFilters ?? DEFAULT_USER_SETTINGS.SHOW_FILTERS) // When the page loads, only show the "Modules" filter is there are modules to filter by... useEffect(() => { @@ -156,7 +155,6 @@ export default function ReviewFilesFilters({ id={detailedFilters[index].value} label={detailedFilters[index].label} options={detailedFilters[index].options} - settings={settings} />
) diff --git a/assets/js/Components/Widgets/StatusPill.js b/assets/js/Components/Widgets/StatusPill.js index 1908c3179..e21887df9 100644 --- a/assets/js/Components/Widgets/StatusPill.js +++ b/assets/js/Components/Widgets/StatusPill.js @@ -4,36 +4,36 @@ import SeverityIcon from '../Icons/SeverityIcon' import SeverityPotentialIcon from '../Icons/SeverityPotentialIcon' import ResolvedIcon from '../Icons/ResolvedIcon' import FixedIcon from '../Icons/FixedIcon' +import { FILE_FILTER, ISSUE_FILTER } from '../../Services/Constants' export default function StatusPill({ t, - settings, issue, }) { return ( <> - { issue.status === settings.ISSUE_FILTER.ACTIVE ? ( + { issue.status === ISSUE_FILTER.ACTIVE ? (
- ) : (issue.status === settings.ISSUE_FILTER.FIXED || issue.status == settings.ISSUE_FILTER.FIXEDANDRESOLVED) ? ( + ) : (issue.status === ISSUE_FILTER.FIXED || issue.status == ISSUE_FILTER.FIXEDANDRESOLVED) ? (
- ) : (issue.status === settings.ISSUE_FILTER.RESOLVED) ? ( + ) : (issue.status === ISSUE_FILTER.RESOLVED) ? (
- ) : (issue.status === settings.FILE_FILTER.UNREVIEWED) ? ( + ) : (issue.status === FILE_FILTER.UNREVIEWED) ? (
- ) : (issue.status === settings.FILE_FILTER.REVIEWED) ? ( + ) : (issue.status === FILE_FILTER.REVIEWED) ? (
{ return sectionReference } -export function analyzeReport(report, ISSUE_STATE) { +export function analyzeReport(report) { let tempReport = { contentFixed: report.contentFixed || 0, contentResolved: report.contentResolved || 0, diff --git a/assets/js/Services/Settings.js b/assets/js/Services/Settings.js index c0a435b0f..64d29dc9c 100644 --- a/assets/js/Services/Settings.js +++ b/assets/js/Services/Settings.js @@ -1,107 +1,3 @@ -export const ISSUE_STATE = { - UNCHANGED: 0, - SAVING: 1, - RESOLVING: 2, - SAVED: 3, - RESOLVED: 4, - ERROR: 5, -} - -export const WIDGET_STATE = { - LOADING: 0, - FIXIT: 1, - LEARN: 2, - LIST: 3, - NO_RESULTS: 4, -} - -// Define the kinds of issue filters that will be available to the user -export const ISSUE_FILTER = { - TYPE: { - SEVERITY: 'SEVERITY', - CONTENT_TYPE: 'CONTENT_TYPE', - RESOLUTION: 'RESOLUTION', - MODULE: 'MODULE', - PUBLISHED: 'PUBLISHED', - }, - ALL: 'ALL', - ISSUE: 'ISSUE', - POTENTIAL: 'POTENTIAL', - SUGGESTION: 'SUGGESTION', - PAGE: 'PAGE', - ASSIGNMENT: 'ASSIGNMENT', - ANNOUNCEMENT: 'ANNOUNCEMENT', - DISCUSSION_TOPIC: 'DISCUSSION_TOPIC', - DISCUSSION_FORUM: 'DISCUSSION_FORUM', - FILE: 'FILE', - QUIZ: 'QUIZ', - SYLLABUS: 'SYLLABUS', - MODULE: 'MODULE', - FILE_OBJECT: 'FILE_OBJECT', - ACTIVE: 'ACTIVE', - FIXED: 'FIXED', - RESOLVED: 'RESOLVED', - FIXEDANDRESOLVED: 'FIXEDANDRESOLVED', // Doesn't appear in any dropdowns, but is used in the code - PUBLISHED: 'PUBLISHED', - UNPUBLISHED: 'UNPUBLISHED', -} - -export const FILE_FILTER = { - TYPE: { - UTILIZATION: 'UTILIZATION', - PUBLISHED: 'PUBLISHED', - FILE_TYPE: 'FILE_TYPE', - RESOLUTION: 'RESOLUTION', - MODULE: 'MODULE', - }, - ALL: 'ALL', - USED: 'USED', - UNUSED: 'UNUSED', - PUBLISHED: 'PUBLISHED', - UNPUBLISHED: 'UNPUBLISHED', - FILE_PDF: 'PDF', - FILE_WORD: 'WORD', - FILE_POWERPOINT: 'POWERPOINT', - FILE_EXCEL: 'EXCEL', - FILE_VIDEO: 'VIDEO', - FILE_AUDIO: 'AUDIO', - FILE_UNKNOWN: 'UNKNOWN', - ACTIVE: 'ACTIVE', - UNREVIEWED: 'UNREVIEWED', - REVIEWED: 'REVIEWED', - REPLACED: 'REPLACED', - FILE_OBJECT: 'FILE_OBJECT', -} - -export const FILE_TYPES = [ - 'pdf', - 'doc', - 'ppt', - 'xls', - 'audio', - 'video', -] - -export const FILE_TYPE_MAP = { - 'pdf': FILE_FILTER.FILE_PDF, - 'doc': FILE_FILTER.FILE_WORD, - 'ppt': FILE_FILTER.FILE_POWERPOINT, - 'xls': FILE_FILTER.FILE_EXCEL, - 'audio': FILE_FILTER.FILE_AUDIO, - 'video': FILE_FILTER.FILE_VIDEO, -} - -export const UFIXIT_OPTIONS = { - ADD_EMPHASIS: 'add-emphasis', - ADD_TEXT: 'add-text', - DELETE_ATTRIBUTE: 'delete-attribute', - DELETE_ELEMENT: 'delete-element', - MARK_AS_REVIEWED: 'mark-as-reviewed', - MARK_DECORATIVE: 'mark-decorative', - SELECT_ATTRIBUTE_VALUE: 'select-attribute-value', - SELECT_TAG: 'select-tag' -} - export const DEFAULT_USER_SETTINGS = { ALERT_TIMEOUT: '5000', FONT_SIZE: 'font-medium', @@ -112,12 +8,12 @@ export const DEFAULT_USER_SETTINGS = { LANGUAGE: 'en', } -export function findEditURLWithIssue(issue, settings) { - if (!issue || !settings || !settings.institution) { +export function findEditURLWithIssue(issue, instanceInfo) { + if (!issue || !instanceInfo || !instanceInfo.institution) { return '' } - let lms = settings.institution.lmsId + let lms = instanceInfo.institution.lmsId if (lms === 'canvas') { return `${issue.contentUrl}/edit` } else { diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index 3133a828c..87eab1e14 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -13,6 +13,7 @@ use App\Services\LmsUserService; use App\Services\SessionService; use App\Services\UtilityService; +use App\Services\InitialStateService; use App\Repository\CourseUserRepository; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -89,7 +90,8 @@ public function settingsApi( UtilityService $util, SessionService $sessionService, LmsApiService $lmsApi, - CourseRepository $courseRepo): JsonResponse + CourseRepository $courseRepo, + InitialStateService $initialStateService): JsonResponse { $this->util = $util; $this->session = $sessionService->getSession(); @@ -101,9 +103,23 @@ public function settingsApi( return new JsonResponse(['error' => 'Unauthenticated'], 401); } + $preferences = $initialStateService->getPreferences($user); + + $lms = $lmsApi->getLms(); + + if (!($accountId = $this->session->get('lms_account_id'))) { + $this->util->exitWithMessage('Account ID not found.'); + } + + $accounts = $lms->getAccountData($user, $accountId); + return new JsonResponse([ - 'settings' => $this->getSettings(), - 'messages' => $this->util->getUnreadMessages(true), + 'messages' => $util->getUnreadMessages(true), + 'preferences' => $preferences, + 'instanceInfo' => $initialStateService->getInstanceInfo($user), + 'labels' => $initialStateService->getLabels($preferences), + 'accounts' => $accounts, + 'termInfo' => $this->getTermInfo($accounts), ]); } @@ -657,26 +673,11 @@ public function getUpdatedAccounts( /** PROTECTED FUNCTIONS **/ - protected function getSettings(): array + protected function getTermInfo($accounts): array { $lms = $this->lmsApi->getLms(); - - /** @var User $user */ $user = $this->getUser(); - /** @var \App\Entity\Institution $institution */ - $institution = $user->getInstitution(); - $metadata = $institution->getMetadata(); - /** $lang should be two letters, and match an available JSON file in the /translations folder. */ - $lang = ($_ENV['DEFAULT_LANG'] ? $_ENV['DEFAULT_LANG'] : 'en'); - $lang = (!empty($metadata['lang'])) ? $metadata['lang'] : $lang; - $lang = (array_key_exists("lang", $user->getRoles()) ? $user->getRoles()["lang"] : $lang); - $excludedRuleIds = (!empty($metadata['excludedRuleIds'])) ? $metadata['excludedRuleIds'] : $_ENV['PHPALLY_EXCLUDED_RULES']; - - if (!($accountId = $this->session->get('lms_account_id'))) { - $this->util->exitWithMessage('Account ID not found.'); - } - - $accounts = $lms->getAccountData($user, $accountId); + $terms = $lms->getAccountTerms($user); $terms = $this->filterTermsByAccount($terms, $accounts); $defaultTerm = $this->getDefaultTerm($terms); @@ -688,17 +689,8 @@ protected function getSettings(): array } return [ - 'apiUrl' => !empty($_ENV['BASE_URL']) ? $_ENV['BASE_URL'] : false, - 'user' => $user, - 'institution' => $institution, - 'roles' => $this->session->get('roles'), - 'language' => $lang, - 'labels' => $this->util->getTranslation($lang), - 'excludedRuleIds' => $excludedRuleIds, - 'accounts' => $accounts, 'terms' => $simpleTerms, 'defaultTerm' => $defaultTerm, - 'suggestionRuleIds' => !empty($_ENV['PHPALLY_SUGGESTION_RULES']) ? $_ENV['PHPALLY_SUGGESTION_RULES'] : '', ]; } @@ -726,6 +718,7 @@ protected function getCourseData(Course $course, User $user) 'canScan' => true, ]; } + protected function filterTermsByAccount($terms, $accounts) { diff --git a/src/Controller/DashboardController.php b/src/Controller/DashboardController.php index 72faf4b6c..473d0cc20 100644 --- a/src/Controller/DashboardController.php +++ b/src/Controller/DashboardController.php @@ -9,6 +9,7 @@ use App\Services\LmsUserService; use App\Services\SessionService; use App\Services\UtilityService; +use App\Services\InitialStateService; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -82,7 +83,8 @@ public function index( public function settingsApi( UtilityService $util, SessionService $sessionService, - LmsApiService $lmsApi): JsonResponse + LmsApiService $lmsApi, + InitialStateService $initialStateService,): JsonResponse { $this->util = $util; $this->session = $sessionService->getSession(); @@ -105,45 +107,21 @@ public function settingsApi( $course = $this->createCourse($user->getInstitution(), $lmsCourseId); } + $preferences = $initialStateService->getPreferences($user); + return new JsonResponse([ - 'settings' => $this->getSettings($course), - 'messages' => $this->util->getUnreadMessages(true), + 'messages' => $this->util->getUnreadMessages(true), + 'preferences' => $preferences, + 'labels' => $initialStateService->getLabels($preferences), + 'instanceInfo' => $initialStateService->getInstanceInfo($user, $course), + 'formOptions' => [ + 'backgroundColor' => !empty($_ENV['BACKGROUND_COLOR']) ? $_ENV['BACKGROUND_COLOR'] : '#ffffff', + 'textColor' => !empty($_ENV['TEXT_COLOR']) ? $_ENV['TEXT_COLOR'] : '#000000', + ], ]); } - protected function getSettings(Course $course): array - { - /** @var User $user */ - $user = $this->getUser(); - /** @var \App\Entity\Institution $institution */ - $institution = $user->getInstitution(); - - $metadata = $institution->getMetadata(); - - /** $lang should be two letters, and match an available JSON file in the /translations folder. */ - $lang = ($_ENV['DEFAULT_LANG'] ? $_ENV['DEFAULT_LANG'] : 'en'); - $lang = (!empty($metadata['lang'])) ? $metadata['lang'] : $lang; - $lang = (array_key_exists("lang", $user->getRoles()) ? $user->getRoles()["lang"] : $lang); - $excludedRuleIds = (!empty($metadata['excludedRuleIds'])) ? $metadata['excludedRuleIds'] : $_ENV['PHPALLY_EXCLUDED_RULES']; - - $lms = $this->lmsApi->getLms(); - - return [ - 'apiUrl' => !empty($_ENV['BASE_URL']) ? $_ENV['BASE_URL'] : false, - 'user' => $user, - 'course' => $course, - 'institution' => $institution, - 'roles' => $this->session->get('roles'), - 'language' => $lang, - 'labels' => (array) $this->util->getTranslation($lang), - 'excludedRuleIds' => $excludedRuleIds, - 'contentTypes' => $lms->getContentTypes(), - 'backgroundColor' => !empty($_ENV['BACKGROUND_COLOR']) ? $_ENV['BACKGROUND_COLOR'] : '#ffffff', - 'textColor' => !empty($_ENV['TEXT_COLOR']) ? $_ENV['TEXT_COLOR'] : '#000000', - 'versionNumber' => !empty($_ENV['VERSION_NUMBER']) ? $_ENV['VERSION_NUMBER'] : '', - ]; - } - + protected function createCourse(Institution $institution, $lmsCourseId) { $course = new Course(); diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 780a653e9..61cc15653 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -25,7 +25,7 @@ public function __construct(ManagerRegistry $doctrine) $this->doctrine = $doctrine; } - #[Route('/api/users/{user}', name: 'user_put', methods: ['PUT'])] + #[Route('/api/users/{user}/preferences', name: 'user_preferences_patch', methods: ['PATCH'])] public function update(SessionService $sessionService, User $user, Request $request, UtilityService $util): JsonResponse { $apiResponse = new ApiResponse(); @@ -42,32 +42,48 @@ public function update(SessionService $sessionService, User $user, Request $requ throw new \Exception("msg.no_permissions"); } - $userVals = \json_decode($request->getContent(), true); + $newPreferences = \json_decode($request->getContent(), true); $oldRoles = $user->getRoles(); $oldLang = $oldRoles['lang'] ?? 'en'; - $newRoles = $userVals['roles'] ?? []; - $newLang = $newRoles['lang'] ?? 'en'; - $changeLang = ($oldLang !== $newLang); + // Preference IDs on the client differ from DB column names, so we must translate + $roleMap = [ + 'textSpacing' => 'text_spacing', + 'fontSize' => 'font_size', + 'fontFamily' => 'font_family', + 'darkMode' => 'dark_mode', + 'alertTimeout' => 'alert_timeout', + 'dailyGoal' => 'daily_goal', + 'showFilters' => 'show_filters', + 'viewOnlyPublished' => 'view_only_published', + 'lang' => 'lang' + ]; - $user->setRoles($newRoles); - if (empty($userVals['hasApiKey'])) { - $user->setApiKey(''); - $user->setRefreshToken(''); + $newRoles = $oldRoles; + + foreach($newPreferences as $roleKey => $roleValue) + { + if (!isset($roleMap[$roleKey])) continue; + $newKey = $roleMap[$roleKey]; + $newRoles[$newKey] = $roleValue; } + + + $changeLang = (!empty($newRoles['lang']) && $oldLang !== $newRoles['lang']); + + $user->setRoles($newRoles); $responseObject = [ 'user' => $user, - 'language' => $newLang, - 'labels' => [], + 'labels' => NULL, ]; if ($changeLang) { $this->util = $util; - $labels = $this->util->getTranslation($newLang); + $labels = $this->util->getTranslation($newRoles['lang']); $responseObject['labels'] = $labels; } diff --git a/src/Services/InitialStateService.php b/src/Services/InitialStateService.php new file mode 100644 index 000000000..3ed8fa8cd --- /dev/null +++ b/src/Services/InitialStateService.php @@ -0,0 +1,77 @@ +util = $util; + } + + public function getPreferences(User $user): array + { + $roles = $user->getRoles(); + $institution = $user->getInstitution(); + $metadata = $institution->getMetadata(); + + $lang = $_ENV['DEFAULT_LANG'] ?? 'en'; + $lang = !empty($metadata['lang']) ? $metadata['lang'] : $lang; + $lang = array_key_exists('lang', $roles) ? $roles['lang'] : $lang; + + return [ + 'textSpacing' => $roles['text_spacing'] ?? null, + 'fontSize' => $roles['font_size'] ?? null, + 'fontFamily' => $roles['font_family'] ?? null, + 'darkMode' => $roles['dark_mode'] ?? null, + 'alertTimeout' => $roles['alert_timeout'] ?? null, + 'dailyGoal' => $roles['daily_goal'] ?? null, + 'showFilters' => $roles['show_filters'] ?? null, + 'viewOnlyPublished' => $roles['view_only_published'] ?? null, + 'lang' => $lang, + ]; + } + + public function getInstanceInfo(User $user, $course = null): array + { + $institution = $user->getInstitution(); + $metadata = $institution->getMetadata(); + + return [ + 'apiUrl' => !empty($_ENV['BASE_URL']) ? $_ENV['BASE_URL'] : false, + 'course' => $course, + 'institution' => $institution, + 'versionNumber' => $_ENV['VERSION_NUMBER'] ?? '', + 'excludedRuleIds' => !empty($metadata['excludedRuleIds']) + ? $metadata['excludedRuleIds'] + : $_ENV['PHPALLY_EXCLUDED_RULES'], + 'user' => [ + 'id' => $user->getId(), + 'username' => $user->getUserIdentifier(), + 'name' => $user->getName(), + ], + ]; + } + + public function getLabels(array $preferences): array + { + return (array) $this->util->getTranslation($preferences['lang']); + } + + public function getFormOptions(): array + { + return [ + 'backgroundColor' => !empty($_ENV['BACKGROUND_COLOR']) ? $_ENV['BACKGROUND_COLOR'] : '#ffffff', + 'textColor' => !empty($_ENV['TEXT_COLOR']) ? $_ENV['TEXT_COLOR'] : '#000000', + ]; + } +} \ No newline at end of file