From 058d4e7de169a7cc228a902e21e10cfd15ccb5fe Mon Sep 17 00:00:00 2001 From: diana-villalvazo-wgu Date: Thu, 4 Dec 2025 13:00:03 -0600 Subject: [PATCH 1/3] feat: add external links override support --- docs/how_tos/override-external-urls.rst | 78 +++++++++++++++++++ plugins/course-apps/live/BBBSettings.jsx | 4 +- .../settings-modal/SettingsModal.jsx | 3 +- src/accessibility-page/AccessibilityPage.jsx | 6 +- .../sidebar/SplitTestSidebarInfo.tsx | 3 +- .../content/AdvanceTypeSelect.tsx | 3 +- .../LicenseWidget/LicenseDisplay.jsx | 3 +- .../components/SocialShareWidget/index.jsx | 3 +- src/editors/data/constants/problem.ts | 21 ++--- .../WelcomeLibrariesV2Alert.tsx | 3 +- 10 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 docs/how_tos/override-external-urls.rst diff --git a/docs/how_tos/override-external-urls.rst b/docs/how_tos/override-external-urls.rst new file mode 100644 index 0000000000..24e19798b0 --- /dev/null +++ b/docs/how_tos/override-external-urls.rst @@ -0,0 +1,78 @@ +Override External URLs +====================== + +What is getExternalLinkUrl? +--------------------------- + +The `getExternalLinkUrl` function is a utility from `@edx/frontend-platform` that allows for centralized management of external URLs. It enables the override of external links through configuration, making it possible to customize external references without modifying the source code directly. + +URLs wrapped with getExternalLinkUrl +------------------------------------ +Use cases: + +1. **Accessibility Page** (`src/accessibility-page/AccessibilityPage.jsx`) + - `COMMUNITY_ACCESSIBILITY_LINK` - Points to community accessibility resources: https://www.edx.org/accessibility + +2. **Course Outline** (if applicable) + - Documentation links + - Help resources + +3. **Other pages** (search for `getExternalLinkUrl` usage across the codebase) + - Help documentation + - External tool integrations + +Currently, the following external URLs are wrapped with `getExternalLinkUrl` in the authoring application: + +- 'https://www.edx.org/accessibility' +- 'https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html' +- 'https://docs.openedx.org/en/latest/educators/references/course_development/exercise_tools/guide_problem_types.html#advanced-problem-types' +- 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components' +- 'https://openai.com/api-data-privacy' +- 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/create_new_library.html' +- 'https://bigbluebutton.org/privacy-policy/' +- 'https://creativecommons.org/about' + +How to Override External URLs +----------------------------- + +To override external URLs, you can use the frontend platform's configuration system. +This object should be added to the config object defined in the env.config.[js,jsx,ts,tsx], and must be named externalLinkUrlOverrides. + +1. **Environment Configuration** + Add the URL overrides to your environment configuration: + + .. code-block:: javascript + + const config = { + // Other config options... + externalLinkUrlOverrides: { + 'https://www.edx.org/accessibility': 'https://your-custom-domain.com/accessibility', + // Add other URL overrides here + } + }; + +Examples +-------- + +**Original URL:** Default community accessibility link +**Override:** Your institution's accessibility policy page + +.. code-block:: javascript + + // In your app configuration + getExternalLinkUrl('https://www.edx.org/accessibility') + // Returns: 'https://your-custom-domain.com/accessibility' + // Instead of the default Open edX community link + +Benefits +-------- + +- **Customization**: Institutions can point to their own resources +- **Maintainability**: URLs can be changed without code modifications +- **Consistency**: Centralized URL management across the application +- **Flexibility**: Different environments can have different external links diff --git a/plugins/course-apps/live/BBBSettings.jsx b/plugins/course-apps/live/BBBSettings.jsx index 0f19c17aa3..e81e3aa03c 100644 --- a/plugins/course-apps/live/BBBSettings.jsx +++ b/plugins/course-apps/live/BBBSettings.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { getConfig } from '@edx/frontend-platform'; +import { getConfig, getExternalLinkUrl } from '@edx/frontend-platform'; import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { Form, Hyperlink } from '@openedx/paragon'; import PropTypes from 'prop-types'; @@ -93,7 +93,7 @@ const BbbSettings = ({ {intl.formatMessage(messages.freePlanMessage)} diff --git a/src/accessibility-page/AccessibilityPage.jsx b/src/accessibility-page/AccessibilityPage.jsx index ccb31736cf..1490c9487e 100644 --- a/src/accessibility-page/AccessibilityPage.jsx +++ b/src/accessibility-page/AccessibilityPage.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { useIntl } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { Helmet } from 'react-helmet'; import { Container } from '@openedx/paragon'; import { StudioFooterSlot } from '@edx/frontend-component-footer'; @@ -25,7 +26,10 @@ const AccessibilityPage = () => {
diff --git a/src/course-unit/sidebar/SplitTestSidebarInfo.tsx b/src/course-unit/sidebar/SplitTestSidebarInfo.tsx index 02cf04c20a..b96e1b8894 100644 --- a/src/course-unit/sidebar/SplitTestSidebarInfo.tsx +++ b/src/course-unit/sidebar/SplitTestSidebarInfo.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Card, Hyperlink, Stack } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import messages from './messages'; @@ -44,7 +45,7 @@ const SplitTestSidebarInfo = () => {
diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx index 524bbd6989..032767f878 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx @@ -12,6 +12,7 @@ import { } from '@openedx/paragon'; import { ArrowBack } from '@openedx/paragon/icons'; import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { AdvancedProblemType, AdvanceProblems, @@ -91,7 +92,7 @@ const AdvanceTypeSelect: React.FC = ({ diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx index 52f4b3e0a9..845b0e5b68 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { FormattedMessage, } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { Stack, Hyperlink, @@ -30,7 +31,7 @@ const LicenseDisplay = ({ {license === LicenseTypes.creativeCommons && ( diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx index 07b13f772d..a79c8237d3 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx @@ -5,6 +5,7 @@ import { FormattedMessage, useIntl, } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { Hyperlink, Form, @@ -30,7 +31,7 @@ const SocialShareWidget = ({ const intl = useIntl(); const isSetByCourse = allowVideoSharing.level === 'course'; const videoSharingEnabled = isLibrary ? videoSharingEnabledForAll : videoSharingEnabledForCourse; - const learnMoreLink = videoSharingLearnMoreLink || 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html'; + const learnMoreLink = videoSharingLearnMoreLink || getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html'); const onSocialSharingCheckboxChange = hooks.useTrackSocialSharingChange({ updateField }); const getSubtitle = () => { diff --git a/src/editors/data/constants/problem.ts b/src/editors/data/constants/problem.ts index a5089addf1..ab622123be 100644 --- a/src/editors/data/constants/problem.ts +++ b/src/editors/data/constants/problem.ts @@ -1,3 +1,4 @@ +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { StrictDict } from '../../utils'; import singleSelect from '../images/singleSelect.png'; import multiSelect from '../images/multiSelect.png'; @@ -41,7 +42,7 @@ export const getProblemTypes = (formatMessage) => ({ preview: singleSelect, previewDescription: formatMessage(problemMessages.singleSelectDescription), description: formatMessage(problemMessages.singleSelectInstruction), - helpLink: 'https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html'), prev: ProblemTypeKeys.TEXTINPUT, next: ProblemTypeKeys.MULTISELECT, template: basicProblemTemplates.singleSelect.olx, @@ -52,7 +53,7 @@ export const getProblemTypes = (formatMessage) => ({ preview: multiSelect, previewDescription: formatMessage(problemMessages.multiSelectDescription), description: formatMessage(problemMessages.multiSelectInstruction), - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html'), next: ProblemTypeKeys.DROPDOWN, prev: ProblemTypeKeys.SINGLESELECT, template: basicProblemTemplates.multiSelect.olx, @@ -63,7 +64,7 @@ export const getProblemTypes = (formatMessage) => ({ preview: dropdown, previewDescription: formatMessage(problemMessages.dropdownDescription), description: formatMessage(problemMessages.dropdownInstruction), - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html'), next: ProblemTypeKeys.NUMERIC, prev: ProblemTypeKeys.MULTISELECT, template: basicProblemTemplates.dropdown.olx, @@ -74,7 +75,7 @@ export const getProblemTypes = (formatMessage) => ({ preview: numericalInput, previewDescription: formatMessage(problemMessages.numericalInputDescription), description: formatMessage(problemMessages.numericalInputInstruction), - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html'), next: ProblemTypeKeys.TEXTINPUT, prev: ProblemTypeKeys.DROPDOWN, template: basicProblemTemplates.numeric.olx, @@ -85,7 +86,7 @@ export const getProblemTypes = (formatMessage) => ({ preview: textInput, previewDescription: formatMessage(problemMessages.textInputDescription), description: formatMessage(problemMessages.textInputInstruction), - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html'), prev: ProblemTypeKeys.NUMERIC, next: ProblemTypeKeys.SINGLESELECT, template: basicProblemTemplates.textInput.olx, @@ -105,7 +106,7 @@ export const ProblemTypes = StrictDict({ preview: singleSelect, previewDescription: 'Learners must select the correct answer from a list of possible options.', description: 'Enter your single select answers below and select which choices are correct. Learners must choose one correct answer.', - helpLink: 'https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html'), prev: ProblemTypeKeys.TEXTINPUT, next: ProblemTypeKeys.MULTISELECT, template: basicProblemTemplates.singleSelect.olx, @@ -116,7 +117,7 @@ export const ProblemTypes = StrictDict({ preview: multiSelect, previewDescription: 'Learners must select all correct answers from a list of possible options.', description: 'Enter your multi select answers below and select which choices are correct. Learners must choose all correct answers.', - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html'), next: ProblemTypeKeys.DROPDOWN, prev: ProblemTypeKeys.SINGLESELECT, template: basicProblemTemplates.multiSelect.olx, @@ -127,7 +128,7 @@ export const ProblemTypes = StrictDict({ preview: dropdown, previewDescription: 'Learners must select the correct answer from a list of possible options', description: 'Enter your dropdown answers below and select which choice is correct. Learners must select one correct answer.', - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html'), next: ProblemTypeKeys.NUMERIC, prev: ProblemTypeKeys.MULTISELECT, template: basicProblemTemplates.dropdown.olx, @@ -138,7 +139,7 @@ export const ProblemTypes = StrictDict({ preview: numericalInput, previewDescription: 'Specify one or more correct numeric answers, submitted in a response field.', description: 'Enter correct numerical input answers below. Learners must enter one correct answer.', - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html'), next: ProblemTypeKeys.TEXTINPUT, prev: ProblemTypeKeys.DROPDOWN, template: basicProblemTemplates.numeric.olx, @@ -149,7 +150,7 @@ export const ProblemTypes = StrictDict({ preview: textInput, previewDescription: 'Specify one or more correct text answers, including numbers and special characters, submitted in a response field.', description: 'Enter your text input answers below and select which choices are correct. Learners must enter one correct answer.', - helpLink: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html', + helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html'), prev: ProblemTypeKeys.NUMERIC, next: ProblemTypeKeys.SINGLESELECT, template: basicProblemTemplates.textInput.olx, diff --git a/src/studio-home/tabs-section/libraries-v2-tab/WelcomeLibrariesV2Alert.tsx b/src/studio-home/tabs-section/libraries-v2-tab/WelcomeLibrariesV2Alert.tsx index cc71d732ae..89d1e70c18 100644 --- a/src/studio-home/tabs-section/libraries-v2-tab/WelcomeLibrariesV2Alert.tsx +++ b/src/studio-home/tabs-section/libraries-v2-tab/WelcomeLibrariesV2Alert.tsx @@ -1,5 +1,6 @@ import { Alert, Button, Hyperlink } from '@openedx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { getExternalLinkUrl } from '@edx/frontend-platform'; import { useNavigate } from 'react-router-dom'; import { useLibrariesV1Data } from '@src/studio-home/data/apiHooks'; @@ -10,7 +11,7 @@ const libraryDocsLink = ( From 8127bc969841b4c7eec4c26e9d824a71c20f0bf2 Mon Sep 17 00:00:00 2001 From: diana-villalvazo-wgu Date: Thu, 18 Dec 2025 13:50:19 -0600 Subject: [PATCH 2/3] fix: add note, fix 404 url and fix unrelated typo --- docs/how_tos/override-external-urls.rst | 6 ++++-- src/accessibility-page/AccessibilityPage.jsx | 2 +- src/course-unit/sidebar/SplitTestSidebarInfo.tsx | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/how_tos/override-external-urls.rst b/docs/how_tos/override-external-urls.rst index 24e19798b0..c4a4f609a6 100644 --- a/docs/how_tos/override-external-urls.rst +++ b/docs/how_tos/override-external-urls.rst @@ -21,7 +21,7 @@ Use cases: - Help documentation - External tool integrations -Currently, the following external URLs are wrapped with `getExternalLinkUrl` in the authoring application: +Following external URLs are wrapped with `getExternalLinkUrl` in the authoring application: - 'https://www.edx.org/accessibility' - 'https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html' @@ -31,12 +31,14 @@ Currently, the following external URLs are wrapped with `getExternalLinkUrl` in - 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html' - 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html' - 'https://docs.openedx.org/en/latest/educators/references/course_development/exercise_tools/guide_problem_types.html#advanced-problem-types' -- 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components' +- 'https://docs.openedx.org/en/latest/educators/references/course_development/parent_child_components.html' - 'https://openai.com/api-data-privacy' - 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/create_new_library.html' - 'https://bigbluebutton.org/privacy-policy/' - 'https://creativecommons.org/about' +Note: as new external URLs are added to the codebase, more URLs will be wrapped with `getExternalLinkUrl` and this list may not always be up to date. + How to Override External URLs ----------------------------- diff --git a/src/accessibility-page/AccessibilityPage.jsx b/src/accessibility-page/AccessibilityPage.jsx index 1490c9487e..d2bba2a024 100644 --- a/src/accessibility-page/AccessibilityPage.jsx +++ b/src/accessibility-page/AccessibilityPage.jsx @@ -24,7 +24,7 @@ const AccessibilityPage = () => {
- + {
From 6066bb5b0fdb9eba6dc5fa004797e8ce2e5ab9b1 Mon Sep 17 00:00:00 2001 From: diana-villalvazo-wgu Date: Thu, 18 Dec 2025 15:51:03 -0600 Subject: [PATCH 3/3] test: fix --- src/course-unit/CourseUnit.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/course-unit/CourseUnit.test.jsx b/src/course-unit/CourseUnit.test.jsx index f957f7472b..df211b6c05 100644 --- a/src/course-unit/CourseUnit.test.jsx +++ b/src/course-unit/CourseUnit.test.jsx @@ -2191,7 +2191,7 @@ describe('', () => { const currentSectionName = courseSectionVerticalMock.xblock_info.ancestor_info.ancestors[1].display_name; const currentSubSectionName = courseSectionVerticalMock.xblock_info.ancestor_info.ancestors[1].display_name; - const helpLinkUrl = 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components'; + const helpLinkUrl = 'https://docs.openedx.org/en/latest/educators/references/course_development/parent_child_components.html'; await waitFor(() => { const unitHeaderTitle = screen.getByTestId('unit-header-title');