pull/6022/merge
christianbeeznest 8 months ago committed by GitHub
commit 6ad75c38fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      assets/vue/components/basecomponents/ChamiloIcons.js
  2. 62
      assets/vue/views/documents/DocumentsList.vue
  3. 99
      src/CoreBundle/Controller/Api/ReplaceDocumentFileAction.php
  4. 26
      src/CourseBundle/Entity/CDocument.php

@ -55,6 +55,7 @@ export const chamiloIconToClass = {
"file-generic": "mdi mdi-file",
"file-image": "mdi mdi-file-image",
"file-pdf": "mdi mdi-file-pdf-box",
"file-swap": "mdi mdi-swap-horizontal",
"file-text": "mdi mdi-file-document",
"file-upload": "mdi mdi-file-upload",
"file-video": "mdi mdi-file-video",

@ -182,6 +182,14 @@
type="secondary"
@click="openMoveDialog(slotProps.data)"
/>
<BaseButton
:disabled="slotProps.data.filetype !== 'file'"
:title="slotProps.data.filetype !== 'file' ? t('Replace (files only)') : t('Replace')"
icon="file-swap"
size="small"
type="secondary"
@click="slotProps.data.filetype === 'file' && openReplaceDialog(slotProps.data)"
/>
<BaseButton
:title="t('Information')"
icon="information"
@ -352,6 +360,21 @@
</div>
</BaseDialogConfirmCancel>
<BaseDialogConfirmCancel
v-model:is-visible="isReplaceDialogVisible"
:title="t('Replace document')"
@confirm-clicked="replaceDocument"
@cancel-clicked="isReplaceDialogVisible = false"
>
<BaseFileUpload
id="replace-file"
:label="t('Select file to replace')"
accept="*/*"
model-value="selectedReplaceFile"
@file-selected="selectedReplaceFile = $event"
/>
</BaseDialogConfirmCancel>
<BaseDialog
v-model:is-visible="isFileUsageDialogVisible"
:style="{ width: '28rem' }"
@ -519,6 +542,10 @@ const isSessionDocument = (item) => {
const isHtmlFile = (fileData) => isHtml(fileData)
const isReplaceDialogVisible = ref(false)
const selectedReplaceFile = ref(null)
const documentToReplace = ref(null)
onMounted(async () => {
isAllowedToEdit.value = await checkIsAllowedToEdit(true, true, true)
filters.value.loadNode = 1
@ -784,6 +811,41 @@ function openMoveDialog(document) {
isMoveDialogVisible.value = true
}
function openReplaceDialog(document) {
documentToReplace.value = document
isReplaceDialogVisible.value = true
}
async function replaceDocument() {
if (!selectedReplaceFile.value) {
notification.showErrorNotification(t("No file selected."))
return
}
if (documentToReplace.value.filetype !== 'file') {
notification.showErrorNotification(t("Only files can be replaced."))
return
}
const formData = new FormData()
console.log(selectedReplaceFile.value)
formData.append('file', selectedReplaceFile.value)
try {
await axios.post(`/api/documents/${documentToReplace.value.iid}/replace`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
notification.showSuccessNotification(t("Document replaced successfully."))
isReplaceDialogVisible.value = false
onUpdateOptions(options.value)
} catch (error) {
notification.showErrorNotification(t("Error replacing document."))
console.error(error)
}
}
async function fetchFolders(nodeId = null, parentPath = "") {
const foldersList = [
{

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller\Api;
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
use Chamilo\CourseBundle\Entity\CDocument;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
class ReplaceDocumentFileAction extends BaseResourceFileAction
{
private string $uploadBasePath;
public function __construct(KernelInterface $kernel)
{
$this->uploadBasePath = $kernel->getProjectDir() . '/var/upload/resource';
}
public function __invoke(
CDocument $document,
Request $request,
ResourceNodeRepository $resourceNodeRepository,
EntityManagerInterface $em
): Response {
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required.');
}
$resourceNode = $document->getResourceNode();
if (!$resourceNode) {
throw new BadRequestHttpException('ResourceNode not found.');
}
$resourceFile = $resourceNode->getFirstResourceFile();
if (!$resourceFile) {
throw new BadRequestHttpException('No file found in the resource node.');
}
$filePath = $this->uploadBasePath . $resourceNodeRepository->getFilename($resourceFile);
if (!$filePath) {
throw new BadRequestHttpException('File path could not be resolved.');
}
$this->prepareDirectory($filePath);
try {
$uploadedFile->move(dirname($filePath), basename($filePath));
} catch (FileException $e) {
throw new BadRequestHttpException(sprintf('Failed to move the file: %s', $e->getMessage()));
}
$movedFilePath = $filePath;
if (!file_exists($movedFilePath)) {
throw new \RuntimeException('The moved file does not exist at the expected location.');
}
$fileSize = filesize($movedFilePath);
$resourceFile->setSize($fileSize);
$newFileName = $uploadedFile->getClientOriginalName();
$document->setTitle($newFileName);
$resourceFile->setOriginalName($newFileName);
$resourceNode->setUpdatedAt(new \DateTime());
$em->persist($document);
$em->persist($resourceFile);
$em->flush();
return new Response('Document replaced successfully.', Response::HTTP_OK);
}
/**
* Prepares the directory to ensure it exists and is writable.
*/
protected function prepareDirectory(string $filePath): void
{
$directory = dirname($filePath);
if (!is_dir($directory)) {
if (!mkdir($directory, 0775, true) && !is_dir($directory)) {
throw new \RuntimeException(sprintf('Unable to create directory "%s".', $directory));
}
}
if (!is_writable($directory)) {
throw new \RuntimeException(sprintf('Directory "%s" is not writable.', $directory));
}
}
}

@ -18,6 +18,7 @@ use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use Chamilo\CoreBundle\Controller\Api\CreateDocumentFileAction;
use Chamilo\CoreBundle\Controller\Api\ReplaceDocumentFileAction;
use Chamilo\CoreBundle\Controller\Api\UpdateDocumentFileAction;
use Chamilo\CoreBundle\Controller\Api\UpdateVisibilityDocument;
use Chamilo\CoreBundle\Entity\AbstractResource;
@ -60,6 +61,31 @@ use Symfony\Component\Validator\Constraints as Assert;
security: "is_granted('EDIT', object.resourceNode)",
deserialize: true
),
new Post(
uriTemplate: '/documents/{iid}/replace',
controller: ReplaceDocumentFileAction::class,
openapiContext: [
'summary' => 'Replace a document file, maintaining the same IDs.',
'requestBody' => [
'content' => [
'multipart/form-data' => [
'schema' => [
'type' => 'object',
'properties' => [
'file' => [
'type' => 'string',
'format' => 'binary',
],
],
],
],
],
],
],
security: "is_granted('ROLE_CURRENT_COURSE_TEACHER') or is_granted('ROLE_CURRENT_COURSE_SESSION_TEACHER') or is_granted('ROLE_TEACHER')",
validationContext: ['groups' => ['Default', 'media_object_create', 'document:write']],
deserialize: false
),
new Get(security: "is_granted('VIEW', object.resourceNode)"),
new Delete(security: "is_granted('DELETE', object.resourceNode)"),
new Post(

Loading…
Cancel
Save