Skip to content

Commit b85a2f3

Browse files
committed
refactor: migrated attachment page (#3834)
Added AttachmentService to avoid logic in the controller
1 parent ea83024 commit b85a2f3

File tree

6 files changed

+251
-162
lines changed

6 files changed

+251
-162
lines changed

phpmyfaq/attachment.php

Lines changed: 0 additions & 158 deletions
This file was deleted.

phpmyfaq/src/phpMyFAQ/Attachment/AbstractAttachment.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,11 @@ protected function getMeta(): bool
150150

151151
public function buildUrl(): string
152152
{
153-
return sprintf('index.php?action=attachment&id=%d', $this->id);
153+
return sprintf('./attachment/%d', $this->id);
154154
}
155155

156156
/**
157-
* Set encryption key.
157+
* Set the encryption key.
158158
*
159159
* @param string|null $key Encryption key
160160
* @param bool $default if the key is default system-wide
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
/**
4+
* Attachment service for handling attachment retrieval and permissions.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public License,
7+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
8+
* obtain one at https://mozilla.org/MPL/2.0/.
9+
*
10+
* @package phpMyFAQ
11+
* @author Thorsten Rinne <thorsten@phpmyfaq.de>
12+
* @copyright 2025 phpMyFAQ Team
13+
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14+
* @link https://www.phpmyfaq.de
15+
* @since 2025-01-01
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace phpMyFAQ\Attachment;
21+
22+
use phpMyFAQ\Configuration;
23+
use phpMyFAQ\Faq\Permission;
24+
use phpMyFAQ\Permission\MediumPermission;
25+
use phpMyFAQ\Translation;
26+
use phpMyFAQ\User\CurrentUser;
27+
28+
/**
29+
* Service for attachment operations and permission checks.
30+
*/
31+
final class AttachmentService
32+
{
33+
public function __construct(
34+
private readonly Configuration $configuration,
35+
private readonly CurrentUser $currentUser,
36+
private readonly Permission $faqPermission,
37+
) {
38+
}
39+
40+
/**
41+
* Retrieves an attachment by ID.
42+
*
43+
* @throws AttachmentException
44+
*/
45+
public function getAttachment(int $attachmentId): ?AbstractAttachment
46+
{
47+
return AttachmentFactory::create($attachmentId);
48+
}
49+
50+
/**
51+
* Checks if the current user has permission to download an attachment.
52+
*/
53+
public function canDownloadAttachment(AbstractAttachment $attachment): bool
54+
{
55+
// Allow downloads for guests if configured
56+
if ($this->configuration->get('records.allowDownloadsForGuests')) {
57+
return true;
58+
}
59+
60+
// Check group and user permissions
61+
$hasGroupPermission = $this->checkGroupPermission($attachment);
62+
$hasUserPermission = $this->checkUserPermission($attachment);
63+
$userRights = $this->getUserRights();
64+
65+
return (
66+
($hasGroupPermission || $hasGroupPermission && $hasUserPermission)
67+
&& isset($userRights['dlattachment'])
68+
&& $userRights['dlattachment']
69+
);
70+
}
71+
72+
/**
73+
* Checks group permission for an attachment.
74+
*/
75+
private function checkGroupPermission(AbstractAttachment $attachment): bool
76+
{
77+
$groupPermission = $this->faqPermission->get(Permission::GROUP, $attachment->getRecordId());
78+
79+
if (!$this->currentUser->perm instanceof MediumPermission) {
80+
return true;
81+
}
82+
83+
if ($groupPermission === []) {
84+
return false;
85+
}
86+
87+
foreach ($this->currentUser->perm->getUserGroups($this->currentUser->getUserId()) as $userGroup) {
88+
if (in_array($userGroup, $groupPermission, true)) {
89+
return true;
90+
}
91+
}
92+
93+
return false;
94+
}
95+
96+
/**
97+
* Checks user permission for an attachment.
98+
*/
99+
private function checkUserPermission(AbstractAttachment $attachment): bool
100+
{
101+
$userPermission = $this->faqPermission->get(Permission::USER, $attachment->getRecordId());
102+
return in_array($this->currentUser->getUserId(), $userPermission, true);
103+
}
104+
105+
/**
106+
* Gets all user rights.
107+
*
108+
* @return array<string, bool>
109+
*/
110+
private function getUserRights(): array
111+
{
112+
$permission = [];
113+
114+
if (!$this->currentUser->isLoggedIn()) {
115+
return $permission;
116+
}
117+
118+
// Read all rights, set false
119+
$allRights = $this->currentUser->perm->getAllRightsData();
120+
foreach ($allRights as $right) {
121+
$permission[$right['name']] = false;
122+
}
123+
124+
// Check user rights, set true
125+
$allUserRights = $this->currentUser->perm->getAllUserRights($this->currentUser->getUserId());
126+
foreach ($allRights as $allRight) {
127+
if (in_array($allRight['right_id'], $allUserRights, true)) {
128+
$permission[$allRight['name']] = true;
129+
}
130+
}
131+
132+
return $permission;
133+
}
134+
135+
/**
136+
* Gets an error message for attachment exceptions.
137+
*/
138+
public function getAttachmentErrorMessage(AttachmentException $exception): string
139+
{
140+
return Translation::get(key: 'msgAttachmentInvalid') . ' (' . $exception->getMessage() . ')';
141+
}
142+
143+
/**
144+
* Gets generic attachment error message.
145+
*/
146+
public function getGenericErrorMessage(): string
147+
{
148+
return Translation::get(key: 'msgAttachmentInvalid');
149+
}
150+
}

0 commit comments

Comments
 (0)