Add external links override support (#2730)
* feat: add external links override support * fix: add note, fix 404 url and fix unrelated typo * test: fix
This commit is contained in:
80
docs/how_tos/override-external-urls.rst
Normal file
80
docs/how_tos/override-external-urls.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
|
||||
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://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
|
||||
-----------------------------
|
||||
|
||||
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
|
||||
@@ -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 = ({
|
||||
<span data-testid="free-plan-message">
|
||||
{intl.formatMessage(messages.freePlanMessage)}
|
||||
<Hyperlink
|
||||
destination="https://bigbluebutton.org/privacy-policy/"
|
||||
destination={getExternalLinkUrl('https://bigbluebutton.org/privacy-policy/')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
showLaunchIcon
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { getExternalLinkUrl } from '@edx/frontend-platform';
|
||||
import {
|
||||
ActionRow,
|
||||
Alert,
|
||||
@@ -276,7 +277,7 @@ const SettingsModal = ({
|
||||
<div className="py-1">
|
||||
<Hyperlink
|
||||
className="text-primary-500"
|
||||
destination="https://openai.com/api-data-privacy"
|
||||
destination={getExternalLinkUrl('https://openai.com/api-data-privacy')}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
|
||||
@@ -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';
|
||||
@@ -23,9 +24,12 @@ const AccessibilityPage = () => {
|
||||
</title>
|
||||
</Helmet>
|
||||
<Header isHiddenMainMenu />
|
||||
<Container size="xl" classNamae="px-4">
|
||||
<Container size="xl" className="px-4">
|
||||
<AccessibilityBody
|
||||
{...{ email: ACCESSIBILITY_EMAIL, communityAccessibilityLink: COMMUNITY_ACCESSIBILITY_LINK }}
|
||||
{...{
|
||||
email: ACCESSIBILITY_EMAIL,
|
||||
communityAccessibilityLink: getExternalLinkUrl(COMMUNITY_ACCESSIBILITY_LINK),
|
||||
}}
|
||||
/>
|
||||
<AccessibilityForm accessibilityEmail={ACCESSIBILITY_EMAIL} />
|
||||
</Container>
|
||||
|
||||
@@ -2191,7 +2191,7 @@ describe('<CourseUnit />', () => {
|
||||
|
||||
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');
|
||||
|
||||
@@ -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 = () => {
|
||||
<hr className="course-split-test-sidebar-devider my-4" />
|
||||
<Hyperlink
|
||||
showLaunchIcon={false}
|
||||
destination="https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_components.html#components-that-contain-other-components"
|
||||
destination={getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/references/course_development/parent_child_components.html')}
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
@@ -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<Props> = ({
|
||||
</Form.RadioSet>
|
||||
</Form.Group>
|
||||
<Hyperlink
|
||||
destination="https://docs.openedx.org/en/latest/educators/references/course_development/exercise_tools/guide_problem_types.html#advanced-problem-types"
|
||||
destination={getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/references/course_development/exercise_tools/guide_problem_types.html#advanced-problem-types')}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage {...messages.learnMoreAdvancedButtonLabel} />
|
||||
|
||||
@@ -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 && (
|
||||
<Hyperlink
|
||||
className="text-primary-500 x-small"
|
||||
destination="https://creativecommons.org/about"
|
||||
destination={getExternalLinkUrl('https://creativecommons.org/about')}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage {...messages.viewLicenseDetailsLabel} />
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = (
|
||||
<Hyperlink
|
||||
target="_blank"
|
||||
showLaunchIcon={false}
|
||||
destination="https://docs.openedx.org/en/latest/educators/how-tos/course_development/create_new_library.html"
|
||||
destination={getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/create_new_library.html')}
|
||||
>
|
||||
<FormattedMessage {...messages.alertLibrariesDocLinkText} />
|
||||
</Hyperlink>
|
||||
|
||||
Reference in New Issue
Block a user