Skip to content

Commit 7dc729a

Browse files
committed
Merge branch 'master' of github.com:chamilo/chamilo-lms
2 parents 6f31c26 + 440cff8 commit 7dc729a

File tree

7 files changed

+233
-138
lines changed

7 files changed

+233
-138
lines changed

public/main/inc/ajax/record_audio_rtc.ajax.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,25 @@
4444
break;
4545

4646
case Asset::EXERCISE_FEEDBACK:
47+
/** @var TrackEExercise|null $exeAttempt */
48+
$exeAttempt = Container::getTrackEExerciseRepository()->find($trackExerciseId);
49+
50+
if (null === $exeAttempt) {
51+
exit;
52+
}
53+
54+
// Make feedback asset unique per attempt + question (not only per question)
55+
$assetTitle = sprintf('feedback_%d_%d', $questionId, $trackExerciseId);
4756
$asset = (new Asset())
4857
->setCategory(Asset::EXERCISE_FEEDBACK)
49-
->setTitle("feedback_$questionId")
58+
->setTitle($assetTitle)
5059
;
5160

5261
$asset = $assetRepo->createFromRequest($asset, $_FILES['audio_blob']);
5362

5463
$attemptFeedback = (new AttemptFeedback())
5564
->setAsset($asset);
5665

57-
/** @var TrackEExercise $exeAttempt */
58-
$exeAttempt = Container::getTrackEExerciseRepository()->find($trackExerciseId);
5966
$attempt = $exeAttempt->getAttemptByQuestionId($questionId);
6067

