@@ -229,7 +229,7 @@ |
||
229 | 229 | 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'), |
230 | 230 | ] |
231 | 231 | ]); |
232 | - } elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) { |
|
232 | + } elseif ($recoveryEnabledForUser && !$validRecoveryPassword) { |
|
233 | 233 | return new JSONResponse([ |
234 | 234 | 'status' => 'error', |
235 | 235 | 'data' => [ |
@@ -49,235 +49,235 @@ |
||
49 | 49 | use OCP\IUserSession; |
50 | 50 | |
51 | 51 | class ChangePasswordController extends Controller { |
52 | - private ?string $userId; |
|
53 | - private IUserManager $userManager; |
|
54 | - private IL10N $l; |
|
55 | - private GroupManager $groupManager; |
|
56 | - private Session $userSession; |
|
57 | - private IAppManager $appManager; |
|
52 | + private ?string $userId; |
|
53 | + private IUserManager $userManager; |
|
54 | + private IL10N $l; |
|
55 | + private GroupManager $groupManager; |
|
56 | + private Session $userSession; |
|
57 | + private IAppManager $appManager; |
|
58 | 58 | |
59 | - public function __construct(string $appName, |
|
60 | - IRequest $request, |
|
61 | - ?string $userId, |
|
62 | - IUserManager $userManager, |
|
63 | - IUserSession $userSession, |
|
64 | - IGroupManager $groupManager, |
|
65 | - IAppManager $appManager, |
|
66 | - IL10N $l) { |
|
67 | - parent::__construct($appName, $request); |
|
59 | + public function __construct(string $appName, |
|
60 | + IRequest $request, |
|
61 | + ?string $userId, |
|
62 | + IUserManager $userManager, |
|
63 | + IUserSession $userSession, |
|
64 | + IGroupManager $groupManager, |
|
65 | + IAppManager $appManager, |
|
66 | + IL10N $l) { |
|
67 | + parent::__construct($appName, $request); |
|
68 | 68 | |
69 | - $this->userId = $userId; |
|
70 | - $this->userManager = $userManager; |
|
71 | - $this->userSession = $userSession; |
|
72 | - $this->groupManager = $groupManager; |
|
73 | - $this->appManager = $appManager; |
|
74 | - $this->l = $l; |
|
75 | - } |
|
69 | + $this->userId = $userId; |
|
70 | + $this->userManager = $userManager; |
|
71 | + $this->userSession = $userSession; |
|
72 | + $this->groupManager = $groupManager; |
|
73 | + $this->appManager = $appManager; |
|
74 | + $this->l = $l; |
|
75 | + } |
|
76 | 76 | |
77 | - /** |
|
78 | - * @NoAdminRequired |
|
79 | - * @NoSubAdminRequired |
|
80 | - * @BruteForceProtection(action=changePersonalPassword) |
|
81 | - */ |
|
82 | - public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse { |
|
83 | - $loginName = $this->userSession->getLoginName(); |
|
84 | - /** @var IUser $user */ |
|
85 | - $user = $this->userManager->checkPassword($loginName, $oldpassword); |
|
86 | - if ($user === false) { |
|
87 | - $response = new JSONResponse([ |
|
88 | - 'status' => 'error', |
|
89 | - 'data' => [ |
|
90 | - 'message' => $this->l->t('Wrong password'), |
|
91 | - ], |
|
92 | - ]); |
|
93 | - $response->throttle(); |
|
94 | - return $response; |
|
95 | - } |
|
77 | + /** |
|
78 | + * @NoAdminRequired |
|
79 | + * @NoSubAdminRequired |
|
80 | + * @BruteForceProtection(action=changePersonalPassword) |
|
81 | + */ |
|
82 | + public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse { |
|
83 | + $loginName = $this->userSession->getLoginName(); |
|
84 | + /** @var IUser $user */ |
|
85 | + $user = $this->userManager->checkPassword($loginName, $oldpassword); |
|
86 | + if ($user === false) { |
|
87 | + $response = new JSONResponse([ |
|
88 | + 'status' => 'error', |
|
89 | + 'data' => [ |
|
90 | + 'message' => $this->l->t('Wrong password'), |
|
91 | + ], |
|
92 | + ]); |
|
93 | + $response->throttle(); |
|
94 | + return $response; |
|
95 | + } |
|
96 | 96 | |
97 | - try { |
|
98 | - if ($newpassword === null || strlen($newpassword) > IUserManager::MAX_PASSWORD_LENGTH || $user->setPassword($newpassword) === false) { |
|
99 | - return new JSONResponse([ |
|
100 | - 'status' => 'error', |
|
101 | - 'data' => [ |
|
102 | - 'message' => $this->l->t('Unable to change personal password'), |
|
103 | - ], |
|
104 | - ]); |
|
105 | - } |
|
106 | - // password policy app throws exception |
|
107 | - } catch (HintException $e) { |
|
108 | - return new JSONResponse([ |
|
109 | - 'status' => 'error', |
|
110 | - 'data' => [ |
|
111 | - 'message' => $e->getHint(), |
|
112 | - ], |
|
113 | - ]); |
|
114 | - } |
|
97 | + try { |
|
98 | + if ($newpassword === null || strlen($newpassword) > IUserManager::MAX_PASSWORD_LENGTH || $user->setPassword($newpassword) === false) { |
|
99 | + return new JSONResponse([ |
|
100 | + 'status' => 'error', |
|
101 | + 'data' => [ |
|
102 | + 'message' => $this->l->t('Unable to change personal password'), |
|
103 | + ], |
|
104 | + ]); |
|
105 | + } |
|
106 | + // password policy app throws exception |
|
107 | + } catch (HintException $e) { |
|
108 | + return new JSONResponse([ |
|
109 | + 'status' => 'error', |
|
110 | + 'data' => [ |
|
111 | + 'message' => $e->getHint(), |
|
112 | + ], |
|
113 | + ]); |
|
114 | + } |
|
115 | 115 | |
116 | - $this->userSession->updateSessionTokenPassword($newpassword); |
|
116 | + $this->userSession->updateSessionTokenPassword($newpassword); |
|
117 | 117 | |
118 | - return new JSONResponse([ |
|
119 | - 'status' => 'success', |
|
120 | - 'data' => [ |
|
121 | - 'message' => $this->l->t('Saved'), |
|
122 | - ], |
|
123 | - ]); |
|
124 | - } |
|
118 | + return new JSONResponse([ |
|
119 | + 'status' => 'success', |
|
120 | + 'data' => [ |
|
121 | + 'message' => $this->l->t('Saved'), |
|
122 | + ], |
|
123 | + ]); |
|
124 | + } |
|
125 | 125 | |
126 | - /** |
|
127 | - * @NoAdminRequired |
|
128 | - * @PasswordConfirmationRequired |
|
129 | - */ |
|
130 | - public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse { |
|
131 | - if ($username === null) { |
|
132 | - return new JSONResponse([ |
|
133 | - 'status' => 'error', |
|
134 | - 'data' => [ |
|
135 | - 'message' => $this->l->t('No user supplied'), |
|
136 | - ], |
|
137 | - ]); |
|
138 | - } |
|
126 | + /** |
|
127 | + * @NoAdminRequired |
|
128 | + * @PasswordConfirmationRequired |
|
129 | + */ |
|
130 | + public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse { |
|
131 | + if ($username === null) { |
|
132 | + return new JSONResponse([ |
|
133 | + 'status' => 'error', |
|
134 | + 'data' => [ |
|
135 | + 'message' => $this->l->t('No user supplied'), |
|
136 | + ], |
|
137 | + ]); |
|
138 | + } |
|
139 | 139 | |
140 | - if ($password === null) { |
|
141 | - return new JSONResponse([ |
|
142 | - 'status' => 'error', |
|
143 | - 'data' => [ |
|
144 | - 'message' => $this->l->t('Unable to change password'), |
|
145 | - ], |
|
146 | - ]); |
|
147 | - } |
|
140 | + if ($password === null) { |
|
141 | + return new JSONResponse([ |
|
142 | + 'status' => 'error', |
|
143 | + 'data' => [ |
|
144 | + 'message' => $this->l->t('Unable to change password'), |
|
145 | + ], |
|
146 | + ]); |
|
147 | + } |
|
148 | 148 | |
149 | - if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) { |
|
150 | - return new JSONResponse([ |
|
151 | - 'status' => 'error', |
|
152 | - 'data' => [ |
|
153 | - 'message' => $this->l->t('Unable to change password. Password too long.'), |
|
154 | - ], |
|
155 | - ]); |
|
156 | - } |
|
149 | + if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) { |
|
150 | + return new JSONResponse([ |
|
151 | + 'status' => 'error', |
|
152 | + 'data' => [ |
|
153 | + 'message' => $this->l->t('Unable to change password. Password too long.'), |
|
154 | + ], |
|
155 | + ]); |
|
156 | + } |
|
157 | 157 | |
158 | - $currentUser = $this->userSession->getUser(); |
|
159 | - $targetUser = $this->userManager->get($username); |
|
160 | - if ($currentUser === null || $targetUser === null || |
|
161 | - !($this->groupManager->isAdmin($this->userId) || |
|
162 | - $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser)) |
|
163 | - ) { |
|
164 | - return new JSONResponse([ |
|
165 | - 'status' => 'error', |
|
166 | - 'data' => [ |
|
167 | - 'message' => $this->l->t('Authentication error'), |
|
168 | - ], |
|
169 | - ]); |
|
170 | - } |
|
158 | + $currentUser = $this->userSession->getUser(); |
|
159 | + $targetUser = $this->userManager->get($username); |
|
160 | + if ($currentUser === null || $targetUser === null || |
|
161 | + !($this->groupManager->isAdmin($this->userId) || |
|
162 | + $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser)) |
|
163 | + ) { |
|
164 | + return new JSONResponse([ |
|
165 | + 'status' => 'error', |
|
166 | + 'data' => [ |
|
167 | + 'message' => $this->l->t('Authentication error'), |
|
168 | + ], |
|
169 | + ]); |
|
170 | + } |
|
171 | 171 | |
172 | - if ($this->appManager->isEnabledForUser('encryption')) { |
|
173 | - //handle the recovery case |
|
174 | - $crypt = new \OCA\Encryption\Crypto\Crypt( |
|
175 | - \OC::$server->getLogger(), |
|
176 | - \OC::$server->getUserSession(), |
|
177 | - \OC::$server->getConfig(), |
|
178 | - \OC::$server->getL10N('encryption')); |
|
179 | - $keyStorage = \OC::$server->getEncryptionKeyStorage(); |
|
180 | - $util = new \OCA\Encryption\Util( |
|
181 | - new \OC\Files\View(), |
|
182 | - $crypt, |
|
183 | - \OC::$server->getLogger(), |
|
184 | - \OC::$server->getUserSession(), |
|
185 | - \OC::$server->getConfig(), |
|
186 | - \OC::$server->getUserManager()); |
|
187 | - $keyManager = new \OCA\Encryption\KeyManager( |
|
188 | - $keyStorage, |
|
189 | - $crypt, |
|
190 | - \OC::$server->getConfig(), |
|
191 | - \OC::$server->getUserSession(), |
|
192 | - new \OCA\Encryption\Session(\OC::$server->getSession()), |
|
193 | - \OC::$server->getLogger(), |
|
194 | - $util, |
|
195 | - \OC::$server->getLockingProvider() |
|
196 | - ); |
|
197 | - $recovery = new \OCA\Encryption\Recovery( |
|
198 | - \OC::$server->getUserSession(), |
|
199 | - $crypt, |
|
200 | - $keyManager, |
|
201 | - \OC::$server->getConfig(), |
|
202 | - \OC::$server->getEncryptionFilesHelper(), |
|
203 | - new \OC\Files\View()); |
|
204 | - $recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled(); |
|
172 | + if ($this->appManager->isEnabledForUser('encryption')) { |
|
173 | + //handle the recovery case |
|
174 | + $crypt = new \OCA\Encryption\Crypto\Crypt( |
|
175 | + \OC::$server->getLogger(), |
|
176 | + \OC::$server->getUserSession(), |
|
177 | + \OC::$server->getConfig(), |
|
178 | + \OC::$server->getL10N('encryption')); |
|
179 | + $keyStorage = \OC::$server->getEncryptionKeyStorage(); |
|
180 | + $util = new \OCA\Encryption\Util( |
|
181 | + new \OC\Files\View(), |
|
182 | + $crypt, |
|
183 | + \OC::$server->getLogger(), |
|
184 | + \OC::$server->getUserSession(), |
|
185 | + \OC::$server->getConfig(), |
|
186 | + \OC::$server->getUserManager()); |
|
187 | + $keyManager = new \OCA\Encryption\KeyManager( |
|
188 | + $keyStorage, |
|
189 | + $crypt, |
|
190 | + \OC::$server->getConfig(), |
|
191 | + \OC::$server->getUserSession(), |
|
192 | + new \OCA\Encryption\Session(\OC::$server->getSession()), |
|
193 | + \OC::$server->getLogger(), |
|
194 | + $util, |
|
195 | + \OC::$server->getLockingProvider() |
|
196 | + ); |
|
197 | + $recovery = new \OCA\Encryption\Recovery( |
|
198 | + \OC::$server->getUserSession(), |
|
199 | + $crypt, |
|
200 | + $keyManager, |
|
201 | + \OC::$server->getConfig(), |
|
202 | + \OC::$server->getEncryptionFilesHelper(), |
|
203 | + new \OC\Files\View()); |
|
204 | + $recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled(); |
|
205 | 205 | |
206 | - $validRecoveryPassword = false; |
|
207 | - $recoveryEnabledForUser = false; |
|
208 | - if ($recoveryAdminEnabled) { |
|
209 | - $validRecoveryPassword = $keyManager->checkRecoveryPassword($recoveryPassword); |
|
210 | - $recoveryEnabledForUser = $recovery->isRecoveryEnabledForUser($username); |
|
211 | - } |
|
206 | + $validRecoveryPassword = false; |
|
207 | + $recoveryEnabledForUser = false; |
|
208 | + if ($recoveryAdminEnabled) { |
|
209 | + $validRecoveryPassword = $keyManager->checkRecoveryPassword($recoveryPassword); |
|
210 | + $recoveryEnabledForUser = $recovery->isRecoveryEnabledForUser($username); |
|
211 | + } |
|
212 | 212 | |
213 | - if ($recoveryEnabledForUser && $recoveryPassword === '') { |
|
214 | - return new JSONResponse([ |
|
215 | - 'status' => 'error', |
|
216 | - 'data' => [ |
|
217 | - 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'), |
|
218 | - ] |
|
219 | - ]); |
|
220 | - } elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) { |
|
221 | - return new JSONResponse([ |
|
222 | - 'status' => 'error', |
|
223 | - 'data' => [ |
|
224 | - 'message' => $this->l->t('Wrong admin recovery password. Please check the password and try again.'), |
|
225 | - ] |
|
226 | - ]); |
|
227 | - } else { // now we know that everything is fine regarding the recovery password, let's try to change the password |
|
228 | - try { |
|
229 | - $result = $targetUser->setPassword($password, $recoveryPassword); |
|
230 | - // password policy app throws exception |
|
231 | - } catch (HintException $e) { |
|
232 | - return new JSONResponse([ |
|
233 | - 'status' => 'error', |
|
234 | - 'data' => [ |
|
235 | - 'message' => $e->getHint(), |
|
236 | - ], |
|
237 | - ]); |
|
238 | - } |
|
239 | - if (!$result && $recoveryEnabledForUser) { |
|
240 | - return new JSONResponse([ |
|
241 | - 'status' => 'error', |
|
242 | - 'data' => [ |
|
243 | - 'message' => $this->l->t('Backend does not support password change, but the user\'s encryption key was updated.'), |
|
244 | - ] |
|
245 | - ]); |
|
246 | - } elseif (!$result && !$recoveryEnabledForUser) { |
|
247 | - return new JSONResponse([ |
|
248 | - 'status' => 'error', |
|
249 | - 'data' => [ |
|
250 | - 'message' => $this->l->t('Unable to change password'), |
|
251 | - ] |
|
252 | - ]); |
|
253 | - } |
|
254 | - } |
|
255 | - } else { |
|
256 | - try { |
|
257 | - if ($targetUser->setPassword($password) === false) { |
|
258 | - return new JSONResponse([ |
|
259 | - 'status' => 'error', |
|
260 | - 'data' => [ |
|
261 | - 'message' => $this->l->t('Unable to change password'), |
|
262 | - ], |
|
263 | - ]); |
|
264 | - } |
|
265 | - // password policy app throws exception |
|
266 | - } catch (HintException $e) { |
|
267 | - return new JSONResponse([ |
|
268 | - 'status' => 'error', |
|
269 | - 'data' => [ |
|
270 | - 'message' => $e->getHint(), |
|
271 | - ], |
|
272 | - ]); |
|
273 | - } |
|
274 | - } |
|
213 | + if ($recoveryEnabledForUser && $recoveryPassword === '') { |
|
214 | + return new JSONResponse([ |
|
215 | + 'status' => 'error', |
|
216 | + 'data' => [ |
|
217 | + 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'), |
|
218 | + ] |
|
219 | + ]); |
|
220 | + } elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) { |
|
221 | + return new JSONResponse([ |
|
222 | + 'status' => 'error', |
|
223 | + 'data' => [ |
|
224 | + 'message' => $this->l->t('Wrong admin recovery password. Please check the password and try again.'), |
|
225 | + ] |
|
226 | + ]); |
|
227 | + } else { // now we know that everything is fine regarding the recovery password, let's try to change the password |
|
228 | + try { |
|
229 | + $result = $targetUser->setPassword($password, $recoveryPassword); |
|
230 | + // password policy app throws exception |
|
231 | + } catch (HintException $e) { |
|
232 | + return new JSONResponse([ |
|
233 | + 'status' => 'error', |
|
234 | + 'data' => [ |
|
235 | + 'message' => $e->getHint(), |
|
236 | + ], |
|
237 | + ]); |
|
238 | + } |
|
239 | + if (!$result && $recoveryEnabledForUser) { |
|
240 | + return new JSONResponse([ |
|
241 | + 'status' => 'error', |
|
242 | + 'data' => [ |
|
243 | + 'message' => $this->l->t('Backend does not support password change, but the user\'s encryption key was updated.'), |
|
244 | + ] |
|
245 | + ]); |
|
246 | + } elseif (!$result && !$recoveryEnabledForUser) { |
|
247 | + return new JSONResponse([ |
|
248 | + 'status' => 'error', |
|
249 | + 'data' => [ |
|
250 | + 'message' => $this->l->t('Unable to change password'), |
|
251 | + ] |
|
252 | + ]); |
|
253 | + } |
|
254 | + } |
|
255 | + } else { |
|
256 | + try { |
|
257 | + if ($targetUser->setPassword($password) === false) { |
|
258 | + return new JSONResponse([ |
|
259 | + 'status' => 'error', |
|
260 | + 'data' => [ |
|
261 | + 'message' => $this->l->t('Unable to change password'), |
|
262 | + ], |
|
263 | + ]); |
|
264 | + } |
|
265 | + // password policy app throws exception |
|
266 | + } catch (HintException $e) { |
|
267 | + return new JSONResponse([ |
|
268 | + 'status' => 'error', |
|
269 | + 'data' => [ |
|
270 | + 'message' => $e->getHint(), |
|
271 | + ], |
|
272 | + ]); |
|
273 | + } |
|
274 | + } |
|
275 | 275 | |
276 | - return new JSONResponse([ |
|
277 | - 'status' => 'success', |
|
278 | - 'data' => [ |
|
279 | - 'username' => $username, |
|
280 | - ], |
|
281 | - ]); |
|
282 | - } |
|
276 | + return new JSONResponse([ |
|
277 | + 'status' => 'success', |
|
278 | + 'data' => [ |
|
279 | + 'username' => $username, |
|
280 | + ], |
|
281 | + ]); |
|
282 | + } |
|
283 | 283 | } |
@@ -46,9 +46,9 @@ |
||
46 | 46 | </div> |
47 | 47 | |
48 | 48 | <div id="postsetupchecks" data-check-wellknown="<?php if ($_['checkForWorkingWellKnownSetup']) { |
49 | - p('true'); |
|
49 | + p('true'); |
|
50 | 50 | } else { |
51 | - p('false'); |
|
51 | + p('false'); |
|
52 | 52 | } ?>"> |
53 | 53 | <ul class="errors hidden"></ul> |
54 | 54 | <ul class="warnings hidden"></ul> |
@@ -28,21 +28,21 @@ discard block |
||
28 | 28 | ?> |
29 | 29 | |
30 | 30 | <div id="security-warning" class="section"> |
31 | - <h2 class="inlineblock"><?php p($l->t('Security & setup warnings'));?></h2> |
|
32 | - <a target="_blank" rel="noreferrer" class="icon-info" title="<?php p($l->t('Open documentation'));?>" href="<?php p(link_to_docs('admin-warnings')); ?>"></a> |
|
33 | - <p class="settings-hint"><?php p($l->t('It\'s important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the linked documentation for more information.'));?></p> |
|
31 | + <h2 class="inlineblock"><?php p($l->t('Security & setup warnings')); ?></h2> |
|
32 | + <a target="_blank" rel="noreferrer" class="icon-info" title="<?php p($l->t('Open documentation')); ?>" href="<?php p(link_to_docs('admin-warnings')); ?>"></a> |
|
33 | + <p class="settings-hint"><?php p($l->t('It\'s important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the linked documentation for more information.')); ?></p> |
|
34 | 34 | |
35 | 35 | <div id="security-warning-state-ok" class="hidden"> |
36 | - <span class="icon icon-checkmark-white"></span><span class="message"><?php p($l->t('All checks passed.'));?></span> |
|
36 | + <span class="icon icon-checkmark-white"></span><span class="message"><?php p($l->t('All checks passed.')); ?></span> |
|
37 | 37 | </div> |
38 | 38 | <div id="security-warning-state-failure" class="hidden"> |
39 | - <span class="icon icon-close-white"></span><span class="message"><?php p($l->t('There are some errors regarding your setup.'));?></span> |
|
39 | + <span class="icon icon-close-white"></span><span class="message"><?php p($l->t('There are some errors regarding your setup.')); ?></span> |
|
40 | 40 | </div> |
41 | 41 | <div id="security-warning-state-warning" class="hidden"> |
42 | - <span class="icon icon-error-white"></span><span class="message"><?php p($l->t('There are some warnings regarding your setup.'));?></span> |
|
42 | + <span class="icon icon-error-white"></span><span class="message"><?php p($l->t('There are some warnings regarding your setup.')); ?></span> |
|
43 | 43 | </div> |
44 | 44 | <div id="security-warning-state-loading"> |
45 | - <span class="icon loading"></span><span class="message"><?php p($l->t('Checking for system and security issues.'));?></span> |
|
45 | + <span class="icon loading"></span><span class="message"><?php p($l->t('Checking for system and security issues.')); ?></span> |
|
46 | 46 | </div> |
47 | 47 | |
48 | 48 | <div id="postsetupchecks" data-check-wellknown="<?php if ($_['checkForWorkingWellKnownSetup']) { |
@@ -59,14 +59,14 @@ discard block |
||
59 | 59 | </p> |
60 | 60 | |
61 | 61 | <p class="extra-top-margin"> |
62 | - <?php print_unescaped($l->t('Check the security of your Nextcloud over <a target="_blank" rel="noreferrer noopener" href="%s">our security scan ↗</a>.', ['https://scan.nextcloud.com']));?> |
|
62 | + <?php print_unescaped($l->t('Check the security of your Nextcloud over <a target="_blank" rel="noreferrer noopener" href="%s">our security scan ↗</a>.', ['https://scan.nextcloud.com'])); ?> |
|
63 | 63 | </p> |
64 | 64 | |
65 | 65 | </div> |
66 | 66 | |
67 | 67 | <div id="version" class="section"> |
68 | 68 | <!-- should be the last part, so Updater can follow if enabled (it has no heading therefore). --> |
69 | - <h2><?php p($l->t('Version'));?></h2> |
|
69 | + <h2><?php p($l->t('Version')); ?></h2> |
|
70 | 70 | <?php if ($theme->getTitle() === 'Nextcloud'): ?> |
71 | 71 | <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank">Nextcloud Hub 3</a> (<?php p(OC_Util::getHumanVersion()) ?>)</strong></p> |
72 | 72 | <?php else: ?> |
@@ -69,7 +69,10 @@ |
||
69 | 69 | <h2><?php p($l->t('Version'));?></h2> |
70 | 70 | <?php if ($theme->getTitle() === 'Nextcloud'): ?> |
71 | 71 | <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank">Nextcloud Hub 3</a> (<?php p(OC_Util::getHumanVersion()) ?>)</strong></p> |
72 | - <?php else: ?> |
|
73 | - <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank"><?php p($theme->getTitle()); ?></a> <?php p(OC_Util::getHumanVersion()) ?></strong></p> |
|
72 | + <?php else { |
|
73 | + : ?> |
|
74 | + <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); |
|
75 | +} |
|
76 | +?>" rel="noreferrer noopener" target="_blank"><?php p($theme->getTitle()); ?></a> <?php p(OC_Util::getHumanVersion()) ?></strong></p> |
|
74 | 77 | <?php endif; ?> |
75 | 78 | </div> |
@@ -27,7 +27,7 @@ |
||
27 | 27 | ?> |
28 | 28 | |
29 | 29 | <?php foreach ($_['forms'] as $form) { |
30 | - if (isset($form['form'])) {?> |
|
30 | + if (isset($form['form'])) {?> |
|
31 | 31 | <div id="<?php isset($form['anchor']) ? p($form['anchor']) : p('');?>"><?php print_unescaped($form['form']);?></div> |
32 | 32 | <?php } |
33 | 33 | } ?> |
@@ -28,6 +28,6 @@ |
||
28 | 28 | |
29 | 29 | <?php foreach ($_['forms'] as $form) { |
30 | 30 | if (isset($form['form'])) {?> |
31 | - <div id="<?php isset($form['anchor']) ? p($form['anchor']) : p('');?>"><?php print_unescaped($form['form']);?></div> |
|
31 | + <div id="<?php isset($form['anchor']) ? p($form['anchor']) : p(''); ?>"><?php print_unescaped($form['form']); ?></div> |
|
32 | 32 | <?php } |
33 | 33 | } ?> |
@@ -26,7 +26,7 @@ |
||
26 | 26 | |
27 | 27 | // Do we have some data to inject ? |
28 | 28 | if (is_array($_['serverData'])) { |
29 | - ?> |
|
29 | + ?> |
|
30 | 30 | <span id="serverData" data-server="<?php p(json_encode($_['serverData'])); ?>"></span> |
31 | 31 | <?php |
32 | 32 | } ?> |
@@ -31,15 +31,15 @@ |
||
31 | 31 | use OCP\Settings\ISettings; |
32 | 32 | |
33 | 33 | class PersonalSettings implements ISettings { |
34 | - public function getForm(): TemplateResponse { |
|
35 | - return new TemplateResponse(Application::APP_ID, 'settings-personal'); |
|
36 | - } |
|
34 | + public function getForm(): TemplateResponse { |
|
35 | + return new TemplateResponse(Application::APP_ID, 'settings-personal'); |
|
36 | + } |
|
37 | 37 | |
38 | - public function getSection(): string { |
|
39 | - return 'sharing'; |
|
40 | - } |
|
38 | + public function getSection(): string { |
|
39 | + return 'sharing'; |
|
40 | + } |
|
41 | 41 | |
42 | - public function getPriority(): int { |
|
43 | - return 90; |
|
44 | - } |
|
42 | + public function getPriority(): int { |
|
43 | + return 90; |
|
44 | + } |
|
45 | 45 | } |
@@ -37,100 +37,100 @@ |
||
37 | 37 | use OCP\IUser; |
38 | 38 | |
39 | 39 | class ResourceProvider implements IProvider { |
40 | - public const RESOURCE_TYPE = 'file'; |
|
41 | - |
|
42 | - /** @var IRootFolder */ |
|
43 | - protected $rootFolder; |
|
44 | - /** @var IPreview */ |
|
45 | - private $preview; |
|
46 | - /** @var IURLGenerator */ |
|
47 | - private $urlGenerator; |
|
48 | - |
|
49 | - /** @var array */ |
|
50 | - protected $nodes = []; |
|
51 | - |
|
52 | - public function __construct(IRootFolder $rootFolder, |
|
53 | - IPreview $preview, |
|
54 | - IURLGenerator $urlGenerator) { |
|
55 | - $this->rootFolder = $rootFolder; |
|
56 | - $this->preview = $preview; |
|
57 | - $this->urlGenerator = $urlGenerator; |
|
58 | - } |
|
59 | - |
|
60 | - private function getNode(IResource $resource): ?Node { |
|
61 | - if (isset($this->nodes[(int) $resource->getId()])) { |
|
62 | - return $this->nodes[(int) $resource->getId()]; |
|
63 | - } |
|
64 | - $nodes = $this->rootFolder->getById((int) $resource->getId()); |
|
65 | - if (!empty($nodes)) { |
|
66 | - $this->nodes[(int) $resource->getId()] = array_shift($nodes); |
|
67 | - return $this->nodes[(int) $resource->getId()]; |
|
68 | - } |
|
69 | - return null; |
|
70 | - } |
|
71 | - |
|
72 | - /** |
|
73 | - * @param IResource $resource |
|
74 | - * @return array |
|
75 | - * @since 16.0.0 |
|
76 | - */ |
|
77 | - public function getResourceRichObject(IResource $resource): array { |
|
78 | - if (isset($this->nodes[(int) $resource->getId()])) { |
|
79 | - $node = $this->nodes[(int) $resource->getId()]->getPath(); |
|
80 | - } else { |
|
81 | - $node = $this->getNode($resource); |
|
82 | - } |
|
83 | - |
|
84 | - if ($node instanceof Node) { |
|
85 | - $link = $this->urlGenerator->linkToRouteAbsolute( |
|
86 | - 'files.viewcontroller.showFile', |
|
87 | - ['fileid' => $resource->getId()] |
|
88 | - ); |
|
89 | - return [ |
|
90 | - 'type' => 'file', |
|
91 | - 'id' => $resource->getId(), |
|
92 | - 'name' => $node->getName(), |
|
93 | - 'path' => $node->getInternalPath(), |
|
94 | - 'link' => $link, |
|
95 | - 'mimetype' => $node->getMimetype(), |
|
96 | - 'preview-available' => $this->preview->isAvailable($node), |
|
97 | - ]; |
|
98 | - } |
|
99 | - |
|
100 | - throw new ResourceException('File not found'); |
|
101 | - } |
|
102 | - |
|
103 | - /** |
|
104 | - * Can a user/guest access the collection |
|
105 | - * |
|
106 | - * @param IResource $resource |
|
107 | - * @param IUser $user |
|
108 | - * @return bool |
|
109 | - * @since 16.0.0 |
|
110 | - */ |
|
111 | - public function canAccessResource(IResource $resource, IUser $user = null): bool { |
|
112 | - if (!$user instanceof IUser) { |
|
113 | - return false; |
|
114 | - } |
|
115 | - |
|
116 | - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); |
|
117 | - $nodes = $userFolder->getById((int) $resource->getId()); |
|
118 | - |
|
119 | - if (!empty($nodes)) { |
|
120 | - $this->nodes[(int) $resource->getId()] = array_shift($nodes); |
|
121 | - return true; |
|
122 | - } |
|
123 | - |
|
124 | - return false; |
|
125 | - } |
|
126 | - |
|
127 | - /** |
|
128 | - * Get the resource type of the provider |
|
129 | - * |
|
130 | - * @return string |
|
131 | - * @since 16.0.0 |
|
132 | - */ |
|
133 | - public function getType(): string { |
|
134 | - return self::RESOURCE_TYPE; |
|
135 | - } |
|
40 | + public const RESOURCE_TYPE = 'file'; |
|
41 | + |
|
42 | + /** @var IRootFolder */ |
|
43 | + protected $rootFolder; |
|
44 | + /** @var IPreview */ |
|
45 | + private $preview; |
|
46 | + /** @var IURLGenerator */ |
|
47 | + private $urlGenerator; |
|
48 | + |
|
49 | + /** @var array */ |
|
50 | + protected $nodes = []; |
|
51 | + |
|
52 | + public function __construct(IRootFolder $rootFolder, |
|
53 | + IPreview $preview, |
|
54 | + IURLGenerator $urlGenerator) { |
|
55 | + $this->rootFolder = $rootFolder; |
|
56 | + $this->preview = $preview; |
|
57 | + $this->urlGenerator = $urlGenerator; |
|
58 | + } |
|
59 | + |
|
60 | + private function getNode(IResource $resource): ?Node { |
|
61 | + if (isset($this->nodes[(int) $resource->getId()])) { |
|
62 | + return $this->nodes[(int) $resource->getId()]; |
|
63 | + } |
|
64 | + $nodes = $this->rootFolder->getById((int) $resource->getId()); |
|
65 | + if (!empty($nodes)) { |
|
66 | + $this->nodes[(int) $resource->getId()] = array_shift($nodes); |
|
67 | + return $this->nodes[(int) $resource->getId()]; |
|
68 | + } |
|
69 | + return null; |
|
70 | + } |
|
71 | + |
|
72 | + /** |
|
73 | + * @param IResource $resource |
|
74 | + * @return array |
|
75 | + * @since 16.0.0 |
|
76 | + */ |
|
77 | + public function getResourceRichObject(IResource $resource): array { |
|
78 | + if (isset($this->nodes[(int) $resource->getId()])) { |
|
79 | + $node = $this->nodes[(int) $resource->getId()]->getPath(); |
|
80 | + } else { |
|
81 | + $node = $this->getNode($resource); |
|
82 | + } |
|
83 | + |
|
84 | + if ($node instanceof Node) { |
|
85 | + $link = $this->urlGenerator->linkToRouteAbsolute( |
|
86 | + 'files.viewcontroller.showFile', |
|
87 | + ['fileid' => $resource->getId()] |
|
88 | + ); |
|
89 | + return [ |
|
90 | + 'type' => 'file', |
|
91 | + 'id' => $resource->getId(), |
|
92 | + 'name' => $node->getName(), |
|
93 | + 'path' => $node->getInternalPath(), |
|
94 | + 'link' => $link, |
|
95 | + 'mimetype' => $node->getMimetype(), |
|
96 | + 'preview-available' => $this->preview->isAvailable($node), |
|
97 | + ]; |
|
98 | + } |
|
99 | + |
|
100 | + throw new ResourceException('File not found'); |
|
101 | + } |
|
102 | + |
|
103 | + /** |
|
104 | + * Can a user/guest access the collection |
|
105 | + * |
|
106 | + * @param IResource $resource |
|
107 | + * @param IUser $user |
|
108 | + * @return bool |
|
109 | + * @since 16.0.0 |
|
110 | + */ |
|
111 | + public function canAccessResource(IResource $resource, IUser $user = null): bool { |
|
112 | + if (!$user instanceof IUser) { |
|
113 | + return false; |
|
114 | + } |
|
115 | + |
|
116 | + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); |
|
117 | + $nodes = $userFolder->getById((int) $resource->getId()); |
|
118 | + |
|
119 | + if (!empty($nodes)) { |
|
120 | + $this->nodes[(int) $resource->getId()] = array_shift($nodes); |
|
121 | + return true; |
|
122 | + } |
|
123 | + |
|
124 | + return false; |
|
125 | + } |
|
126 | + |
|
127 | + /** |
|
128 | + * Get the resource type of the provider |
|
129 | + * |
|
130 | + * @return string |
|
131 | + * @since 16.0.0 |
|
132 | + */ |
|
133 | + public function getType(): string { |
|
134 | + return self::RESOURCE_TYPE; |
|
135 | + } |
|
136 | 136 | } |
@@ -49,17 +49,17 @@ |
||
49 | 49 | * @param \DOMElement $errorNode |
50 | 50 | * @return void |
51 | 51 | */ |
52 | - public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) { |
|
52 | + public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) { |
|
53 | 53 | |
54 | 54 | // set ownCloud namespace |
55 | 55 | $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); |
56 | 56 | |
57 | 57 | // adding the retry node |
58 | - $error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true)); |
|
58 | + $error = $errorNode->ownerDocument->createElementNS('o:', 'o:retry', var_export($this->retry, true)); |
|
59 | 59 | $errorNode->appendChild($error); |
60 | 60 | |
61 | 61 | // adding the message node |
62 | - $error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage()); |
|
62 | + $error = $errorNode->ownerDocument->createElementNS('o:', 'o:reason', $this->getMessage()); |
|
63 | 63 | $errorNode->appendChild($error); |
64 | 64 | } |
65 | 65 | } |
@@ -26,51 +26,51 @@ |
||
26 | 26 | use Sabre\DAV\Exception; |
27 | 27 | |
28 | 28 | class InvalidPath extends Exception { |
29 | - public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
29 | + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
30 | 30 | |
31 | - /** |
|
32 | - * @var bool |
|
33 | - */ |
|
34 | - private $retry; |
|
31 | + /** |
|
32 | + * @var bool |
|
33 | + */ |
|
34 | + private $retry; |
|
35 | 35 | |
36 | - /** |
|
37 | - * @param string $message |
|
38 | - * @param bool $retry |
|
39 | - * @param \Exception|null $previous |
|
40 | - */ |
|
41 | - public function __construct($message, $retry = false, \Exception $previous = null) { |
|
42 | - parent::__construct($message, 0, $previous); |
|
43 | - $this->retry = $retry; |
|
44 | - } |
|
36 | + /** |
|
37 | + * @param string $message |
|
38 | + * @param bool $retry |
|
39 | + * @param \Exception|null $previous |
|
40 | + */ |
|
41 | + public function __construct($message, $retry = false, \Exception $previous = null) { |
|
42 | + parent::__construct($message, 0, $previous); |
|
43 | + $this->retry = $retry; |
|
44 | + } |
|
45 | 45 | |
46 | - /** |
|
47 | - * Returns the HTTP status code for this exception |
|
48 | - * |
|
49 | - * @return int |
|
50 | - */ |
|
51 | - public function getHTTPCode() { |
|
52 | - return 400; |
|
53 | - } |
|
46 | + /** |
|
47 | + * Returns the HTTP status code for this exception |
|
48 | + * |
|
49 | + * @return int |
|
50 | + */ |
|
51 | + public function getHTTPCode() { |
|
52 | + return 400; |
|
53 | + } |
|
54 | 54 | |
55 | - /** |
|
56 | - * This method allows the exception to include additional information |
|
57 | - * into the WebDAV error response |
|
58 | - * |
|
59 | - * @param \Sabre\DAV\Server $server |
|
60 | - * @param \DOMElement $errorNode |
|
61 | - * @return void |
|
62 | - */ |
|
63 | - public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) { |
|
55 | + /** |
|
56 | + * This method allows the exception to include additional information |
|
57 | + * into the WebDAV error response |
|
58 | + * |
|
59 | + * @param \Sabre\DAV\Server $server |
|
60 | + * @param \DOMElement $errorNode |
|
61 | + * @return void |
|
62 | + */ |
|
63 | + public function serialize(\Sabre\DAV\Server $server,\DOMElement $errorNode) { |
|
64 | 64 | |
65 | - // set ownCloud namespace |
|
66 | - $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); |
|
65 | + // set ownCloud namespace |
|
66 | + $errorNode->setAttribute('xmlns:o', self::NS_OWNCLOUD); |
|
67 | 67 | |
68 | - // adding the retry node |
|
69 | - $error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true)); |
|
70 | - $errorNode->appendChild($error); |
|
68 | + // adding the retry node |
|
69 | + $error = $errorNode->ownerDocument->createElementNS('o:','o:retry', var_export($this->retry, true)); |
|
70 | + $errorNode->appendChild($error); |
|
71 | 71 | |
72 | - // adding the message node |
|
73 | - $error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage()); |
|
74 | - $errorNode->appendChild($error); |
|
75 | - } |
|
72 | + // adding the message node |
|
73 | + $error = $errorNode->ownerDocument->createElementNS('o:','o:reason', $this->getMessage()); |
|
74 | + $errorNode->appendChild($error); |
|
75 | + } |
|
76 | 76 | } |
@@ -92,7 +92,7 @@ discard block |
||
92 | 92 | return; |
93 | 93 | } |
94 | 94 | |
95 | - return $this->checkQuota($parent->getPath() . '/' . basename($uri)); |
|
95 | + return $this->checkQuota($parent->getPath().'/'.basename($uri)); |
|
96 | 96 | } |
97 | 97 | |
98 | 98 | /** |
@@ -163,7 +163,7 @@ discard block |
||
163 | 163 | // there is still enough space for the remaining chunks |
164 | 164 | $length -= $chunkHandler->getCurrentSize(); |
165 | 165 | // use target file name for free space check in case of shared files |
166 | - $path = rtrim($parentPath, '/') . '/' . $info['name']; |
|
166 | + $path = rtrim($parentPath, '/').'/'.$info['name']; |
|
167 | 167 | } |
168 | 168 | $freeSpace = $this->getFreeSpace($path); |
169 | 169 | if ($freeSpace >= 0 && $length > $freeSpace) { |
@@ -44,207 +44,207 @@ |
||
44 | 44 | * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License |
45 | 45 | */ |
46 | 46 | class QuotaPlugin extends \Sabre\DAV\ServerPlugin { |
47 | - /** @var \OC\Files\View */ |
|
48 | - private $view; |
|
49 | - |
|
50 | - /** |
|
51 | - * Reference to main server object |
|
52 | - * |
|
53 | - * @var \Sabre\DAV\Server |
|
54 | - */ |
|
55 | - private $server; |
|
56 | - |
|
57 | - /** |
|
58 | - * @param \OC\Files\View $view |
|
59 | - */ |
|
60 | - public function __construct($view) { |
|
61 | - $this->view = $view; |
|
62 | - } |
|
63 | - |
|
64 | - /** |
|
65 | - * This initializes the plugin. |
|
66 | - * |
|
67 | - * This function is called by \Sabre\DAV\Server, after |
|
68 | - * addPlugin is called. |
|
69 | - * |
|
70 | - * This method should set up the requires event subscriptions. |
|
71 | - * |
|
72 | - * @param \Sabre\DAV\Server $server |
|
73 | - * @return void |
|
74 | - */ |
|
75 | - public function initialize(\Sabre\DAV\Server $server) { |
|
76 | - $this->server = $server; |
|
77 | - |
|
78 | - $server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10); |
|
79 | - $server->on('beforeCreateFile', [$this, 'beforeCreateFile'], 10); |
|
80 | - $server->on('beforeMove', [$this, 'beforeMove'], 10); |
|
81 | - $server->on('beforeCopy', [$this, 'beforeCopy'], 10); |
|
82 | - } |
|
83 | - |
|
84 | - /** |
|
85 | - * Check quota before creating file |
|
86 | - * |
|
87 | - * @param string $uri target file URI |
|
88 | - * @param resource $data data |
|
89 | - * @param INode $parent Sabre Node |
|
90 | - * @param bool $modified modified |
|
91 | - */ |
|
92 | - public function beforeCreateFile($uri, $data, INode $parent, $modified) { |
|
93 | - if (!$parent instanceof Node) { |
|
94 | - return; |
|
95 | - } |
|
96 | - |
|
97 | - return $this->checkQuota($parent->getPath() . '/' . basename($uri)); |
|
98 | - } |
|
99 | - |
|
100 | - /** |
|
101 | - * Check quota before writing content |
|
102 | - * |
|
103 | - * @param string $uri target file URI |
|
104 | - * @param INode $node Sabre Node |
|
105 | - * @param resource $data data |
|
106 | - * @param bool $modified modified |
|
107 | - */ |
|
108 | - public function beforeWriteContent($uri, INode $node, $data, $modified) { |
|
109 | - if (!$node instanceof Node) { |
|
110 | - return; |
|
111 | - } |
|
112 | - |
|
113 | - return $this->checkQuota($node->getPath()); |
|
114 | - } |
|
115 | - |
|
116 | - /** |
|
117 | - * Check if we're moving a Futurefile in which case we need to check |
|
118 | - * the quota on the target destination. |
|
119 | - * |
|
120 | - * @param string $source source path |
|
121 | - * @param string $destination destination path |
|
122 | - */ |
|
123 | - public function beforeMove($source, $destination) { |
|
124 | - $sourceNode = $this->server->tree->getNodeForPath($source); |
|
125 | - if (!$sourceNode instanceof FutureFile) { |
|
126 | - return; |
|
127 | - } |
|
128 | - |
|
129 | - // get target node for proper path conversion |
|
130 | - if ($this->server->tree->nodeExists($destination)) { |
|
131 | - $destinationNode = $this->server->tree->getNodeForPath($destination); |
|
132 | - $path = $destinationNode->getPath(); |
|
133 | - } else { |
|
134 | - $parent = dirname($destination); |
|
135 | - if ($parent === '.') { |
|
136 | - $parent = ''; |
|
137 | - } |
|
138 | - $parentNode = $this->server->tree->getNodeForPath($parent); |
|
139 | - $path = $parentNode->getPath(); |
|
140 | - } |
|
141 | - |
|
142 | - return $this->checkQuota($path, $sourceNode->getSize()); |
|
143 | - } |
|
144 | - |
|
145 | - /** |
|
146 | - * Check quota on the target destination before a copy. |
|
147 | - */ |
|
148 | - public function beforeCopy(string $sourcePath, string $destinationPath): bool { |
|
149 | - $sourceNode = $this->server->tree->getNodeForPath($sourcePath); |
|
150 | - if (!$sourceNode instanceof Node) { |
|
151 | - return true; |
|
152 | - } |
|
153 | - |
|
154 | - // get target node for proper path conversion |
|
155 | - if ($this->server->tree->nodeExists($destinationPath)) { |
|
156 | - $destinationNode = $this->server->tree->getNodeForPath($destinationPath); |
|
157 | - if (!$destinationNode instanceof Node) { |
|
158 | - return true; |
|
159 | - } |
|
160 | - $path = $destinationNode->getPath(); |
|
161 | - } else { |
|
162 | - $parent = dirname($destinationPath); |
|
163 | - if ($parent === '.') { |
|
164 | - $parent = ''; |
|
165 | - } |
|
166 | - $parentNode = $this->server->tree->getNodeForPath($parent); |
|
167 | - if (!$parentNode instanceof Node) { |
|
168 | - return true; |
|
169 | - } |
|
170 | - $path = $parentNode->getPath(); |
|
171 | - } |
|
172 | - |
|
173 | - return $this->checkQuota($path, $sourceNode->getSize()); |
|
174 | - } |
|
175 | - |
|
176 | - |
|
177 | - /** |
|
178 | - * This method is called before any HTTP method and validates there is enough free space to store the file |
|
179 | - * |
|
180 | - * @param string $path relative to the users home |
|
181 | - * @param int $length |
|
182 | - * @throws InsufficientStorage |
|
183 | - * @return bool |
|
184 | - */ |
|
185 | - public function checkQuota($path, $length = null) { |
|
186 | - if ($length === null) { |
|
187 | - $length = $this->getLength(); |
|
188 | - } |
|
189 | - |
|
190 | - if ($length) { |
|
191 | - [$parentPath, $newName] = \Sabre\Uri\split($path); |
|
192 | - if (is_null($parentPath)) { |
|
193 | - $parentPath = ''; |
|
194 | - } |
|
195 | - $req = $this->server->httpRequest; |
|
196 | - if ($req->getHeader('OC-Chunked')) { |
|
197 | - $info = \OC_FileChunking::decodeName($newName); |
|
198 | - $chunkHandler = $this->getFileChunking($info); |
|
199 | - // subtract the already uploaded size to see whether |
|
200 | - // there is still enough space for the remaining chunks |
|
201 | - $length -= $chunkHandler->getCurrentSize(); |
|
202 | - // use target file name for free space check in case of shared files |
|
203 | - $path = rtrim($parentPath, '/') . '/' . $info['name']; |
|
204 | - } |
|
205 | - $freeSpace = $this->getFreeSpace($path); |
|
206 | - if ($freeSpace >= 0 && $length > $freeSpace) { |
|
207 | - if (isset($chunkHandler)) { |
|
208 | - $chunkHandler->cleanup(); |
|
209 | - } |
|
210 | - throw new InsufficientStorage("Insufficient space in $path, $length required, $freeSpace available"); |
|
211 | - } |
|
212 | - } |
|
213 | - return true; |
|
214 | - } |
|
215 | - |
|
216 | - public function getFileChunking($info) { |
|
217 | - // FIXME: need a factory for better mocking support |
|
218 | - return new \OC_FileChunking($info); |
|
219 | - } |
|
220 | - |
|
221 | - public function getLength() { |
|
222 | - $req = $this->server->httpRequest; |
|
223 | - $length = $req->getHeader('X-Expected-Entity-Length'); |
|
224 | - if (!is_numeric($length)) { |
|
225 | - $length = $req->getHeader('Content-Length'); |
|
226 | - $length = is_numeric($length) ? $length : null; |
|
227 | - } |
|
228 | - |
|
229 | - $ocLength = $req->getHeader('OC-Total-Length'); |
|
230 | - if (is_numeric($length) && is_numeric($ocLength)) { |
|
231 | - return max($length, $ocLength); |
|
232 | - } |
|
233 | - |
|
234 | - return $length; |
|
235 | - } |
|
236 | - |
|
237 | - /** |
|
238 | - * @param string $uri |
|
239 | - * @return mixed |
|
240 | - * @throws ServiceUnavailable |
|
241 | - */ |
|
242 | - public function getFreeSpace($uri) { |
|
243 | - try { |
|
244 | - $freeSpace = $this->view->free_space(ltrim($uri, '/')); |
|
245 | - return $freeSpace; |
|
246 | - } catch (StorageNotAvailableException $e) { |
|
247 | - throw new ServiceUnavailable($e->getMessage()); |
|
248 | - } |
|
249 | - } |
|
47 | + /** @var \OC\Files\View */ |
|
48 | + private $view; |
|
49 | + |
|
50 | + /** |
|
51 | + * Reference to main server object |
|
52 | + * |
|
53 | + * @var \Sabre\DAV\Server |
|
54 | + */ |
|
55 | + private $server; |
|
56 | + |
|
57 | + /** |
|
58 | + * @param \OC\Files\View $view |
|
59 | + */ |
|
60 | + public function __construct($view) { |
|
61 | + $this->view = $view; |
|
62 | + } |
|
63 | + |
|
64 | + /** |
|
65 | + * This initializes the plugin. |
|
66 | + * |
|
67 | + * This function is called by \Sabre\DAV\Server, after |
|
68 | + * addPlugin is called. |
|
69 | + * |
|
70 | + * This method should set up the requires event subscriptions. |
|
71 | + * |
|
72 | + * @param \Sabre\DAV\Server $server |
|
73 | + * @return void |
|
74 | + */ |
|
75 | + public function initialize(\Sabre\DAV\Server $server) { |
|
76 | + $this->server = $server; |
|
77 | + |
|
78 | + $server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10); |
|
79 | + $server->on('beforeCreateFile', [$this, 'beforeCreateFile'], 10); |
|
80 | + $server->on('beforeMove', [$this, 'beforeMove'], 10); |
|
81 | + $server->on('beforeCopy', [$this, 'beforeCopy'], 10); |
|
82 | + } |
|
83 | + |
|
84 | + /** |
|
85 | + * Check quota before creating file |
|
86 | + * |
|
87 | + * @param string $uri target file URI |
|
88 | + * @param resource $data data |
|
89 | + * @param INode $parent Sabre Node |
|
90 | + * @param bool $modified modified |
|
91 | + */ |
|
92 | + public function beforeCreateFile($uri, $data, INode $parent, $modified) { |
|
93 | + if (!$parent instanceof Node) { |
|
94 | + return; |
|
95 | + } |
|
96 | + |
|
97 | + return $this->checkQuota($parent->getPath() . '/' . basename($uri)); |
|
98 | + } |
|
99 | + |
|
100 | + /** |
|
101 | + * Check quota before writing content |
|
102 | + * |
|
103 | + * @param string $uri target file URI |
|
104 | + * @param INode $node Sabre Node |
|
105 | + * @param resource $data data |
|
106 | + * @param bool $modified modified |
|
107 | + */ |
|
108 | + public function beforeWriteContent($uri, INode $node, $data, $modified) { |
|
109 | + if (!$node instanceof Node) { |
|
110 | + return; |
|
111 | + } |
|
112 | + |
|
113 | + return $this->checkQuota($node->getPath()); |
|
114 | + } |
|
115 | + |
|
116 | + /** |
|
117 | + * Check if we're moving a Futurefile in which case we need to check |
|
118 | + * the quota on the target destination. |
|
119 | + * |
|
120 | + * @param string $source source path |
|
121 | + * @param string $destination destination path |
|
122 | + */ |
|
123 | + public function beforeMove($source, $destination) { |
|
124 | + $sourceNode = $this->server->tree->getNodeForPath($source); |
|
125 | + if (!$sourceNode instanceof FutureFile) { |
|
126 | + return; |
|
127 | + } |
|
128 | + |
|
129 | + // get target node for proper path conversion |
|
130 | + if ($this->server->tree->nodeExists($destination)) { |
|
131 | + $destinationNode = $this->server->tree->getNodeForPath($destination); |
|
132 | + $path = $destinationNode->getPath(); |
|
133 | + } else { |
|
134 | + $parent = dirname($destination); |
|
135 | + if ($parent === '.') { |
|
136 | + $parent = ''; |
|
137 | + } |
|
138 | + $parentNode = $this->server->tree->getNodeForPath($parent); |
|
139 | + $path = $parentNode->getPath(); |
|
140 | + } |
|
141 | + |
|
142 | + return $this->checkQuota($path, $sourceNode->getSize()); |
|
143 | + } |
|
144 | + |
|
145 | + /** |
|
146 | + * Check quota on the target destination before a copy. |
|
147 | + */ |
|
148 | + public function beforeCopy(string $sourcePath, string $destinationPath): bool { |
|
149 | + $sourceNode = $this->server->tree->getNodeForPath($sourcePath); |
|
150 | + if (!$sourceNode instanceof Node) { |
|
151 | + return true; |
|
152 | + } |
|
153 | + |
|
154 | + // get target node for proper path conversion |
|
155 | + if ($this->server->tree->nodeExists($destinationPath)) { |
|
156 | + $destinationNode = $this->server->tree->getNodeForPath($destinationPath); |
|
157 | + if (!$destinationNode instanceof Node) { |
|
158 | + return true; |
|
159 | + } |
|
160 | + $path = $destinationNode->getPath(); |
|
161 | + } else { |
|
162 | + $parent = dirname($destinationPath); |
|
163 | + if ($parent === '.') { |
|
164 | + $parent = ''; |
|
165 | + } |
|
166 | + $parentNode = $this->server->tree->getNodeForPath($parent); |
|
167 | + if (!$parentNode instanceof Node) { |
|
168 | + return true; |
|
169 | + } |
|
170 | + $path = $parentNode->getPath(); |
|
171 | + } |
|
172 | + |
|
173 | + return $this->checkQuota($path, $sourceNode->getSize()); |
|
174 | + } |
|
175 | + |
|
176 | + |
|
177 | + /** |
|
178 | + * This method is called before any HTTP method and validates there is enough free space to store the file |
|
179 | + * |
|
180 | + * @param string $path relative to the users home |
|
181 | + * @param int $length |
|
182 | + * @throws InsufficientStorage |
|
183 | + * @return bool |
|
184 | + */ |
|
185 | + public function checkQuota($path, $length = null) { |
|
186 | + if ($length === null) { |
|
187 | + $length = $this->getLength(); |
|
188 | + } |
|
189 | + |
|
190 | + if ($length) { |
|
191 | + [$parentPath, $newName] = \Sabre\Uri\split($path); |
|
192 | + if (is_null($parentPath)) { |
|
193 | + $parentPath = ''; |
|
194 | + } |
|
195 | + $req = $this->server->httpRequest; |
|
196 | + if ($req->getHeader('OC-Chunked')) { |
|
197 | + $info = \OC_FileChunking::decodeName($newName); |
|
198 | + $chunkHandler = $this->getFileChunking($info); |
|
199 | + // subtract the already uploaded size to see whether |
|
200 | + // there is still enough space for the remaining chunks |
|
201 | + $length -= $chunkHandler->getCurrentSize(); |
|
202 | + // use target file name for free space check in case of shared files |
|
203 | + $path = rtrim($parentPath, '/') . '/' . $info['name']; |
|
204 | + } |
|
205 | + $freeSpace = $this->getFreeSpace($path); |
|
206 | + if ($freeSpace >= 0 && $length > $freeSpace) { |
|
207 | + if (isset($chunkHandler)) { |
|
208 | + $chunkHandler->cleanup(); |
|
209 | + } |
|
210 | + throw new InsufficientStorage("Insufficient space in $path, $length required, $freeSpace available"); |
|
211 | + } |
|
212 | + } |
|
213 | + return true; |
|
214 | + } |
|
215 | + |
|
216 | + public function getFileChunking($info) { |
|
217 | + // FIXME: need a factory for better mocking support |
|
218 | + return new \OC_FileChunking($info); |
|
219 | + } |
|
220 | + |
|
221 | + public function getLength() { |
|
222 | + $req = $this->server->httpRequest; |
|
223 | + $length = $req->getHeader('X-Expected-Entity-Length'); |
|
224 | + if (!is_numeric($length)) { |
|
225 | + $length = $req->getHeader('Content-Length'); |
|
226 | + $length = is_numeric($length) ? $length : null; |
|
227 | + } |
|
228 | + |
|
229 | + $ocLength = $req->getHeader('OC-Total-Length'); |
|
230 | + if (is_numeric($length) && is_numeric($ocLength)) { |
|
231 | + return max($length, $ocLength); |
|
232 | + } |
|
233 | + |
|
234 | + return $length; |
|
235 | + } |
|
236 | + |
|
237 | + /** |
|
238 | + * @param string $uri |
|
239 | + * @return mixed |
|
240 | + * @throws ServiceUnavailable |
|
241 | + */ |
|
242 | + public function getFreeSpace($uri) { |
|
243 | + try { |
|
244 | + $freeSpace = $this->view->free_space(ltrim($uri, '/')); |
|
245 | + return $freeSpace; |
|
246 | + } catch (StorageNotAvailableException $e) { |
|
247 | + throw new ServiceUnavailable($e->getMessage()); |
|
248 | + } |
|
249 | + } |
|
250 | 250 | } |
@@ -186,14 +186,14 @@ discard block |
||
186 | 186 | return; |
187 | 187 | } |
188 | 188 | |
189 | - $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
189 | + $ns = '{'.$this::NS_OWNCLOUD.'}'; |
|
190 | 190 | $requestedProps = []; |
191 | 191 | $filterRules = []; |
192 | 192 | |
193 | 193 | // parse report properties and gather filter info |
194 | 194 | foreach ($report as $reportProps) { |
195 | 195 | $name = $reportProps['name']; |
196 | - if ($name === $ns . 'filter-rules') { |
|
196 | + if ($name === $ns.'filter-rules') { |
|
197 | 197 | $filterRules = $reportProps['value']; |
198 | 198 | } elseif ($name === '{DAV:}prop') { |
199 | 199 | // propfind properties |
@@ -254,7 +254,7 @@ discard block |
||
254 | 254 | if (empty($filesUri)) { |
255 | 255 | return ''; |
256 | 256 | } |
257 | - return '/' . $filesUri; |
|
257 | + return '/'.$filesUri; |
|
258 | 258 | } |
259 | 259 | |
260 | 260 | /** |
@@ -266,19 +266,19 @@ discard block |
||
266 | 266 | * @throws TagNotFoundException whenever a tag was not found |
267 | 267 | */ |
268 | 268 | protected function processFilterRules($filterRules) { |
269 | - $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
269 | + $ns = '{'.$this::NS_OWNCLOUD.'}'; |
|
270 | 270 | $resultFileIds = null; |
271 | 271 | $systemTagIds = []; |
272 | 272 | $circlesIds = []; |
273 | 273 | $favoriteFilter = null; |
274 | 274 | foreach ($filterRules as $filterRule) { |
275 | - if ($filterRule['name'] === $ns . 'systemtag') { |
|
275 | + if ($filterRule['name'] === $ns.'systemtag') { |
|
276 | 276 | $systemTagIds[] = $filterRule['value']; |
277 | 277 | } |
278 | 278 | if ($filterRule['name'] === self::CIRCLE_PROPERTYNAME) { |
279 | 279 | $circlesIds[] = $filterRule['value']; |
280 | 280 | } |
281 | - if ($filterRule['name'] === $ns . 'favorite') { |
|
281 | + if ($filterRule['name'] === $ns.'favorite') { |
|
282 | 282 | $favoriteFilter = true; |
283 | 283 | } |
284 | 284 | } |
@@ -326,7 +326,7 @@ discard block |
||
326 | 326 | } |
327 | 327 | |
328 | 328 | if (!empty($unknownTagIds)) { |
329 | - throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); |
|
329 | + throw new TagNotFoundException('Tag with ids '.implode(', ', $unknownTagIds).' not found'); |
|
330 | 330 | } |
331 | 331 | } |
332 | 332 | |
@@ -379,7 +379,7 @@ discard block |
||
379 | 379 | public function prepareResponses($filesUri, $requestedProps, $nodes) { |
380 | 380 | $responses = []; |
381 | 381 | foreach ($nodes as $node) { |
382 | - $propFind = new PropFind($filesUri . $node->getPath(), $requestedProps); |
|
382 | + $propFind = new PropFind($filesUri.$node->getPath(), $requestedProps); |
|
383 | 383 | |
384 | 384 | $this->server->getPropertiesByNode($propFind, $node); |
385 | 385 | // copied from Sabre Server's getPropertiesForPath |
@@ -392,7 +392,7 @@ discard block |
||
392 | 392 | } |
393 | 393 | |
394 | 394 | $responses[] = new Response( |
395 | - rtrim($this->server->getBaseUri(), '/') . $filesUri . $node->getPath(), |
|
395 | + rtrim($this->server->getBaseUri(), '/').$filesUri.$node->getPath(), |
|
396 | 396 | $result, |
397 | 397 | 200 |
398 | 398 | ); |
@@ -46,396 +46,396 @@ |
||
46 | 46 | |
47 | 47 | class FilesReportPlugin extends ServerPlugin { |
48 | 48 | |
49 | - // namespace |
|
50 | - public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
51 | - public const REPORT_NAME = '{http://owncloud.org/ns}filter-files'; |
|
52 | - public const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag'; |
|
53 | - public const CIRCLE_PROPERTYNAME = '{http://owncloud.org/ns}circle'; |
|
54 | - |
|
55 | - /** |
|
56 | - * Reference to main server object |
|
57 | - * |
|
58 | - * @var \Sabre\DAV\Server |
|
59 | - */ |
|
60 | - private $server; |
|
61 | - |
|
62 | - /** |
|
63 | - * @var Tree |
|
64 | - */ |
|
65 | - private $tree; |
|
66 | - |
|
67 | - /** |
|
68 | - * @var View |
|
69 | - */ |
|
70 | - private $fileView; |
|
71 | - |
|
72 | - /** |
|
73 | - * @var ISystemTagManager |
|
74 | - */ |
|
75 | - private $tagManager; |
|
76 | - |
|
77 | - /** |
|
78 | - * @var ISystemTagObjectMapper |
|
79 | - */ |
|
80 | - private $tagMapper; |
|
81 | - |
|
82 | - /** |
|
83 | - * Manager for private tags |
|
84 | - * |
|
85 | - * @var ITagManager |
|
86 | - */ |
|
87 | - private $fileTagger; |
|
88 | - |
|
89 | - /** |
|
90 | - * @var IUserSession |
|
91 | - */ |
|
92 | - private $userSession; |
|
93 | - |
|
94 | - /** |
|
95 | - * @var IGroupManager |
|
96 | - */ |
|
97 | - private $groupManager; |
|
98 | - |
|
99 | - /** |
|
100 | - * @var Folder |
|
101 | - */ |
|
102 | - private $userFolder; |
|
103 | - |
|
104 | - /** |
|
105 | - * @var IAppManager |
|
106 | - */ |
|
107 | - private $appManager; |
|
108 | - |
|
109 | - /** |
|
110 | - * @param Tree $tree |
|
111 | - * @param View $view |
|
112 | - * @param ISystemTagManager $tagManager |
|
113 | - * @param ISystemTagObjectMapper $tagMapper |
|
114 | - * @param ITagManager $fileTagger manager for private tags |
|
115 | - * @param IUserSession $userSession |
|
116 | - * @param IGroupManager $groupManager |
|
117 | - * @param Folder $userFolder |
|
118 | - * @param IAppManager $appManager |
|
119 | - */ |
|
120 | - public function __construct(Tree $tree, |
|
121 | - View $view, |
|
122 | - ISystemTagManager $tagManager, |
|
123 | - ISystemTagObjectMapper $tagMapper, |
|
124 | - ITagManager $fileTagger, |
|
125 | - IUserSession $userSession, |
|
126 | - IGroupManager $groupManager, |
|
127 | - Folder $userFolder, |
|
128 | - IAppManager $appManager |
|
129 | - ) { |
|
130 | - $this->tree = $tree; |
|
131 | - $this->fileView = $view; |
|
132 | - $this->tagManager = $tagManager; |
|
133 | - $this->tagMapper = $tagMapper; |
|
134 | - $this->fileTagger = $fileTagger; |
|
135 | - $this->userSession = $userSession; |
|
136 | - $this->groupManager = $groupManager; |
|
137 | - $this->userFolder = $userFolder; |
|
138 | - $this->appManager = $appManager; |
|
139 | - } |
|
140 | - |
|
141 | - /** |
|
142 | - * This initializes the plugin. |
|
143 | - * |
|
144 | - * This function is called by \Sabre\DAV\Server, after |
|
145 | - * addPlugin is called. |
|
146 | - * |
|
147 | - * This method should set up the required event subscriptions. |
|
148 | - * |
|
149 | - * @param \Sabre\DAV\Server $server |
|
150 | - * @return void |
|
151 | - */ |
|
152 | - public function initialize(\Sabre\DAV\Server $server) { |
|
153 | - $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; |
|
154 | - |
|
155 | - $this->server = $server; |
|
156 | - $this->server->on('report', [$this, 'onReport']); |
|
157 | - } |
|
158 | - |
|
159 | - /** |
|
160 | - * Returns a list of reports this plugin supports. |
|
161 | - * |
|
162 | - * This will be used in the {DAV:}supported-report-set property. |
|
163 | - * |
|
164 | - * @param string $uri |
|
165 | - * @return array |
|
166 | - */ |
|
167 | - public function getSupportedReportSet($uri) { |
|
168 | - return [self::REPORT_NAME]; |
|
169 | - } |
|
170 | - |
|
171 | - /** |
|
172 | - * REPORT operations to look for files |
|
173 | - * |
|
174 | - * @param string $reportName |
|
175 | - * @param $report |
|
176 | - * @param string $uri |
|
177 | - * @return bool |
|
178 | - * @throws BadRequest |
|
179 | - * @throws PreconditionFailed |
|
180 | - * @internal param $ [] $report |
|
181 | - */ |
|
182 | - public function onReport($reportName, $report, $uri) { |
|
183 | - $reportTargetNode = $this->server->tree->getNodeForPath($uri); |
|
184 | - if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) { |
|
185 | - return; |
|
186 | - } |
|
187 | - |
|
188 | - $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
189 | - $requestedProps = []; |
|
190 | - $filterRules = []; |
|
191 | - |
|
192 | - // parse report properties and gather filter info |
|
193 | - foreach ($report as $reportProps) { |
|
194 | - $name = $reportProps['name']; |
|
195 | - if ($name === $ns . 'filter-rules') { |
|
196 | - $filterRules = $reportProps['value']; |
|
197 | - } elseif ($name === '{DAV:}prop') { |
|
198 | - // propfind properties |
|
199 | - foreach ($reportProps['value'] as $propVal) { |
|
200 | - $requestedProps[] = $propVal['name']; |
|
201 | - } |
|
202 | - } |
|
203 | - } |
|
204 | - |
|
205 | - if (empty($filterRules)) { |
|
206 | - // an empty filter would return all existing files which would be slow |
|
207 | - throw new BadRequest('Missing filter-rule block in request'); |
|
208 | - } |
|
209 | - |
|
210 | - // gather all file ids matching filter |
|
211 | - try { |
|
212 | - $resultFileIds = $this->processFilterRules($filterRules); |
|
213 | - } catch (TagNotFoundException $e) { |
|
214 | - throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e); |
|
215 | - } |
|
216 | - |
|
217 | - // find sabre nodes by file id, restricted to the root node path |
|
218 | - $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds); |
|
219 | - |
|
220 | - $filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath()); |
|
221 | - $responses = $this->prepareResponses($filesUri, $requestedProps, $results); |
|
222 | - |
|
223 | - $xml = $this->server->xml->write( |
|
224 | - '{DAV:}multistatus', |
|
225 | - new MultiStatus($responses) |
|
226 | - ); |
|
227 | - |
|
228 | - $this->server->httpResponse->setStatus(207); |
|
229 | - $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); |
|
230 | - $this->server->httpResponse->setBody($xml); |
|
231 | - |
|
232 | - return false; |
|
233 | - } |
|
234 | - |
|
235 | - /** |
|
236 | - * Returns the base uri of the files root by removing |
|
237 | - * the subpath from the URI |
|
238 | - * |
|
239 | - * @param string $uri URI from this request |
|
240 | - * @param string $subPath subpath to remove from the URI |
|
241 | - * |
|
242 | - * @return string files base uri |
|
243 | - */ |
|
244 | - private function getFilesBaseUri(string $uri, string $subPath): string { |
|
245 | - $uri = trim($uri, '/'); |
|
246 | - $subPath = trim($subPath, '/'); |
|
247 | - if (empty($subPath)) { |
|
248 | - $filesUri = $uri; |
|
249 | - } else { |
|
250 | - $filesUri = substr($uri, 0, strlen($uri) - strlen($subPath)); |
|
251 | - } |
|
252 | - $filesUri = trim($filesUri, '/'); |
|
253 | - if (empty($filesUri)) { |
|
254 | - return ''; |
|
255 | - } |
|
256 | - return '/' . $filesUri; |
|
257 | - } |
|
258 | - |
|
259 | - /** |
|
260 | - * Find file ids matching the given filter rules |
|
261 | - * |
|
262 | - * @param array $filterRules |
|
263 | - * @return array array of unique file id results |
|
264 | - * |
|
265 | - * @throws TagNotFoundException whenever a tag was not found |
|
266 | - */ |
|
267 | - protected function processFilterRules($filterRules) { |
|
268 | - $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
269 | - $resultFileIds = null; |
|
270 | - $systemTagIds = []; |
|
271 | - $circlesIds = []; |
|
272 | - $favoriteFilter = null; |
|
273 | - foreach ($filterRules as $filterRule) { |
|
274 | - if ($filterRule['name'] === $ns . 'systemtag') { |
|
275 | - $systemTagIds[] = $filterRule['value']; |
|
276 | - } |
|
277 | - if ($filterRule['name'] === self::CIRCLE_PROPERTYNAME) { |
|
278 | - $circlesIds[] = $filterRule['value']; |
|
279 | - } |
|
280 | - if ($filterRule['name'] === $ns . 'favorite') { |
|
281 | - $favoriteFilter = true; |
|
282 | - } |
|
283 | - } |
|
284 | - |
|
285 | - if ($favoriteFilter !== null) { |
|
286 | - $resultFileIds = $this->fileTagger->load('files')->getFavorites(); |
|
287 | - if (empty($resultFileIds)) { |
|
288 | - return []; |
|
289 | - } |
|
290 | - } |
|
291 | - |
|
292 | - if (!empty($systemTagIds)) { |
|
293 | - $fileIds = $this->getSystemTagFileIds($systemTagIds); |
|
294 | - if (empty($resultFileIds)) { |
|
295 | - $resultFileIds = $fileIds; |
|
296 | - } else { |
|
297 | - $resultFileIds = array_intersect($fileIds, $resultFileIds); |
|
298 | - } |
|
299 | - } |
|
300 | - |
|
301 | - if (!empty($circlesIds)) { |
|
302 | - $fileIds = $this->getCirclesFileIds($circlesIds); |
|
303 | - if (empty($resultFileIds)) { |
|
304 | - $resultFileIds = $fileIds; |
|
305 | - } else { |
|
306 | - $resultFileIds = array_intersect($fileIds, $resultFileIds); |
|
307 | - } |
|
308 | - } |
|
309 | - |
|
310 | - return $resultFileIds; |
|
311 | - } |
|
312 | - |
|
313 | - private function getSystemTagFileIds($systemTagIds) { |
|
314 | - $resultFileIds = null; |
|
315 | - |
|
316 | - // check user permissions, if applicable |
|
317 | - if (!$this->isAdmin()) { |
|
318 | - // check visibility/permission |
|
319 | - $tags = $this->tagManager->getTagsByIds($systemTagIds); |
|
320 | - $unknownTagIds = []; |
|
321 | - foreach ($tags as $tag) { |
|
322 | - if (!$tag->isUserVisible()) { |
|
323 | - $unknownTagIds[] = $tag->getId(); |
|
324 | - } |
|
325 | - } |
|
326 | - |
|
327 | - if (!empty($unknownTagIds)) { |
|
328 | - throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); |
|
329 | - } |
|
330 | - } |
|
331 | - |
|
332 | - // fetch all file ids and intersect them |
|
333 | - foreach ($systemTagIds as $systemTagId) { |
|
334 | - $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files'); |
|
335 | - |
|
336 | - if (empty($fileIds)) { |
|
337 | - // This tag has no files, nothing can ever show up |
|
338 | - return []; |
|
339 | - } |
|
340 | - |
|
341 | - // first run ? |
|
342 | - if ($resultFileIds === null) { |
|
343 | - $resultFileIds = $fileIds; |
|
344 | - } else { |
|
345 | - $resultFileIds = array_intersect($resultFileIds, $fileIds); |
|
346 | - } |
|
347 | - |
|
348 | - if (empty($resultFileIds)) { |
|
349 | - // Empty intersection, nothing can show up anymore |
|
350 | - return []; |
|
351 | - } |
|
352 | - } |
|
353 | - return $resultFileIds; |
|
354 | - } |
|
355 | - |
|
356 | - /** |
|
357 | - * @suppress PhanUndeclaredClassMethod |
|
358 | - * @param array $circlesIds |
|
359 | - * @return array |
|
360 | - */ |
|
361 | - private function getCirclesFileIds(array $circlesIds) { |
|
362 | - if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) { |
|
363 | - return []; |
|
364 | - } |
|
365 | - return \OCA\Circles\Api\v1\Circles::getFilesForCircles($circlesIds); |
|
366 | - } |
|
367 | - |
|
368 | - |
|
369 | - /** |
|
370 | - * Prepare propfind response for the given nodes |
|
371 | - * |
|
372 | - * @param string $filesUri $filesUri URI leading to root of the files URI, |
|
373 | - * with a leading slash but no trailing slash |
|
374 | - * @param string[] $requestedProps requested properties |
|
375 | - * @param Node[] nodes nodes for which to fetch and prepare responses |
|
376 | - * @return Response[] |
|
377 | - */ |
|
378 | - public function prepareResponses($filesUri, $requestedProps, $nodes) { |
|
379 | - $responses = []; |
|
380 | - foreach ($nodes as $node) { |
|
381 | - $propFind = new PropFind($filesUri . $node->getPath(), $requestedProps); |
|
382 | - |
|
383 | - $this->server->getPropertiesByNode($propFind, $node); |
|
384 | - // copied from Sabre Server's getPropertiesForPath |
|
385 | - $result = $propFind->getResultForMultiStatus(); |
|
386 | - $result['href'] = $propFind->getPath(); |
|
387 | - |
|
388 | - $resourceType = $this->server->getResourceTypeForNode($node); |
|
389 | - if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { |
|
390 | - $result['href'] .= '/'; |
|
391 | - } |
|
392 | - |
|
393 | - $responses[] = new Response( |
|
394 | - rtrim($this->server->getBaseUri(), '/') . $filesUri . $node->getPath(), |
|
395 | - $result, |
|
396 | - 200 |
|
397 | - ); |
|
398 | - } |
|
399 | - return $responses; |
|
400 | - } |
|
401 | - |
|
402 | - /** |
|
403 | - * Find Sabre nodes by file ids |
|
404 | - * |
|
405 | - * @param Node $rootNode root node for search |
|
406 | - * @param array $fileIds file ids |
|
407 | - * @return Node[] array of Sabre nodes |
|
408 | - */ |
|
409 | - public function findNodesByFileIds($rootNode, $fileIds) { |
|
410 | - $folder = $this->userFolder; |
|
411 | - if (trim($rootNode->getPath(), '/') !== '') { |
|
412 | - $folder = $folder->get($rootNode->getPath()); |
|
413 | - } |
|
414 | - |
|
415 | - $results = []; |
|
416 | - foreach ($fileIds as $fileId) { |
|
417 | - $entry = $folder->getById($fileId); |
|
418 | - if ($entry) { |
|
419 | - $entry = current($entry); |
|
420 | - if ($entry instanceof \OCP\Files\File) { |
|
421 | - $results[] = new File($this->fileView, $entry); |
|
422 | - } elseif ($entry instanceof \OCP\Files\Folder) { |
|
423 | - $results[] = new Directory($this->fileView, $entry); |
|
424 | - } |
|
425 | - } |
|
426 | - } |
|
427 | - |
|
428 | - return $results; |
|
429 | - } |
|
430 | - |
|
431 | - /** |
|
432 | - * Returns whether the currently logged in user is an administrator |
|
433 | - */ |
|
434 | - private function isAdmin() { |
|
435 | - $user = $this->userSession->getUser(); |
|
436 | - if ($user !== null) { |
|
437 | - return $this->groupManager->isAdmin($user->getUID()); |
|
438 | - } |
|
439 | - return false; |
|
440 | - } |
|
49 | + // namespace |
|
50 | + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
51 | + public const REPORT_NAME = '{http://owncloud.org/ns}filter-files'; |
|
52 | + public const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag'; |
|
53 | + public const CIRCLE_PROPERTYNAME = '{http://owncloud.org/ns}circle'; |
|
54 | + |
|
55 | + /** |
|
56 | + * Reference to main server object |
|
57 | + * |
|
58 | + * @var \Sabre\DAV\Server |
|
59 | + */ |
|
60 | + private $server; |
|
61 | + |
|
62 | + /** |
|
63 | + * @var Tree |
|
64 | + */ |
|
65 | + private $tree; |
|
66 | + |
|
67 | + /** |
|
68 | + * @var View |
|
69 | + */ |
|
70 | + private $fileView; |
|
71 | + |
|
72 | + /** |
|
73 | + * @var ISystemTagManager |
|
74 | + */ |
|
75 | + private $tagManager; |
|
76 | + |
|
77 | + /** |
|
78 | + * @var ISystemTagObjectMapper |
|
79 | + */ |
|
80 | + private $tagMapper; |
|
81 | + |
|
82 | + /** |
|
83 | + * Manager for private tags |
|
84 | + * |
|
85 | + * @var ITagManager |
|
86 | + */ |
|
87 | + private $fileTagger; |
|
88 | + |
|
89 | + /** |
|
90 | + * @var IUserSession |
|
91 | + */ |
|
92 | + private $userSession; |
|
93 | + |
|
94 | + /** |
|
95 | + * @var IGroupManager |
|
96 | + */ |
|
97 | + private $groupManager; |
|
98 | + |
|
99 | + /** |
|
100 | + * @var Folder |
|
101 | + */ |
|
102 | + private $userFolder; |
|
103 | + |
|
104 | + /** |
|
105 | + * @var IAppManager |
|
106 | + */ |
|
107 | + private $appManager; |
|
108 | + |
|
109 | + /** |
|
110 | + * @param Tree $tree |
|
111 | + * @param View $view |
|
112 | + * @param ISystemTagManager $tagManager |
|
113 | + * @param ISystemTagObjectMapper $tagMapper |
|
114 | + * @param ITagManager $fileTagger manager for private tags |
|
115 | + * @param IUserSession $userSession |
|
116 | + * @param IGroupManager $groupManager |
|
117 | + * @param Folder $userFolder |
|
118 | + * @param IAppManager $appManager |
|
119 | + */ |
|
120 | + public function __construct(Tree $tree, |
|
121 | + View $view, |
|
122 | + ISystemTagManager $tagManager, |
|
123 | + ISystemTagObjectMapper $tagMapper, |
|
124 | + ITagManager $fileTagger, |
|
125 | + IUserSession $userSession, |
|
126 | + IGroupManager $groupManager, |
|
127 | + Folder $userFolder, |
|
128 | + IAppManager $appManager |
|
129 | + ) { |
|
130 | + $this->tree = $tree; |
|
131 | + $this->fileView = $view; |
|
132 | + $this->tagManager = $tagManager; |
|
133 | + $this->tagMapper = $tagMapper; |
|
134 | + $this->fileTagger = $fileTagger; |
|
135 | + $this->userSession = $userSession; |
|
136 | + $this->groupManager = $groupManager; |
|
137 | + $this->userFolder = $userFolder; |
|
138 | + $this->appManager = $appManager; |
|
139 | + } |
|
140 | + |
|
141 | + /** |
|
142 | + * This initializes the plugin. |
|
143 | + * |
|
144 | + * This function is called by \Sabre\DAV\Server, after |
|
145 | + * addPlugin is called. |
|
146 | + * |
|
147 | + * This method should set up the required event subscriptions. |
|
148 | + * |
|
149 | + * @param \Sabre\DAV\Server $server |
|
150 | + * @return void |
|
151 | + */ |
|
152 | + public function initialize(\Sabre\DAV\Server $server) { |
|
153 | + $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; |
|
154 | + |
|
155 | + $this->server = $server; |
|
156 | + $this->server->on('report', [$this, 'onReport']); |
|
157 | + } |
|
158 | + |
|
159 | + /** |
|
160 | + * Returns a list of reports this plugin supports. |
|
161 | + * |
|
162 | + * This will be used in the {DAV:}supported-report-set property. |
|
163 | + * |
|
164 | + * @param string $uri |
|
165 | + * @return array |
|
166 | + */ |
|
167 | + public function getSupportedReportSet($uri) { |
|
168 | + return [self::REPORT_NAME]; |
|
169 | + } |
|
170 | + |
|
171 | + /** |
|
172 | + * REPORT operations to look for files |
|
173 | + * |
|
174 | + * @param string $reportName |
|
175 | + * @param $report |
|
176 | + * @param string $uri |
|
177 | + * @return bool |
|
178 | + * @throws BadRequest |
|
179 | + * @throws PreconditionFailed |
|
180 | + * @internal param $ [] $report |
|
181 | + */ |
|
182 | + public function onReport($reportName, $report, $uri) { |
|
183 | + $reportTargetNode = $this->server->tree->getNodeForPath($uri); |
|
184 | + if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) { |
|
185 | + return; |
|
186 | + } |
|
187 | + |
|
188 | + $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
189 | + $requestedProps = []; |
|
190 | + $filterRules = []; |
|
191 | + |
|
192 | + // parse report properties and gather filter info |
|
193 | + foreach ($report as $reportProps) { |
|
194 | + $name = $reportProps['name']; |
|
195 | + if ($name === $ns . 'filter-rules') { |
|
196 | + $filterRules = $reportProps['value']; |
|
197 | + } elseif ($name === '{DAV:}prop') { |
|
198 | + // propfind properties |
|
199 | + foreach ($reportProps['value'] as $propVal) { |
|
200 | + $requestedProps[] = $propVal['name']; |
|
201 | + } |
|
202 | + } |
|
203 | + } |
|
204 | + |
|
205 | + if (empty($filterRules)) { |
|
206 | + // an empty filter would return all existing files which would be slow |
|
207 | + throw new BadRequest('Missing filter-rule block in request'); |
|
208 | + } |
|
209 | + |
|
210 | + // gather all file ids matching filter |
|
211 | + try { |
|
212 | + $resultFileIds = $this->processFilterRules($filterRules); |
|
213 | + } catch (TagNotFoundException $e) { |
|
214 | + throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e); |
|
215 | + } |
|
216 | + |
|
217 | + // find sabre nodes by file id, restricted to the root node path |
|
218 | + $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds); |
|
219 | + |
|
220 | + $filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath()); |
|
221 | + $responses = $this->prepareResponses($filesUri, $requestedProps, $results); |
|
222 | + |
|
223 | + $xml = $this->server->xml->write( |
|
224 | + '{DAV:}multistatus', |
|
225 | + new MultiStatus($responses) |
|
226 | + ); |
|
227 | + |
|
228 | + $this->server->httpResponse->setStatus(207); |
|
229 | + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); |
|
230 | + $this->server->httpResponse->setBody($xml); |
|
231 | + |
|
232 | + return false; |
|
233 | + } |
|
234 | + |
|
235 | + /** |
|
236 | + * Returns the base uri of the files root by removing |
|
237 | + * the subpath from the URI |
|
238 | + * |
|
239 | + * @param string $uri URI from this request |
|
240 | + * @param string $subPath subpath to remove from the URI |
|
241 | + * |
|
242 | + * @return string files base uri |
|
243 | + */ |
|
244 | + private function getFilesBaseUri(string $uri, string $subPath): string { |
|
245 | + $uri = trim($uri, '/'); |
|
246 | + $subPath = trim($subPath, '/'); |
|
247 | + if (empty($subPath)) { |
|
248 | + $filesUri = $uri; |
|
249 | + } else { |
|
250 | + $filesUri = substr($uri, 0, strlen($uri) - strlen($subPath)); |
|
251 | + } |
|
252 | + $filesUri = trim($filesUri, '/'); |
|
253 | + if (empty($filesUri)) { |
|
254 | + return ''; |
|
255 | + } |
|
256 | + return '/' . $filesUri; |
|
257 | + } |
|
258 | + |
|
259 | + /** |
|
260 | + * Find file ids matching the given filter rules |
|
261 | + * |
|
262 | + * @param array $filterRules |
|
263 | + * @return array array of unique file id results |
|
264 | + * |
|
265 | + * @throws TagNotFoundException whenever a tag was not found |
|
266 | + */ |
|
267 | + protected function processFilterRules($filterRules) { |
|
268 | + $ns = '{' . $this::NS_OWNCLOUD . '}'; |
|
269 | + $resultFileIds = null; |
|
270 | + $systemTagIds = []; |
|
271 | + $circlesIds = []; |
|
272 | + $favoriteFilter = null; |
|
273 | + foreach ($filterRules as $filterRule) { |
|
274 | + if ($filterRule['name'] === $ns . 'systemtag') { |
|
275 | + $systemTagIds[] = $filterRule['value']; |
|
276 | + } |
|
277 | + if ($filterRule['name'] === self::CIRCLE_PROPERTYNAME) { |
|
278 | + $circlesIds[] = $filterRule['value']; |
|
279 | + } |
|
280 | + if ($filterRule['name'] === $ns . 'favorite') { |
|
281 | + $favoriteFilter = true; |
|
282 | + } |
|
283 | + } |
|
284 | + |
|
285 | + if ($favoriteFilter !== null) { |
|
286 | + $resultFileIds = $this->fileTagger->load('files')->getFavorites(); |
|
287 | + if (empty($resultFileIds)) { |
|
288 | + return []; |
|
289 | + } |
|
290 | + } |
|
291 | + |
|
292 | + if (!empty($systemTagIds)) { |
|
293 | + $fileIds = $this->getSystemTagFileIds($systemTagIds); |
|
294 | + if (empty($resultFileIds)) { |
|
295 | + $resultFileIds = $fileIds; |
|
296 | + } else { |
|
297 | + $resultFileIds = array_intersect($fileIds, $resultFileIds); |
|
298 | + } |
|
299 | + } |
|
300 | + |
|
301 | + if (!empty($circlesIds)) { |
|
302 | + $fileIds = $this->getCirclesFileIds($circlesIds); |
|
303 | + if (empty($resultFileIds)) { |
|
304 | + $resultFileIds = $fileIds; |
|
305 | + } else { |
|
306 | + $resultFileIds = array_intersect($fileIds, $resultFileIds); |
|
307 | + } |
|
308 | + } |
|
309 | + |
|
310 | + return $resultFileIds; |
|
311 | + } |
|
312 | + |
|
313 | + private function getSystemTagFileIds($systemTagIds) { |
|
314 | + $resultFileIds = null; |
|
315 | + |
|
316 | + // check user permissions, if applicable |
|
317 | + if (!$this->isAdmin()) { |
|
318 | + // check visibility/permission |
|
319 | + $tags = $this->tagManager->getTagsByIds($systemTagIds); |
|
320 | + $unknownTagIds = []; |
|
321 | + foreach ($tags as $tag) { |
|
322 | + if (!$tag->isUserVisible()) { |
|
323 | + $unknownTagIds[] = $tag->getId(); |
|
324 | + } |
|
325 | + } |
|
326 | + |
|
327 | + if (!empty($unknownTagIds)) { |
|
328 | + throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); |
|
329 | + } |
|
330 | + } |
|
331 | + |
|
332 | + // fetch all file ids and intersect them |
|
333 | + foreach ($systemTagIds as $systemTagId) { |
|
334 | + $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files'); |
|
335 | + |
|
336 | + if (empty($fileIds)) { |
|
337 | + // This tag has no files, nothing can ever show up |
|
338 | + return []; |
|
339 | + } |
|
340 | + |
|
341 | + // first run ? |
|
342 | + if ($resultFileIds === null) { |
|
343 | + $resultFileIds = $fileIds; |
|
344 | + } else { |
|
345 | + $resultFileIds = array_intersect($resultFileIds, $fileIds); |
|
346 | + } |
|
347 | + |
|
348 | + if (empty($resultFileIds)) { |
|
349 | + // Empty intersection, nothing can show up anymore |
|
350 | + return []; |
|
351 | + } |
|
352 | + } |
|
353 | + return $resultFileIds; |
|
354 | + } |
|
355 | + |
|
356 | + /** |
|
357 | + * @suppress PhanUndeclaredClassMethod |
|
358 | + * @param array $circlesIds |
|
359 | + * @return array |
|
360 | + */ |
|
361 | + private function getCirclesFileIds(array $circlesIds) { |
|
362 | + if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) { |
|
363 | + return []; |
|
364 | + } |
|
365 | + return \OCA\Circles\Api\v1\Circles::getFilesForCircles($circlesIds); |
|
366 | + } |
|
367 | + |
|
368 | + |
|
369 | + /** |
|
370 | + * Prepare propfind response for the given nodes |
|
371 | + * |
|
372 | + * @param string $filesUri $filesUri URI leading to root of the files URI, |
|
373 | + * with a leading slash but no trailing slash |
|
374 | + * @param string[] $requestedProps requested properties |
|
375 | + * @param Node[] nodes nodes for which to fetch and prepare responses |
|
376 | + * @return Response[] |
|
377 | + */ |
|
378 | + public function prepareResponses($filesUri, $requestedProps, $nodes) { |
|
379 | + $responses = []; |
|
380 | + foreach ($nodes as $node) { |
|
381 | + $propFind = new PropFind($filesUri . $node->getPath(), $requestedProps); |
|
382 | + |
|
383 | + $this->server->getPropertiesByNode($propFind, $node); |
|
384 | + // copied from Sabre Server's getPropertiesForPath |
|
385 | + $result = $propFind->getResultForMultiStatus(); |
|
386 | + $result['href'] = $propFind->getPath(); |
|
387 | + |
|
388 | + $resourceType = $this->server->getResourceTypeForNode($node); |
|
389 | + if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { |
|
390 | + $result['href'] .= '/'; |
|
391 | + } |
|
392 | + |
|
393 | + $responses[] = new Response( |
|
394 | + rtrim($this->server->getBaseUri(), '/') . $filesUri . $node->getPath(), |
|
395 | + $result, |
|
396 | + 200 |
|
397 | + ); |
|
398 | + } |
|
399 | + return $responses; |
|
400 | + } |
|
401 | + |
|
402 | + /** |
|
403 | + * Find Sabre nodes by file ids |
|
404 | + * |
|
405 | + * @param Node $rootNode root node for search |
|
406 | + * @param array $fileIds file ids |
|
407 | + * @return Node[] array of Sabre nodes |
|
408 | + */ |
|
409 | + public function findNodesByFileIds($rootNode, $fileIds) { |
|
410 | + $folder = $this->userFolder; |
|
411 | + if (trim($rootNode->getPath(), '/') !== '') { |
|
412 | + $folder = $folder->get($rootNode->getPath()); |
|
413 | + } |
|
414 | + |
|
415 | + $results = []; |
|
416 | + foreach ($fileIds as $fileId) { |
|
417 | + $entry = $folder->getById($fileId); |
|
418 | + if ($entry) { |
|
419 | + $entry = current($entry); |
|
420 | + if ($entry instanceof \OCP\Files\File) { |
|
421 | + $results[] = new File($this->fileView, $entry); |
|
422 | + } elseif ($entry instanceof \OCP\Files\Folder) { |
|
423 | + $results[] = new Directory($this->fileView, $entry); |
|
424 | + } |
|
425 | + } |
|
426 | + } |
|
427 | + |
|
428 | + return $results; |
|
429 | + } |
|
430 | + |
|
431 | + /** |
|
432 | + * Returns whether the currently logged in user is an administrator |
|
433 | + */ |
|
434 | + private function isAdmin() { |
|
435 | + $user = $this->userSession->getUser(); |
|
436 | + if ($user !== null) { |
|
437 | + return $this->groupManager->isAdmin($user->getUID()); |
|
438 | + } |
|
439 | + return false; |
|
440 | + } |
|
441 | 441 | } |