Total Complexity | 70 |
Total Lines | 580 |
Duplicated Lines | 0 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Complex classes like ResourceController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ResourceController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | #[Route('/r')] |
||
52 | class ResourceController extends AbstractResourceController implements CourseControllerInterface |
||
53 | { |
||
54 | use ControllerTrait; |
||
55 | use CourseControllerTrait; |
||
56 | use GradebookControllerTrait; |
||
57 | use ResourceControllerTrait; |
||
58 | |||
59 | public function __construct( |
||
60 | private readonly UserHelper $userHelper, |
||
61 | private readonly ResourceNodeRepository $resourceNodeRepository, |
||
62 | ) {} |
||
63 | |||
64 | #[Route(path: '/{tool}/{type}/{id}/disk_space', methods: ['GET', 'POST'], name: 'chamilo_core_resource_disk_space')] |
||
65 | public function diskSpace(Request $request): Response |
||
66 | { |
||
67 | $nodeId = $request->get('id'); |
||
68 | $repository = $this->getRepositoryFromRequest($request); |
||
69 | |||
70 | /** @var ResourceNode $resourceNode */ |
||
71 | $resourceNode = $repository->getResourceNodeRepository()->find($nodeId); |
||
72 | |||
73 | $this->denyAccessUnlessGranted( |
||
74 | ResourceNodeVoter::VIEW, |
||
75 | $resourceNode, |
||
76 | $this->trans('Unauthorised access to resource') |
||
77 | ); |
||
78 | |||
79 | $course = $this->getCourse(); |
||
80 | $totalSize = 0; |
||
81 | if (null !== $course) { |
||
82 | $totalSize = $course->getDiskQuota(); |
||
83 | } |
||
84 | |||
85 | $size = $repository->getResourceNodeRepository()->getSize( |
||
86 | $resourceNode, |
||
87 | $repository->getResourceType(), |
||
88 | $course |
||
89 | ); |
||
90 | |||
91 | $labels[] = $course->getTitle(); |
||
|
|||
92 | $data[] = $size; |
||
93 | $sessions = $course->getSessions(); |
||
94 | |||
95 | foreach ($sessions as $sessionRelCourse) { |
||
96 | $session = $sessionRelCourse->getSession(); |
||
97 | |||
98 | $labels[] = $course->getTitle().' - '.$session->getTitle(); |
||
99 | $size = $repository->getResourceNodeRepository()->getSize( |
||
100 | $resourceNode, |
||
101 | $repository->getResourceType(), |
||
102 | $course, |
||
103 | $session |
||
104 | ); |
||
105 | $data[] = $size; |
||
106 | } |
||
107 | |||
108 | /*$groups = $course->getGroups(); |
||
109 | foreach ($groups as $group) { |
||
110 | $labels[] = $course->getTitle().' - '.$group->getTitle(); |
||
111 | $size = $repository->getResourceNodeRepository()->getSize( |
||
112 | $resourceNode, |
||
113 | $repository->getResourceType(), |
||
114 | $course, |
||
115 | null, |
||
116 | $group |
||
117 | ); |
||
118 | $data[] = $size; |
||
119 | }*/ |
||
120 | |||
121 | $used = array_sum($data); |
||
122 | $labels[] = $this->trans('Free'); |
||
123 | $data[] = $totalSize - $used; |
||
124 | |||
125 | return $this->render( |
||
126 | '@ChamiloCore/Resource/disk_space.html.twig', |
||
127 | [ |
||
128 | 'resourceNode' => $resourceNode, |
||
129 | 'labels' => $labels, |
||
130 | 'data' => $data, |
||
131 | ] |
||
132 | ); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * View file of a resource node. |
||
137 | */ |
||
138 | #[Route('/{tool}/{type}/{id}/view', name: 'chamilo_core_resource_view', methods: ['GET'])] |
||
139 | public function view(Request $request, TrackEDownloadsRepository $trackEDownloadsRepository): Response |
||
140 | { |
||
141 | $id = $request->get('id'); |
||
142 | $filter = (string) $request->get('filter'); // See filters definitions in /config/services.yml. |
||
143 | $resourceNode = $this->getResourceNodeRepository()->findOneBy(['uuid' => $id]); |
||
144 | |||
145 | if (null === $resourceNode) { |
||
146 | throw new FileNotFoundException($this->trans('Resource not found')); |
||
147 | } |
||
148 | |||
149 | $user = $this->userHelper->getCurrent(); |
||
150 | $firstResourceLink = $resourceNode->getResourceLinks()->first(); |
||
151 | $firstResourceFile = $resourceNode->getResourceFiles()->first(); |
||
152 | if ($firstResourceLink && $user && $firstResourceFile) { |
||
153 | $url = $firstResourceFile->getOriginalName(); |
||
154 | $trackEDownloadsRepository->saveDownload($user, $firstResourceLink, $url); |
||
155 | } |
||
156 | |||
157 | $cid = (int) $request->query->get('cid'); |
||
158 | $sid = (int) $request->query->get('sid'); |
||
159 | $allUserInfo = null; |
||
160 | if ($cid && $user) { |
||
161 | $allUserInfo = $this->getAllInfoToCertificate( |
||
162 | $user->getId(), |
||
163 | $cid, |
||
164 | $sid, |
||
165 | false |
||
166 | ); |
||
167 | } |
||
168 | |||
169 | return $this->processFile($request, $resourceNode, 'show', $filter, $allUserInfo); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Redirect resource to link. |
||
174 | * |
||
175 | * @return RedirectResponse|void |
||
176 | */ |
||
177 | #[Route('/{tool}/{type}/{id}/link', name: 'chamilo_core_resource_link', methods: ['GET'])] |
||
208 | } |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Download file of a resource node. |
||
213 | */ |
||
214 | #[Route('/{tool}/{type}/{id}/download', name: 'chamilo_core_resource_download', methods: ['GET'])] |
||
307 | } |
||
308 | |||
309 | #[Route('/{tool}/{type}/{id}/change_visibility', name: 'chamilo_core_resource_change_visibility', methods: ['POST'])] |
||
374 | } |
||
375 | |||
376 | #[Route( |
||
459 | } |
||
460 | |||
461 | private function processFile(Request $request, ResourceNode $resourceNode, string $mode = 'show', string $filter = '', ?array $allUserInfo = null): mixed |
||
462 | { |
||
463 | $this->denyAccessUnlessGranted( |
||
464 | ResourceNodeVoter::VIEW, |
||
465 | $resourceNode, |
||
466 | $this->trans('Unauthorised view access to resource') |
||
467 | ); |
||
468 | |||
469 | $resourceFile = $resourceNode->getResourceFiles()->first(); |
||
470 | |||
471 | if (!$resourceFile) { |
||
472 | throw $this->createNotFoundException($this->trans('File not found for resource')); |
||
473 | } |
||
474 | |||
475 | $fileName = $resourceFile->getOriginalName(); |
||
476 | $fileSize = $resourceFile->getSize(); |
||
477 | $mimeType = $resourceFile->getMimeType(); |
||
478 | [$start, $end, $length] = $this->getRange($request, $fileSize); |
||
479 | $resourceNodeRepo = $this->getResourceNodeRepository(); |
||
480 | |||
481 | // Convert the file name to ASCII using iconv |
||
482 | $fileName = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $fileName); |
||
483 | |||
484 | switch ($mode) { |
||
485 | case 'download': |
||
486 | $forceDownload = true; |
||
487 | |||
488 | break; |
||
489 | |||
490 | case 'show': |
||
491 | default: |
||
492 | $forceDownload = false; |
||
493 | // If it's an image then send it to Glide. |
||
494 | if (str_contains($mimeType, 'image')) { |
||
495 | $glide = $this->getGlide(); |
||
496 | $server = $glide->getServer(); |
||
497 | $params = $request->query->all(); |
||
498 | |||
499 | // The filter overwrites the params from GET. |
||
500 | if (!empty($filter)) { |
||
501 | $params = $glide->getFilters()[$filter] ?? []; |
||
502 | } |
||
503 | |||
504 | // The image was cropped manually by the user, so we force to render this version, |
||
505 | // no matter other crop parameters. |
||
506 | $crop = $resourceFile->getCrop(); |
||
507 | if (!empty($crop)) { |
||
508 | $params['crop'] = $crop; |
||
509 | } |
||
510 | |||
511 | $filePath = $resourceNodeRepo->getFilename($resourceFile); |
||
512 | |||
513 | $response = $server->getImageResponse($filePath, $params); |
||
514 | |||
515 | $disposition = $response->headers->makeDisposition( |
||
516 | ResponseHeaderBag::DISPOSITION_INLINE, |
||
517 | $fileName |
||
518 | ); |
||
519 | $response->headers->set('Content-Disposition', $disposition); |
||
520 | |||
521 | return $response; |
||
522 | } |
||
523 | |||
524 | // Modify the HTML content before displaying it. |
||
525 | if (str_contains($mimeType, 'html')) { |
||
526 | $content = $resourceNodeRepo->getResourceNodeFileContent($resourceNode); |
||
527 | |||
528 | if (null !== $allUserInfo) { |
||
529 | $tagsToReplace = $allUserInfo[0]; |
||
530 | $replacementValues = $allUserInfo[1]; |
||
531 | $content = str_replace($tagsToReplace, $replacementValues, $content); |
||
532 | } |
||
533 | |||
534 | $response = new Response(); |
||
535 | $disposition = $response->headers->makeDisposition( |
||
536 | ResponseHeaderBag::DISPOSITION_INLINE, |
||
537 | $fileName |
||
538 | ); |
||
539 | $response->headers->set('Content-Disposition', $disposition); |
||
540 | $response->headers->set('Content-Type', 'text/html'); |
||
541 | |||
542 | // @todo move into a function/class |
||
543 | if ('true' === $this->getSettingsManager()->getSetting('editor.translate_html')) { |
||
544 | $user = $this->userHelper->getCurrent(); |
||
545 | if (null !== $user) { |
||
546 | // Overwrite user_json, otherwise it will be loaded by the TwigListener.php |
||
547 | $userJson = json_encode(['locale' => $user->getLocale()]); |
||
548 | $js = $this->renderView( |
||
549 | '@ChamiloCore/Layout/document.html.twig', |
||
550 | ['breadcrumb' => '', 'user_json' => $userJson] |
||
551 | ); |
||
552 | // Insert inside the head tag. |
||
553 | $content = str_replace('</head>', $js.'</head>', $content); |
||
554 | } |
||
555 | } |
||
556 | if ('true' === $this->getSettingsManager()->getSetting('course.enable_bootstrap_in_documents_html')) { |
||
557 | // It adds the bootstrap and awesome css |
||
558 | $links = '<link href="'.api_get_path(WEB_PATH).'libs/bootstrap/bootstrap.min.css" rel="stylesheet">'; |
||
559 | $links .= '<link href="'.api_get_path(WEB_PATH).'libs/bootstrap/font-awesome.min.css" rel="stylesheet">'; |
||
560 | // Insert inside the head tag. |
||
561 | $content = str_replace('</head>', $links.'</head>', $content); |
||
562 | } |
||
563 | $response->setContent($content); |
||
564 | |||
565 | return $response; |
||
566 | } |
||
567 | |||
568 | break; |
||
569 | } |
||
570 | |||
571 | $response = new StreamedResponse( |
||
572 | function () use ($resourceNode, $start, $length): void { |
||
573 | $this->streamFileContent($resourceNode, $start, $length); |
||
574 | } |
||
575 | ); |
||
576 | |||
577 | $disposition = $response->headers->makeDisposition( |
||
578 | $forceDownload ? ResponseHeaderBag::DISPOSITION_ATTACHMENT : ResponseHeaderBag::DISPOSITION_INLINE, |
||
579 | $fileName |
||
580 | ); |
||
581 | $response->headers->set('Content-Disposition', $disposition); |
||
582 | $response->headers->set('Content-Type', $mimeType ?: 'application/octet-stream'); |
||
583 | $response->headers->set('Content-Length', (string) $resourceFile->getSize()); |
||
584 | $response->headers->set('Accept-Ranges', 'bytes'); |
||
585 | $response->headers->set('Content-Range', "bytes $start-$end/$fileSize"); |
||
586 | $response->setStatusCode( |
||
587 | $start > 0 || $end < $fileSize - 1 ? Response::HTTP_PARTIAL_CONTENT : Response::HTTP_OK |
||
588 | ); |
||
589 | |||
590 | return $response; |
||
591 | } |
||
592 | |||
593 | private function getRange(Request $request, int $fileSize): array |
||
612 | } |
||
613 | |||
614 | private function streamFileContent(ResourceNode $resourceNode, int $start, int $length): void |
||
631 | } |
||
632 | } |
||
633 |