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
57 changes: 38 additions & 19 deletions src/CoreBundle/Controller/Admin/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@ public function listFilesInfo(
$linksCount = [];
$coursesByFile = [];

/** @var ResourceFile $file */
foreach ($files as $file) {
$resourceNode = $file->getResourceNode();
$count = 0;
$coursesForThisFile = [];

if ($resourceNode) {
// Public URL to open/download this file
$fileUrls[$file->getId()] = $this->resourceNodeRepository->getResourceFileUrl($resourceNode);

// Count how many ResourceLinks still point to this node and collect courses.
Expand All @@ -104,12 +106,18 @@ public function listFilesInfo(
}

$courseId = $course->getId();

// Root resource node of the course (used to build Documents URL in the template JS)
$courseResourceNode = $course->getResourceNode();
$courseResourceNodeId = $courseResourceNode ? $courseResourceNode->getId() : null;

// Avoid duplicates for the same course.
if (!isset($coursesForThisFile[$courseId])) {
$coursesForThisFile[$courseId] = [
'id' => $courseId,
'code' => $course->getCode(),
'title' => $course->getTitle(),
'id' => $courseId,
'code' => $course->getCode(),
'title' => $course->getTitle(),
'resourceNodeId' => $courseResourceNodeId,
];
}
}
Expand All @@ -118,6 +126,7 @@ public function listFilesInfo(
$fileUrls[$file->getId()] = null;
}

// Physical path on disk (used only for display/copy)
$filePaths[$file->getId()] = '/upload/resource'.$this->resourceNodeRepository->getFilename($file);

$linksCount[$file->getId()] = $count;
Expand All @@ -131,22 +140,26 @@ public function listFilesInfo(

/** @var Course $course */
foreach ($allCourses as $course) {
$courseResourceNode = $course->getResourceNode();
$courseResourceNodeId = $courseResourceNode ? $courseResourceNode->getId() : null;

$courseOptions[] = [
'id' => $course->getId(),
'code' => $course->getCode(),
'title' => $course->getTitle(),
'id' => $course->getId(),
'code' => $course->getCode(),
'title' => $course->getTitle(),
'resourceNodeId' => $courseResourceNodeId,
];
}

return $this->render('@ChamiloCore/Admin/files_info.html.twig', [
'files' => $files,
'fileUrls' => $fileUrls,
'filePaths' => $filePaths,
'totalPages' => $totalPages,
'currentPage' => $page,
'search' => $search,
'orphanFlags' => $orphanFlags,
'linksCount' => $linksCount,
'files' => $files,
'fileUrls' => $fileUrls,
'filePaths' => $filePaths,
'totalPages' => $totalPages,
'currentPage' => $page,
'search' => $search,
'orphanFlags' => $orphanFlags,
'linksCount' => $linksCount,
'coursesByFile' => $coursesByFile,
'courseOptions' => $courseOptions,
]);
Expand Down Expand Up @@ -178,7 +191,7 @@ public function attachOrphanFileToCourse(
]);
}


// Collect course codes from multi-select.
$courseCodes = [];
$multi = $request->request->all('course_codes');
if (\is_array($multi)) {
Expand All @@ -190,6 +203,7 @@ public function attachOrphanFileToCourse(
}
}

// Fallback to single value if any.
if (0 === \count($courseCodes)) {
$single = $request->request->get('course_code');
$single = null === $single ? '' : trim((string) $single);
Expand Down Expand Up @@ -231,7 +245,7 @@ public function attachOrphanFileToCourse(
]);
}

// also create visible documents in the Documents tool.
// Hidden field in the template: always "1" now.
$createDocuments = (bool) $request->request->get('create_documents', false);