6168
if (null === $attempt) {

public/main/inc/lib/exercise.lib.php

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,7 +5593,7 @@ public static function getOralFileAudio(int $trackExerciseId, int $questionId, b
55935593
*/
55945594
public static function getOralFeedbackAudio(int $attemptId, int $questionId): string
55955595
{
5596-
/** @var TrackEExercise $tExercise */
5596+
/** @var TrackEExercise|null $tExercise */
55975597
$tExercise = Container::getTrackEExerciseRepository()->find($attemptId);
55985598

55995599
if (null === $tExercise) {
@@ -5606,23 +5606,45 @@ public static function getOralFeedbackAudio(int $attemptId, int $questionId): st
56065606
return '';
56075607
}
56085608

5609-
$assetRepo = Container::getAssetRepository();
5610-
$html = '';
5609+
$feedbacks = $qAttempt->getAttemptFeedbacks();
56115610

5612-
// Keep only the latest audio feedback to avoid duplicated players
5613-
foreach ($qAttempt->getAttemptFeedbacks() as $attemptFeedback) {
5614-
$html = Display::tag(
5615-
'audio',
5616-
'',
5617-
[
5618-
'src' => $assetRepo->getAssetUrl($attemptFeedback->getAsset()),
5619-
'controls' => '',
5620-
]
5611+
if ($feedbacks->isEmpty()) {
5612+
return '';
5613+
}
56215614

5622-
);
5615+
$latestFeedback = null;
5616+
5617+
foreach ($feedbacks as $feedback) {
5618+
// Skip feedbacks without asset, just in case
5619+
if (null === $feedback->getAsset()) {
5620+
continue;
5621+
}
5622+
5623+
if (null === $latestFeedback) {
5624+
$latestFeedback = $feedback;
5625+
continue;
5626+
}
5627+
5628+
// Choose the feedback with the latest createdAt
5629+
if ($feedback->getCreatedAt() > $latestFeedback->getCreatedAt()) {
5630+
$latestFeedback = $feedback;
5631+
}
56235632
}
56245633

5625-
return $html;
5634+
if (null === $latestFeedback) {
5635+
return '';
5636+
}
5637+
5638+
$assetRepo = Container::getAssetRepository();
5639+
5640+
return Display::tag(
5641+
'audio',
5642+
'',
5643+
[
5644+
'src' => $assetRepo->getAssetUrl($latestFeedback->getAsset()),
5645+
'controls' => '',
5646+
]
5647+
);
56265648
}
56275649

56285650
public static function getUploadAnswerFiles(int $trackExerciseId, int $questionId, bool $returnUrls = false)

src/CoreBundle/Controller/Api/DownloadSelectedDocumentsAction.php

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<?php
22

3-
declare(strict_types=1);
4-
53
/* For licensing terms, see /license.txt */
64

5+
declare(strict_types=1);
6+
77
namespace Chamilo\CoreBundle\Controller\Api;
88

99
use Chamilo\CoreBundle\Entity\ResourceNode;
1010
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
11-
use Chamilo\CourseBundle\Entity\CDocument;
12-
use Doctrine\ORM\EntityManagerInterface;
11+
use Chamilo\CoreBundle\Traits\ControllerTrait;
12+
use Chamilo\CourseBundle\Repository\CDocumentRepository;
1313
use Exception;
1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\HttpFoundation\Response;
@@ -19,16 +19,20 @@
1919

2020
class DownloadSelectedDocumentsAction
2121
{
22-
private KernelInterface $kernel;
23-
private ResourceNodeRepository $resourceNodeRepository;
22+
use ControllerTrait;
2423

25-
public function __construct(KernelInterface $kernel, ResourceNodeRepository $resourceNodeRepository)
26-
{
27-
$this->kernel = $kernel;
28-
$this->resourceNodeRepository = $resourceNodeRepository;
29-
}
24+
public const CONTENT_TYPE = 'application/zip';
25+
26+
public function __construct(
27+
private readonly KernelInterface $kernel,
28+
private readonly ResourceNodeRepository $resourceNodeRepository,
29+
private readonly CDocumentRepository $documentRepo,
30+
) { }
3031

31-
public function __invoke(Request $request, EntityManagerInterface $em): Response
32+
/**
33+
* @throws Exception
34+
*/
35+
public function __invoke(Request $request): Response
3236
{
3337
ini_set('max_execution_time', '300');
3438
ini_set('memory_limit', '512M');
@@ -40,7 +44,7 @@ public function __invoke(Request $request, EntityManagerInterface $em): Response
4044
return new Response('No items selected.', Response::HTTP_BAD_REQUEST);
4145
}
4246

43-
$documents = $em->getRepository(CDocument::class)->findBy(['iid' => $documentIds]);
47+
$documents = $this->documentRepo->findBy(['iid' => $documentIds]);
4448

4549
if (empty($documents)) {
4650
return new Response('No documents found.', Response::HTTP_NOT_FOUND);
@@ -59,21 +63,27 @@ public function __invoke(Request $request, EntityManagerInterface $em): Response
5963
throw new Exception('ZIP file is empty or unreadable.');
6064
}
6165

62-
$response = new StreamedResponse(function () use ($zipFilePath): void {
63-
$handle = fopen($zipFilePath, 'rb');
64-
if ($handle) {
65-
while (!feof($handle)) {
66-
echo fread($handle, 8192);
67-
ob_flush();
68-
flush();
69-
}
70-
fclose($handle);
71-
}
72-
});
66+
[$start, $end, $length] = $this->getRange($request, $fileSize);
67+
68+
$response = new StreamedResponse(
69+
function () use ($start, $length, $zipFilePath): void {
70+
$handle = fopen($zipFilePath, 'rb');
7371

74-
$response->headers->set('Content-Type', 'application/zip');
75-
$response->headers->set('Content-Disposition', 'inline; filename="selected_documents.zip"');
76-
$response->headers->set('Content-Length', (string) $fileSize);
72+
$this->echoBuffer($handle, $start, $length);
73+
}
74+
);
75+
76+
$this->setHeadersToStreamedResponse(
77+
$response,
78+
false,
79+
'selected_documents.zip',
80+
self::CONTENT_TYPE,
81+
$length,
82+
$start,
83+
$end,
84+
$fileSize,
85+
Response::HTTP_CREATED
86+
);
7787

7888
return $response;
7989
}

src/CoreBundle/Controller/ResourceController.php

Lines changed: 20 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Chamilo\CoreBundle\Entity\Session;
1414
use Chamilo\CoreBundle\Entity\User;
1515
use Chamilo\CoreBundle\Helpers\AccessUrlHelper;
16+
use Chamilo\CoreBundle\Helpers\ResourceFileHelper;
1617
use Chamilo\CoreBundle\Helpers\UserHelper;
1718
use Chamilo\CoreBundle\Repository\ResourceFileRepository;
1819
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
@@ -144,8 +145,7 @@ public function diskSpace(Request $request): Response
144145
public function view(
145146
Request $request,
146147
TrackEDownloadsRepository $trackEDownloadsRepository,
147-
SettingsManager $settingsManager,
148-
AccessUrlHelper $accessUrlHelper
148+
ResourceFileHelper $resourceFileHelper,
149149
): Response {
150150
$id = $request->get('id');
151151
$resourceFileId = $request->get('resourceFileId');
@@ -161,26 +161,7 @@ public function view(
161161
$resourceFile = $this->resourceFileRepository->find($resourceFileId);
162162
}
163163

164-
if (!$resourceFile) {
165-
$accessUrlSpecificFiles = $settingsManager->getSetting('document.access_url_specific_files') && $accessUrlHelper->isMultiple();
166-
$currentUrl = $accessUrlHelper->getCurrent()?->getUrl();
167-
168-
$resourceFiles = $resourceNode->getResourceFiles();
169-
170-
if ($accessUrlSpecificFiles) {
171-
foreach ($resourceFiles as $file) {
172-
if ($file->getAccessUrl() && $file->getAccessUrl()->getUrl() === $currentUrl) {
173-
$resourceFile = $file;
174-
175-
break;
176-
}
177-
}
178-
}
179-
180-
if (!$resourceFile) {
181-
$resourceFile = $resourceFiles->filter(fn ($file) => null === $file->getAccessUrl())->first();
182-
}
183-
}
164+
$resourceFile ??= $resourceFileHelper->resolveResourceFileByAccessUrl($resourceNode);
184165

185166
if (!$resourceFile) {
186167
throw new FileNotFoundException($this->trans('Resource file not found for the given resource node'));
@@ -254,8 +235,7 @@ public function link(Request $request, RouterInterface $router, CLinkRepository
254235
public function download(
255236
Request $request,
256237
TrackEDownloadsRepository $trackEDownloadsRepository,
257-
SettingsManager $settingsManager,
258-
AccessUrlHelper $accessUrlHelper
238+
ResourceFileHelper $resourceFileHelper,
259239
): Response {
260240
$id = $request->get('id');
261241
$resourceNode = $this->getResourceNodeRepository()->findOneBy(['uuid' => $id]);
@@ -272,23 +252,7 @@ public function download(
272252
$this->trans('Unauthorised access to resource')
273253
);
274254

275-
$accessUrlSpecificFiles = $settingsManager->getSetting('document.access_url_specific_files') && $accessUrlHelper->isMultiple();
276-
$currentUrl = $accessUrlHelper->getCurrent()?->getUrl();
277-
278-
$resourceFiles = $resourceNode->getResourceFiles();
279-
$resourceFile = null;
280-
281-
if ($accessUrlSpecificFiles) {
282-
foreach ($resourceFiles as $file) {
283-
if ($file->getAccessUrl() && $file->getAccessUrl()->getUrl() === $currentUrl) {
284-
$resourceFile = $file;
285-
286-
break;
287-
}
288-
}
289-
}
290-
291-
$resourceFile ??= $resourceFiles->filter(fn ($file) => null === $file->getAccessUrl())->first();
255+
$resourceFile = $resourceFileHelper->resolveResourceFileByAccessUrl($resourceNode);
292256

293257
// If resource node has a file just download it. Don't download the children.
294258
if ($resourceFile) {
@@ -688,21 +652,24 @@ private function processFile(Request $request, ResourceNode $resourceNode, strin
688652

689653
$response = new StreamedResponse(
690654
function () use ($resourceNodeRepo, $resourceFile, $start, $length): void {
691-
$this->streamFileContent($resourceNodeRepo, $resourceFile, $start, $length);
655+
$stream = $resourceNodeRepo->getResourceNodeFileStream(
656+
$resourceFile->getResourceNode(),
657+
$resourceFile
658+
);
659+
660+
$this->echoBuffer($stream, $start, $length);
692661
}
693662
);
694663

695-
$disposition = $response->headers->makeDisposition(
696-
$forceDownload ? ResponseHeaderBag::DISPOSITION_ATTACHMENT : ResponseHeaderBag::DISPOSITION_INLINE,
697-
$fileName
698-
);
699-
$response->headers->set('Content-Disposition', $disposition);
700-
$response->headers->set('Content-Type', $mimeType ?: 'application/octet-stream');
701-
$response->headers->set('Content-Length', (string) $length);
702-
$response->headers->set('Accept-Ranges', 'bytes');
703-
$response->headers->set('Content-Range', "bytes $start-$end/$fileSize");
704-
$response->setStatusCode(
705-
$start > 0 || $end < $fileSize - 1 ? Response::HTTP_PARTIAL_CONTENT : Response::HTTP_OK
664+
$this->setHeadersToStreamedResponse(
665+
$response,
666+
$forceDownload,
667+
$fileName,
668+
$mimeType ?: 'application/octet-stream',
669+
$length,
670+
$start,
671+
$end,
672+
$fileSize
706673
);
707674

708675
return $response;
@@ -821,45 +788,4 @@ private function normalizeGeneratedHtml(string $content): string
821788

822789
return $normalized;
823790
}
824-
825-
826-
private function getRange(Request $request, int $fileSize): array
827-
{
828-
$range = $request->headers->get('Range');
829-
830-
if ($range) {
831-
[, $range] = explode('=', $range, 2);
832-
[$start, $end] = explode('-', $range);
833-
834-
$start = (int) $start;
835-
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
836-
837-
$length = $end - $start + 1;
838-
} else {
839-
$start = 0;
840-
$end = $fileSize - 1;
841-
$length = $fileSize;
842-
}
843-
844-
return [$start, $end, $length];
845-
}
846-
847-
private function streamFileContent(ResourceNodeRepository $resourceNodeRepo, ResourceFile $resourceFile, int $start, int $length): void
848-
{
849-
$stream = $resourceNodeRepo->getResourceNodeFileStream($resourceFile->getResourceNode(), $resourceFile);
850-
851-
fseek($stream, $start);
852-
853-
$bytesSent = 0;
854-
855-
while ($bytesSent < $length && !feof($stream)) {
856-
$buffer = fread($stream, min(1024 * 8, $length - $bytesSent));
857-
858-
echo $buffer;
859-
860-
$bytesSent += \strlen($buffer);
861-
}
862-
863-
fclose($stream);
864-
}
865791
}

0 commit comments

Comments
 (0)