|
12 | 12 | use Chamilo\CoreBundle\Entity\User; |
13 | 13 | use Chamilo\CoreBundle\Repository\PageCategoryRepository; |
14 | 14 | use Chamilo\CoreBundle\Repository\PageRepository; |
| 15 | +use Chamilo\CoreBundle\Repository\SysAnnouncementRepository; |
| 16 | +use Symfony\Component\Security\Core\User\UserInterface; |
15 | 17 |
|
16 | 18 | class PageHelper |
17 | 19 | { |
18 | 20 | protected PageRepository $pageRepository; |
19 | 21 | protected PageCategoryRepository $pageCategoryRepository; |
20 | 22 |
|
21 | | - public function __construct(PageRepository $pageRepository, PageCategoryRepository $pageCategoryRepository) |
22 | | - { |
| 23 | + /** |
| 24 | + * Repository used to read system announcements (platform news). |
| 25 | + */ |
| 26 | + protected SysAnnouncementRepository $sysAnnouncementRepository; |
| 27 | + |
| 28 | + /** |
| 29 | + * Helper used to retrieve the current AccessUrl. |
| 30 | + */ |
| 31 | + protected AccessUrlHelper $accessUrlHelper; |
| 32 | + |
| 33 | + public function __construct( |
| 34 | + PageRepository $pageRepository, |
| 35 | + PageCategoryRepository $pageCategoryRepository, |
| 36 | + SysAnnouncementRepository $sysAnnouncementRepository, |
| 37 | + AccessUrlHelper $accessUrlHelper |
| 38 | + ) { |
23 | 39 | $this->pageRepository = $pageRepository; |
24 | 40 | $this->pageCategoryRepository = $pageCategoryRepository; |
| 41 | + $this->sysAnnouncementRepository = $sysAnnouncementRepository; |
| 42 | + $this->accessUrlHelper = $accessUrlHelper; |
25 | 43 | } |
26 | 44 |
|
27 | 45 | public function createDefaultPages(User $user, AccessUrl $url, string $locale): bool |
@@ -99,8 +117,7 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): |
99 | 117 |
|
100 | 118 | $this->pageCategoryRepository->update($footerPrivateCategory); |
101 | 119 |
|
102 | | - // Categories for extra content in admin blocks |
103 | | - |
| 120 | + // Categories for extra content in admin blocks. |
104 | 121 | foreach (self::getCategoriesForAdminBlocks() as $nameBlock) { |
105 | 122 | $usersAdminBlock = (new PageCategory()) |
106 | 123 | ->setTitle($nameBlock) |
@@ -142,4 +159,68 @@ public static function getCategoriesForAdminBlocks(): array |
142 | 159 | 'block-admin-chamilo', |
143 | 160 | ]; |
144 | 161 | } |
| 162 | + |
| 163 | + /** |
| 164 | + * Checks if a document file URL is effectively exposed through a visible system announcement. |
| 165 | + * |
| 166 | + * This centralizes the logic used by different parts of the platform (e.g. voters, controllers) |
| 167 | + * to decide if a file coming from personal files can be considered "public" because it is |
| 168 | + * embedded inside a system announcement that is visible to the current user. |
| 169 | + * |
| 170 | + * @param string $pathInfo Full request path (e.g. /r/document/files/{uuid}/view) |
| 171 | + * @param string|null $identifier File identifier extracted from the URL (usually a UUID) |
| 172 | + * @param UserInterface|null $user Current user, or null to behave as anonymous |
| 173 | + * @param string $locale Current locale used to fetch announcements |
| 174 | + */ |
| 175 | + public function isFilePathExposedByVisibleAnnouncement( |
| 176 | + string $pathInfo, |
| 177 | + ?string $identifier, |
| 178 | + ?UserInterface $user, |
| 179 | + string $locale |
| 180 | + ): bool { |
| 181 | + // Only relax security for the document file viewer route. |
| 182 | + if ('' === $pathInfo || !str_contains($pathInfo, '/r/document/files/')) { |
| 183 | + return false; |
| 184 | + } |
| 185 | + |
| 186 | + // Normalize user: if no authenticated user is provided, behave as anonymous. |
| 187 | + if (null === $user) { |
| 188 | + $anon = new User(); |
| 189 | + $anon->setRoles(['ROLE_ANONYMOUS']); |
| 190 | + $user = $anon; |
| 191 | + } |
| 192 | + |
| 193 | + $accessUrl = $this->accessUrlHelper->getCurrent(); |
| 194 | + |
| 195 | + // Fetch announcements that are visible for the given user, URL and locale. |
| 196 | + $announcements = $this->sysAnnouncementRepository->getAnnouncements( |
| 197 | + $user, |
| 198 | + $accessUrl, |
| 199 | + $locale |
| 200 | + ); |
| 201 | + |
| 202 | + foreach ($announcements as $item) { |
| 203 | + $content = ''; |
| 204 | + |
| 205 | + if (\is_array($item)) { |
| 206 | + $content = (string) ($item['content'] ?? ''); |
| 207 | + } elseif (\is_object($item) && method_exists($item, 'getContent')) { |
| 208 | + $content = (string) $item->getContent(); |
| 209 | + } |
| 210 | + |
| 211 | + if ('' === $content) { |
| 212 | + continue; |
| 213 | + } |
| 214 | + |
| 215 | + // Check if the announcement HTML contains the viewer path or the identifier. |
| 216 | + if ( |
| 217 | + str_contains($content, $pathInfo) |
| 218 | + || ($identifier && str_contains($content, $identifier)) |
| 219 | + ) { |
| 220 | + return true; |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + return false; |
| 225 | + } |
145 | 226 | } |
0 commit comments