// Map existing links by course id to avoid duplicates.
Expand Down Expand Up @@ -286,7 +300,7 @@ public function attachOrphanFileToCourse(
$existingByCourseId[$courseId] = true;
$attachedTitles[] = (string) $course->getTitle();

// Optional feature: also create a visible document entry for this course.
// Also create a visible document entry for this course (Documents tool).
if ($createDocuments) {
$this->createVisibleDocumentFromResourceFile($resourceFile, $course, $em);
}
Expand Down Expand Up @@ -714,8 +728,8 @@ private function createVisibleDocumentFromResourceFile(
$parentResource = $course;
$parentNode = $parentResource->getResourceNode();

$title = $resourceFile->getTitle()
?? $resourceFile->getOriginalName()
$title = $resourceFile->getOriginalName()
?? $resourceFile->getTitle()
?? (string) $resourceFile->getId();

$existingDocument = $documentRepo->findCourseResourceByTitle(
Expand All @@ -727,6 +741,7 @@ private function createVisibleDocumentFromResourceFile(
);

if (null !== $existingDocument) {
// Document already exists for this title in this course context.
return;
}

Expand All @@ -743,14 +758,18 @@ private function createVisibleDocumentFromResourceFile(
$em->persist($document);
$em->flush();

// Physical file path in var/upload/resource (hashed filename)
$relativePath = $this->resourceNodeRepository->getFilename($resourceFile);
$storageRoot = $this->getParameter('kernel.project_dir').'/var/upload/resource';
$absolutePath = $storageRoot.$relativePath;

if (!is_file($absolutePath)) {
// If the physical file is missing, we cannot create the document content.
return;
}

// This will copy the file into the course documents structure,
// using $title as the base name (Document repository handles dedup and hashing).
$documentRepo->addFileFromPath($document, $title, $absolutePath);
}

Expand Down
79 changes: 65 additions & 14 deletions src/CoreBundle/Resources/views/Admin/files_info.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,8 @@
</small>
</div>

<div class="form-check mt-2">
<input type="checkbox"
class="form-check-input"
id="create-documents-checkbox"
name="create_documents"
value="1"
checked="checked">
<label class="form-check-label" for="create-documents-checkbox">
{{ 'Also create a visible document in the Documents tool for each selected course.'|trans }}
</label>
</div>
{# Always create a visible document in the Documents tool (no user option needed) #}
<input type="hidden" name="create_documents" value="1">

<button type="submit" class="btn btn--primary mt-3">
{{ 'Attach to selected course(s) (hidden)'|trans }}
Expand Down Expand Up @@ -347,6 +338,52 @@
var detachFileIdInput = document.getElementById('detach-resource-file-id');
var detachCourseIdInput = document.getElementById('detach-course-id');

// Base URL for the course Documents tool (e.g. /resources/document/)
var documentsBaseUrl = "{{ app.request.basePath|e('js') }}/resources/document/";

/**
* Build the URL to the Documents tool of a given course.
* Expects a course object with:
* - id or courseId => course identifier (cid)
* - resourceNodeId => resource node id of the course root
*/
function buildCourseDocumentsUrl(courseData) {
if (!courseData) {
return null;
}

var courseId =
courseData.id ||
courseData.courseId ||
null;

var courseNodeId =
courseData.resourceNodeId ||
courseData.resource_node_id ||
courseData.nodeId ||
null;

if (!courseId || !courseNodeId) {
// If we miss any piece of data, we keep the tag non-clickable.
return null;
}

return (
documentsBaseUrl +
encodeURIComponent(courseNodeId) +
"/?cid=" +
encodeURIComponent(courseId) +
"&gid=0"
);
}

/**
* Render course tags for the current file.
* Each tag:
* - Shows course code and title
* - Is clickable (opens Documents tool) when we have enough data
* - Keeps the "×" button to detach the file from the course
*/
function renderCourseTags(courses, resourceFileId) {
if (!courseTagsContainer) {
return;
Expand All @@ -368,9 +405,23 @@
"inline-flex items-center rounded-full bg-slate-100 text-slate-800 " +
"text-xs font-medium px-2 py-1 mr-1 mb-1 border border-slate-200";

var label = document.createElement("span");
label.textContent = "[" + (c.code || "") + "] " + (c.title || "");
pill.appendChild(label);
var labelText = "[" + (c.code || "") + "] " + (c.title || "");
var courseUrl = buildCourseDocumentsUrl(c);

if (courseUrl) {
// Clickable link to the course Documents tool.
var link = document.createElement("a");
link.href = courseUrl;
link.target = "_blank";
link.rel = "noopener noreferrer";
link.textContent = labelText;
link.className = "hover:underline";
pill.appendChild(link);
} else {
var label = document.createElement("span");
label.textContent = labelText;
pill.appendChild(label);
}

if (detachForm && detachFileIdInput && detachCourseIdInput) {
var removeBtn = document.createElement("button");
Expand Down
Loading