Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion assets/js/Components/Forms/FileForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export default function FileForm ({

return (
<>
{ activeFile.reviewed && activeFile.replacement &&
{ activeFile.reviewed && activeFile.replacement && activeFile.references?.length == 0 &&
<div className='flex-column gap-1'>
<div className={`resolve-option ${activeOption === FORM_OPTIONS.MARK_DELETE ? 'selected' : ''}`}>
<label className={`option-label` + (isDisabled ? ' disabled' : '')}>
Expand Down Expand Up @@ -238,6 +238,52 @@ export default function FileForm ({
</div>
}

{activeFile.reviewed && activeFile.replacement && activeFile.references?.length > 0 &&
<div className={`resolve-option ${activeOption === FORM_OPTIONS.MARK_REVERT ? 'selected' : ''}`}>
<label className={`option-label` + (isDisabled ? ' disabled' : '')}>
<input
type="radio"
id={FORM_OPTIONS.MARK_REVERT}
name="altTextOption"
tabIndex="0"
checked={activeOption === FORM_OPTIONS.MARK_REVERT}
disabled={isDisabled}
onChange={() => {
handleOptionChange(FORM_OPTIONS.MARK_REVERT)
}} />
{t('form.file.revert_label')}
</label>
{activeOption === FORM_OPTIONS.MARK_REVERT && <div className='instructions'>{t('form.file.revert_instructions', {file: activeFile.fileName})}</div>}
</div>
}

{!activeFile.reviewed && activeFile.replacement &&
<>
<div className={`resolve-option ${activeOption === FORM_OPTIONS.MARK_AS_REVIEWED ? 'selected' : ''}`}>
<label className={`option-label` + (isDisabled ? ' disabled' : '')}>
<input
type="radio"
id={FORM_OPTIONS.MARK_AS_REVIEWED}
name="altTextOption"
tabIndex="0"
checked={activeOption === FORM_OPTIONS.MARK_AS_REVIEWED}
disabled={isDisabled}
onChange={() => {
handleOptionChange(FORM_OPTIONS.MARK_AS_REVIEWED)
}} />
{t('form.file.mark_review')}
</label>
</div>
<div className='callout-container'>
<div className='p-2 flex-column justify-content-center align-items-center text-center'>
<h3 className="mt-0">{t('form.file.failed_replacement')}</h3>
<div className="instructions">{t('form.file.failed_instruction')}</div>
</div>
</div>

</>
}

{activeFile.reviewed && !activeFile.replacement &&
<div className='callout-container'>
<div className='p-2 flex-column justify-content-center align-items-center text-center'>
Expand Down
14 changes: 10 additions & 4 deletions assets/js/Components/ReviewFilesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -747,7 +752,7 @@ useEffect(() => {
}

const createContentItemPostOptions = (fullPageHtml, contentUrl, contentId, contentType, sectionIds) => {
const contentItemOption = {
const contentItemOption = {
fullPageHtml: fullPageHtml,
contentUrl: contentUrl,
contentId: contentId,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"){
Expand Down
47 changes: 35 additions & 12 deletions assets/js/Components/Widgets/FileReviewPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -122,28 +141,32 @@ export default function FixIssuesContentPreview({
</>
))}

{ fileReferenceHolder.length > 0 ? (
{ Object.keys(fileReferenceHolder).length > 0 ? (
<>
<div className="strong-caps mt-3">{t('form.file.instances.label')}</div>
<div className="mt-2 rounded-table-wrapper">
<table className="udoit-sortable-table first-column-wide">
<table className="udoit-sortable-table">
<thead>
<tr>
<th>{t('form.file.location.label')}</th>
<th>{t('fix.label.references')}</th>
<th>{t('form.file.status.label')}</th>
</tr>
</thead>
<tbody>
{ fileReferenceHolder?.map((ref, index) => (
{ Object.keys(fileReferenceHolder)?.map((key, index) => (
<tr key={index}>
<td>
<a href={ref.contentType == "quiz_question" ? ref.contentItemUrl.replace(/\/questions.*/, "/edit#questions_tab") : ref.contentItemUrl} target='_blank' className='location-link flex-row align-items-center'>
{ref.contentItemTitle}
<a href={fileReferenceHolder[key][0].contentType == "quiz_question" ? fileReferenceHolder[key][0].contentItemUrl.replace(/\/questions.*/, "/edit#questions_tab") : fileReferenceHolder[key][0].contentItemUrl} target='_blank' className='location-link flex-row align-items-center'>
{fileReferenceHolder[key][0].contentItemTitle}
<ExternalLinkIcon className="link-color align-self-center ms-2 icon-sm"/>
</a>
</td>
<td>
{activeIssue.fileData.replacement ? (
<p>{fileReferenceHolder[key]?.length}</p>
</td>
<td>
{key.includes(REPLACED_LABEL) ? (
<div className='file-label-pill file-new'>{t('form.file.new.label')}</div>
) : (
<div className='file-label-pill'>{t('form.file.original.label')}</div>
Expand Down
9 changes: 9 additions & 0 deletions src/Lms/Canvas/CanvasApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'] = [];
}
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
25 changes: 18 additions & 7 deletions src/Lms/Canvas/CanvasLms.php
Original file line number Diff line number Diff line change
Expand Up @@ -629,23 +629,27 @@ 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();
}
$normalizedResponse = [
'content' => $normalizedContent,
'id' => $contentItem->getId(),
'status' => $response['status'],
'type' => $response['type'],
'type' => $response['type']
];
$normalizedResponses[] = $normalizedResponse;
}
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
Loading