diff --git a/src/course-outline/page-alerts/PageAlerts.jsx b/src/course-outline/page-alerts/PageAlerts.jsx
index 4b12996395..2dc9842025 100644
--- a/src/course-outline/page-alerts/PageAlerts.jsx
+++ b/src/course-outline/page-alerts/PageAlerts.jsx
@@ -343,13 +343,34 @@ const PageAlerts = ({
const renderApiErrors = () => {
let errorList = Object.entries(errors).filter(obj => obj[1] !== null).map(([k, v]) => {
switch (v.type) {
- case API_ERROR_TYPES.serverError:
+ case API_ERROR_TYPES.serverError: {
+ let description = (
+
+ {v.data || intl.formatMessage(messages.serverErrorAlertBody)}
+
+ );
+ let alertTitle = intl.formatMessage(messages.serverErrorAlert);
+ if (v.status === 403) {
+ description = intl.formatMessage(messages.forbiddenAlertBody, {
+ LMS: (
+
+ {intl.formatMessage(messages.forbiddenAlertLmsUrl)}
+
+ ),
+ });
+ alertTitle = intl.formatMessage(messages.forbiddenAlert);
+ }
return {
key: k,
- desc: v.data || intl.formatMessage(messages.serverErrorAlertBody),
- title: intl.formatMessage(messages.serverErrorAlert),
+ desc: description,
+ title: alertTitle,
dismissible: v.dismissible,
};
+ }
case API_ERROR_TYPES.networkError:
return {
key: k,
@@ -378,7 +399,7 @@ const PageAlerts = ({
dismissError={() => dispatch(dismissError(msgObj.key))}
>
{msgObj.title}
- {msgObj.desc && {msgObj.desc}}
+ {msgObj.desc}
) : (
{msgObj.title}
- {msgObj.desc && {msgObj.desc}}
+ {msgObj.desc}
)
))
diff --git a/src/course-outline/page-alerts/PageAlerts.test.jsx b/src/course-outline/page-alerts/PageAlerts.test.jsx
index 6d646f7cb4..28e39945ef 100644
--- a/src/course-outline/page-alerts/PageAlerts.test.jsx
+++ b/src/course-outline/page-alerts/PageAlerts.test.jsx
@@ -218,4 +218,29 @@ describe('', () => {
expect(queryByText('some error')).toBeInTheDocument();
expect(queryByText('some unknown error')).toBeInTheDocument();
});
+
+ it('renders forbidden api error alerts', async () => {
+ const { queryByText } = renderComponent({
+ ...pageAlertsData,
+ errors: {
+ outlineIndexApi: {
+ data: 'some error', status: 403, type: API_ERROR_TYPES.serverError, dismissable: true,
+ },
+ },
+ });
+ expect(queryByText(messages.forbiddenAlert.defaultMessage)).toBeInTheDocument();
+ expect(queryByText(messages.forbiddenAlertBody.defaultMessage)).toBeInTheDocument();
+ });
+
+ it('renders api error alerts when status is not 403', async () => {
+ const { queryByText } = renderComponent({
+ ...pageAlertsData,
+ errors: {
+ outlineIndexApi: {
+ data: 'some error', status: 500, type: API_ERROR_TYPES.serverError, dismissable: true,
+ },
+ },
+ });
+ expect(queryByText('some error')).toBeInTheDocument();
+ });
});
diff --git a/src/course-outline/page-alerts/messages.js b/src/course-outline/page-alerts/messages.js
index f9638398d8..9aa6756a78 100644
--- a/src/course-outline/page-alerts/messages.js
+++ b/src/course-outline/page-alerts/messages.js
@@ -121,6 +121,21 @@ const messages = defineMessages({
defaultMessage: 'Network error',
description: 'Generic network error alert.',
},
+ forbiddenAlert: {
+ id: 'course-authoring.course-outline.page-alert.forbidden.title',
+ defaultMessage: 'Access Restricted',
+ description: 'Forbidden(403) alert title',
+ },
+ forbiddenAlertBody: {
+ id: 'course-authoring.course-outline.page-alert.forbidden.body',
+ defaultMessage: 'It looks like you’re trying to access a page you don’t have permission to view. Contact your admin if you think this is a mistake, or head back to the {LMS}.',
+ description: 'Forbidden(403) alert body',
+ },
+ forbiddenAlertLmsUrl: {
+ id: 'course-authoring.course-outline.page-alert.lms',
+ defaultMessage: 'LMS',
+ description: 'LMS base redirection url',
+ },
});
export default messages;