diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 08a1c79c44..24874d5d5f 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -476,6 +476,7 @@ "url-not-image": "URL must link directly to an image file", "use-valid-url": "Please use a valid URL", "editor-url": "Remember to submit the Live App URL.", + "http-url": "An unsecure (http) URL cannot be used.", "own-work-url": "Remember to submit your own work.", "publicly-visible-url": "Remember to submit a publicly visible app URL." }, diff --git a/client/src/components/formHelpers/FormFields.js b/client/src/components/formHelpers/FormFields.js index 72367f814f..12ae3ab1fc 100644 --- a/client/src/components/formHelpers/FormFields.js +++ b/client/src/components/formHelpers/FormFields.js @@ -15,7 +15,8 @@ import { editorValidator, localhostValidator, composeValidators, - fCCValidator + fCCValidator, + httpValidator } from './FormValidators'; const propTypes = { @@ -39,7 +40,8 @@ function FormFields(props) { placeholders = {}, required = [], types = {}, - isEditorLinkAllowed = false + isEditorLinkAllowed = false, + isLocalLinkAllowed = false } = options; const nullOrWarning = (value, error, isURL, name) => { @@ -54,7 +56,8 @@ function FormFields(props) { const validationWarning = composeValidators( name === 'githubLink' || isEditorLinkAllowed ? null : editorValidator, fCCValidator, - localhostValidator + httpValidator, + isLocalLinkAllowed ? null : localhostValidator )(value); const message = error || validationError || validationWarning; return message ? ( diff --git a/client/src/components/formHelpers/FormValidators.js b/client/src/components/formHelpers/FormValidators.js index 13f4fd4045..05aa86f550 100644 --- a/client/src/components/formHelpers/FormValidators.js +++ b/client/src/components/formHelpers/FormValidators.js @@ -5,6 +5,7 @@ import { Trans } from 'react-i18next'; const editorRegex = /repl\.it\/(@|join\/)|glitch\.com\/edit\/#!|codesandbox\.io\/s\/|github\.com/; const fCCRegex = /codepen\.io\/freecodecamp|freecodecamp\.rocks|github\.com\/freecodecamp/i; const localhostRegex = /localhost:/; +const httpRegex = /http(?!s|([^s]+?localhost))/; export const editorValidator = value => editorRegex.test(value) ? validation.editor-url : null; @@ -17,5 +18,8 @@ export const localhostValidator = value => validation.publicly-visible-url ) : null; +export const httpValidator = value => + httpRegex.test(value) ? validation.http-url : null; + export const composeValidators = (...validators) => value => validators.reduce((error, validator) => error ?? validator?.(value), null); diff --git a/client/src/components/formHelpers/index.js b/client/src/components/formHelpers/index.js index 1a70ea1caf..40daf0da60 100644 --- a/client/src/components/formHelpers/index.js +++ b/client/src/components/formHelpers/index.js @@ -3,7 +3,8 @@ import { localhostValidator, editorValidator, composeValidators, - fCCValidator + fCCValidator, + httpValidator } from './FormValidators'; export { default as BlockSaveButton } from './BlockSaveButton.js'; @@ -16,13 +17,14 @@ const normalizeOptions = { }; export function formatUrlValues(values, options) { - const { isEditorLinkAllowed, types } = options; + const { isEditorLinkAllowed, isLocalLinkAllowed, types } = options; const validatedValues = { values: {}, errors: [], invalidValues: [] }; const urlValues = Object.keys(values).reduce((result, key) => { let value = values[key]; const nullOrWarning = composeValidators( fCCValidator, - localhostValidator, + httpValidator, + isLocalLinkAllowed ? null : localhostValidator, key === 'githubLink' || isEditorLinkAllowed ? null : editorValidator )(value); if (nullOrWarning) { diff --git a/client/src/templates/Challenges/projects/SolutionForm.js b/client/src/templates/Challenges/projects/SolutionForm.js index a413fda5b7..ab943b4725 100644 --- a/client/src/templates/Challenges/projects/SolutionForm.js +++ b/client/src/templates/Challenges/projects/SolutionForm.js @@ -31,9 +31,9 @@ export class SolutionForm extends Component { handleSubmit(validatedValues) { // Do not execute challenge, if errors if (validatedValues.errors.length === 0) { + // updates values on store + this.props.updateSolutionForm(validatedValues.values); if (validatedValues.invalidValues.length === 0) { - // updates values on server - this.props.updateSolutionForm(validatedValues.values); this.props.onSubmit({ isShouldCompletionModalOpen: true }); } else { this.props.onSubmit({ isShouldCompletionModalOpen: false }); @@ -59,7 +59,8 @@ export class SolutionForm extends Component { githubLink: 'url' }, required: ['solution'], - isEditorLinkAllowed: false + isEditorLinkAllowed: false, + isLocalLinkAllowed: false }; const buttonCopy = isSubmitting @@ -79,6 +80,7 @@ export class SolutionForm extends Component { case backend: formFields = solutionField; + options.isLocalLinkAllowed = true; solutionLink = solutionLink + 'https://project-name.camperbot.repl.co/'; break;