diff --git a/assets/js/Components/Forms/FileForm.js b/assets/js/Components/Forms/FileForm.js index f212e137d..99aea9cc9 100644 --- a/assets/js/Components/Forms/FileForm.js +++ b/assets/js/Components/Forms/FileForm.js @@ -200,7 +200,7 @@ export default function FileForm ({ return ( <> - { activeFile.reviewed && activeFile.replacement && + { activeFile.reviewed && activeFile.replacement && activeFile.references?.length == 0 &&
} + {activeFile.reviewed && activeFile.replacement && activeFile.references?.length > 0 && +
+ + {activeOption === FORM_OPTIONS.MARK_REVERT &&
{t('form.file.revert_instructions', {file: activeFile.fileName})}
} +
+ } + + {!activeFile.reviewed && activeFile.replacement && + <> +
+ +
+
+
+

{t('form.file.failed_replacement')}

+
{t('form.file.failed_instruction')}
+
+
+ + + } + {activeFile.reviewed && !activeFile.replacement &&
diff --git a/assets/js/Components/ReviewFilesPage.js b/assets/js/Components/ReviewFilesPage.js index 1524da29b..854c61e9f 100644 --- a/assets/js/Components/ReviewFilesPage.js +++ b/assets/js/Components/ReviewFilesPage.js @@ -654,13 +654,18 @@ useEffect(() => { } } - const extractUrl = (url) => { + const extractUrl = (url, contentType) => { if(!url) return '' const idx = url.indexOf('courses/'); if (idx !== -1) { // slice from "courses/" onward and strip any leading slashes (defensive) - return url.slice(idx).replace(/^\/+/, ''); + let slicedUrl = url.slice(idx).replace(/^\/+/, ''); + if(contentType == "syllabus"){ + const parts = slicedUrl.split("/") + slicedUrl = `${parts[0]}/${parts[1]}?include[]=syllabus_body` + } + return slicedUrl } // if no "courses/" found, remove leading slashes and return the remainder @@ -747,7 +752,7 @@ useEffect(() => { } const createContentItemPostOptions = (fullPageHtml, contentUrl, contentId, contentType, sectionIds) => { - const contentItemOption = { + const contentItemOption = { fullPageHtml: fullPageHtml, contentUrl: contentUrl, contentId: contentId, @@ -779,7 +784,7 @@ useEffect(() => { if(reference.contentItemBody){ newFullPageHtml = replaceFileInHtml(reference.contentItemBody, file.lmsFileId, newFile.metadata.url) } - postContentItemOptions.push(createContentItemPostOptions(newFullPageHtml, extractUrl(reference.contentItemUrl), reference.contentItemId, reference.contentType, reference.sectionIds)) + postContentItemOptions.push(createContentItemPostOptions(newFullPageHtml, extractUrl(reference.contentItemUrl, reference.contentType), reference.contentItemId, reference.contentType, reference.sectionIds)) }) } return postContentItemOptions @@ -1027,6 +1032,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => { const postContentItemOptions = getContentPostItems(activeFile.replacement, activeFile, contentReferences) const postSectionOptions = getSectionPostOptions(activeFile, sectionReferences) + if((postContentItemOptions && postContentItemOptions.length > 0) || (postSectionOptions && postSectionOptions.length > 0)){ const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions, activeFile.id) if(responseStatus && responseStatus[0]?.type == "error"){ diff --git a/assets/js/Components/Widgets/FileReviewPreview.js b/assets/js/Components/Widgets/FileReviewPreview.js index e907f5478..b82a3a1e8 100644 --- a/assets/js/Components/Widgets/FileReviewPreview.js +++ b/assets/js/Components/Widgets/FileReviewPreview.js @@ -14,10 +14,13 @@ export default function FixIssuesContentPreview({ isDisabled }) { - const [fileReferenceHolder, setFileReferenceHolder] = useState([]) + const [fileReferenceHolder, setFileReferenceHolder] = useState({}) const [currentFile, setCurrentFile] = useState(null) const [oldFile, setOldFile] = useState(null) + const ORIGINAL_LABEL = "-original" + const REPLACED_LABEL = "-replaced" + useEffect(() => { if(activeIssue){ handleFileReference() @@ -47,32 +50,48 @@ export default function FixIssuesContentPreview({ }, [activeIssue]) const handleFileReference = () => { - let tempReferences = [] + let tempReferences = {} activeIssue.fileData.replacement?.references?.forEach((ref) => { let tempRef = JSON.parse(JSON.stringify(ref)) tempRef.status = 1 - tempReferences.push(tempRef) + const refKey = tempRef.contentItemId + REPLACED_LABEL + if(!tempReferences[refKey]){ + tempReferences[refKey] = [] + } + tempReferences[refKey].push(tempRef) }) activeIssue.fileData.replacement?.sectionRefs?.forEach((ref) => { let tempRef = JSON.parse(JSON.stringify(ref)) tempRef.status = 1 - tempReferences.push(tempRef) + const refKey = tempRef.contentItemId + REPLACED_LABEL + if(!tempReferences[refKey]){ + tempReferences[refKey] = [] + } + tempReferences[refKey].push(tempRef) }) activeIssue.fileData.references?.forEach((ref) => { let tempRef = JSON.parse(JSON.stringify(ref)) tempRef.status = 0 - tempReferences.push(tempRef) + const refKey = tempRef.contentItemId + ORIGINAL_LABEL + if(!tempReferences[refKey]){ + tempReferences[refKey] = [] + } + tempReferences[refKey].push(tempRef) }) activeIssue.fileData.sectionRefs?.forEach((ref) => { let tempRef = JSON.parse(JSON.stringify(ref)) tempRef.status = 0 - tempReferences.push(tempRef) + const refKey = tempRef.contentItemId + ORIGINAL_LABEL + if(!tempReferences[refKey]){ + tempReferences[refKey] = [] + } + tempReferences[refKey].push(tempRef) }) setFileReferenceHolder(tempReferences) @@ -122,28 +141,32 @@ export default function FixIssuesContentPreview({ ))} - { fileReferenceHolder.length > 0 ? ( + { Object.keys(fileReferenceHolder).length > 0 ? ( <>
{t('form.file.instances.label')}
- +
+ - { fileReferenceHolder?.map((ref, index) => ( + { Object.keys(fileReferenceHolder)?.map((key, index) => ( +
{t('form.file.location.label')}{t('fix.label.references')} {t('form.file.status.label')}
- - {ref.contentItemTitle} + + {fileReferenceHolder[key][0].contentItemTitle} - {activeIssue.fileData.replacement ? ( +

{fileReferenceHolder[key]?.length}

+
+ {key.includes(REPLACED_LABEL) ? (
{t('form.file.new.label')}
) : (
{t('form.file.original.label')}
diff --git a/src/Lms/Canvas/CanvasApi.php b/src/Lms/Canvas/CanvasApi.php index d96270e4d..e54aef2e6 100644 --- a/src/Lms/Canvas/CanvasApi.php +++ b/src/Lms/Canvas/CanvasApi.php @@ -217,6 +217,7 @@ public function apiFilePost(string $url, array $options, string $filepath, strin public function apiPut($url, $options) { $lmsResponse = new LmsResponse(); + $output = new ConsoleOutput(); if(!isset($options['headers'])) { $options['headers'] = []; } @@ -285,6 +286,10 @@ public function apiPutBatch(array $paths, array $options){ $type = ""; $lmsId = ""; + if (preg_match('#^courses/(\d+)\?include\[\]=syllabus_body$#', $paths[$i], $matches)) { + $type = "syllabus"; + } + if (preg_match('#/(\w+)/([^/]+)$#', $paths[$i], $matches)) { $type = $matches[1]; $type = preg_replace('/s$/', '', $type); @@ -301,6 +306,10 @@ public function apiPutBatch(array $paths, array $options){ if ($type == 'discussion_topic' && isset($normalizedContent->is_announcement) && $normalizedContent->is_announcement) { $type = 'announcement'; } + + if ($type == 'syllabus'){ + $lmsId = $normalizedContent->id; + } $response = [ 'content' => $normalizedContent, 'id' => $lmsId, diff --git a/src/Lms/Canvas/CanvasLms.php b/src/Lms/Canvas/CanvasLms.php index 9e88d793c..d1f9a378d 100644 --- a/src/Lms/Canvas/CanvasLms.php +++ b/src/Lms/Canvas/CanvasLms.php @@ -629,15 +629,19 @@ public function postContentItemNoIssue($contentOptions, $sectionOptions) $sectionPostResponse = $canvasApi->apiPostBatch($sectionPaths, $sectionOptionsBuild); $sectionDeleteResponse = $canvasApi->apiDeleteBatch($deletePaths); $normalizedResponses = []; - foreach ($responses as $response) { + foreach($responses as $response){ $contentItem = $this->contentItemRepo->findOneBy([ 'contentType' => $response['type'], 'lmsContentId' => $response['id'], ]); - if ($contentItem) { + if($contentItem){ $normalizedContent = []; - if ($response['status'] == 200) { - $normalizedContent = $this->normalizeLmsContent($contentItem->getCourse(), $response['type'], json_decode(json_encode($response['content']), true)); + if($response['status'] == 200){ + $lmsContentNew = json_decode(json_encode($response['content']), true); + if ($response['type'] == 'syllabus') { + $lmsContentNew['syllabus_body'] = $option['fullPageHtml']; + } + $normalizedContent = $this->normalizeLmsContent($contentItem->getCourse(), $response['type'], $lmsContentNew); $contentItem->update($normalizedContent); $this->entityManager->flush(); } @@ -645,7 +649,7 @@ public function postContentItemNoIssue($contentOptions, $sectionOptions) 'content' => $normalizedContent, 'id' => $contentItem->getId(), 'status' => $response['status'], - 'type' => $response['type'], + 'type' => $response['type'] ]; $normalizedResponses[] = $normalizedResponse; } @@ -895,8 +899,15 @@ protected function createLmsPostOptions(ContentItem $contentItem) protected function createLmsPostOptionsWithHtml($type, $fullPageHtml) { $options = []; - switch ($type) { - case ('page'): + switch($type){ + case('syllabus'): + $options = [ + 'course' => [ + 'syllabus_body' => $fullPageHtml, + ], + ]; + break; + case('page'): $options = [ 'wiki_page' => [ 'body' => $fullPageHtml, diff --git a/translations/en.json b/translations/en.json index 2ad6bbb3d..79852f0e2 100644 --- a/translations/en.json +++ b/translations/en.json @@ -512,6 +512,9 @@ "form.file.revert_instructions": "You will be reverting the file references to have the original file: {file}", "form.file.revert_label": "Revert Changes", "form.file.marked_review": "Marked as Reviewed", + "form.file.mark_review": "Mark as Reviewed", + "form.file.failed_replacement": "Failed to Replace some Instances", + "form.file.failed_instruction": "Unfortunately we failed to replace the file in some of the content items. Please manually replace the files in the content items where it is still labeled as original.", "form.file.marked_review_instruction": "This file has been checked for accessibility and marked as reviewed. Mark the file has unreviewed if you believe it is inaccessible and needs changes", "form.file.keep_current": "Keep Current File", "form.file.upload_instructions": "Click or drag and drop here to upload a new file.",