@@ -39,721 +39,721 @@ |
||
39 | 39 | */ |
40 | 40 | class FilesPluginTest extends TestCase { |
41 | 41 | |
42 | - private Tree&MockObject $tree; |
|
43 | - private Server&MockObject $server; |
|
44 | - private IConfig&MockObject $config; |
|
45 | - private IRequest&MockObject $request; |
|
46 | - private IPreview&MockObject $previewManager; |
|
47 | - private IUserSession&MockObject $userSession; |
|
48 | - private IFilenameValidator&MockObject $filenameValidator; |
|
49 | - private IAccountManager&MockObject $accountManager; |
|
50 | - private FilesPlugin $plugin; |
|
51 | - |
|
52 | - protected function setUp(): void { |
|
53 | - parent::setUp(); |
|
54 | - $this->server = $this->createMock(Server::class); |
|
55 | - $this->tree = $this->createMock(Tree::class); |
|
56 | - $this->config = $this->createMock(IConfig::class); |
|
57 | - $this->config->expects($this->any())->method('getSystemValue') |
|
58 | - ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) |
|
59 | - ->willReturn('my_fingerprint'); |
|
60 | - $this->request = $this->createMock(IRequest::class); |
|
61 | - $this->previewManager = $this->createMock(IPreview::class); |
|
62 | - $this->userSession = $this->createMock(IUserSession::class); |
|
63 | - $this->filenameValidator = $this->createMock(IFilenameValidator::class); |
|
64 | - $this->accountManager = $this->createMock(IAccountManager::class); |
|
65 | - |
|
66 | - $this->plugin = new FilesPlugin( |
|
67 | - $this->tree, |
|
68 | - $this->config, |
|
69 | - $this->request, |
|
70 | - $this->previewManager, |
|
71 | - $this->userSession, |
|
72 | - $this->filenameValidator, |
|
73 | - $this->accountManager, |
|
74 | - ); |
|
75 | - |
|
76 | - $response = $this->getMockBuilder(ResponseInterface::class) |
|
77 | - ->disableOriginalConstructor() |
|
78 | - ->getMock(); |
|
79 | - $this->server->httpResponse = $response; |
|
80 | - $this->server->xml = new Service(); |
|
81 | - |
|
82 | - $this->plugin->initialize($this->server); |
|
83 | - } |
|
84 | - |
|
85 | - /** |
|
86 | - * @param string $class |
|
87 | - * @return \PHPUnit\Framework\MockObject\MockObject |
|
88 | - */ |
|
89 | - private function createTestNode($class, $path = '/dummypath') { |
|
90 | - $node = $this->getMockBuilder($class) |
|
91 | - ->disableOriginalConstructor() |
|
92 | - ->getMock(); |
|
93 | - |
|
94 | - $node->expects($this->any()) |
|
95 | - ->method('getId') |
|
96 | - ->willReturn(123); |
|
97 | - |
|
98 | - $this->tree->expects($this->any()) |
|
99 | - ->method('getNodeForPath') |
|
100 | - ->with($path) |
|
101 | - ->willReturn($node); |
|
102 | - |
|
103 | - $node->expects($this->any()) |
|
104 | - ->method('getFileId') |
|
105 | - ->willReturn('00000123instanceid'); |
|
106 | - $node->expects($this->any()) |
|
107 | - ->method('getInternalFileId') |
|
108 | - ->willReturn('123'); |
|
109 | - $node->expects($this->any()) |
|
110 | - ->method('getEtag') |
|
111 | - ->willReturn('"abc"'); |
|
112 | - $node->expects($this->any()) |
|
113 | - ->method('getDavPermissions') |
|
114 | - ->willReturn('DWCKMSR'); |
|
115 | - |
|
116 | - $fileInfo = $this->createMock(FileInfo::class); |
|
117 | - $fileInfo->expects($this->any()) |
|
118 | - ->method('isReadable') |
|
119 | - ->willReturn(true); |
|
120 | - $fileInfo->expects($this->any()) |
|
121 | - ->method('getCreationTime') |
|
122 | - ->willReturn(123456789); |
|
123 | - |
|
124 | - $node->expects($this->any()) |
|
125 | - ->method('getFileInfo') |
|
126 | - ->willReturn($fileInfo); |
|
127 | - |
|
128 | - return $node; |
|
129 | - } |
|
130 | - |
|
131 | - public function testGetPropertiesForFile(): void { |
|
132 | - /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
133 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
134 | - |
|
135 | - $propFind = new PropFind( |
|
136 | - '/dummyPath', |
|
137 | - [ |
|
138 | - FilesPlugin::GETETAG_PROPERTYNAME, |
|
139 | - FilesPlugin::FILEID_PROPERTYNAME, |
|
140 | - FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, |
|
141 | - FilesPlugin::SIZE_PROPERTYNAME, |
|
142 | - FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
143 | - FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
144 | - FilesPlugin::OWNER_ID_PROPERTYNAME, |
|
145 | - FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
146 | - FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
147 | - FilesPlugin::CREATIONDATE_PROPERTYNAME, |
|
148 | - ], |
|
149 | - 0 |
|
150 | - ); |
|
151 | - |
|
152 | - $user = $this->getMockBuilder(User::class) |
|
153 | - ->disableOriginalConstructor()->getMock(); |
|
154 | - $user |
|
155 | - ->expects($this->once()) |
|
156 | - ->method('getUID') |
|
157 | - ->willReturn('foo'); |
|
158 | - $user |
|
159 | - ->expects($this->once()) |
|
160 | - ->method('getDisplayName') |
|
161 | - ->willReturn('M. Foo'); |
|
162 | - |
|
163 | - $owner = $this->getMockBuilder(Account::class) |
|
164 | - ->disableOriginalConstructor()->getMock(); |
|
165 | - $this->accountManager->expects($this->once()) |
|
166 | - ->method('getAccount') |
|
167 | - ->with($user) |
|
168 | - ->willReturn($owner); |
|
169 | - |
|
170 | - $node->expects($this->once()) |
|
171 | - ->method('getDirectDownload') |
|
172 | - ->willReturn(['url' => 'http://example.com/']); |
|
173 | - $node->expects($this->exactly(2)) |
|
174 | - ->method('getOwner') |
|
175 | - ->willReturn($user); |
|
176 | - |
|
177 | - $displayNameProp = $this->getMockBuilder(AccountProperty::class) |
|
178 | - ->disableOriginalConstructor()->getMock(); |
|
179 | - $owner |
|
180 | - ->expects($this->once()) |
|
181 | - ->method('getProperty') |
|
182 | - ->with(IAccountManager::PROPERTY_DISPLAYNAME) |
|
183 | - ->willReturn($displayNameProp); |
|
184 | - $displayNameProp |
|
185 | - ->expects($this->once()) |
|
186 | - ->method('getScope') |
|
187 | - ->willReturn(IAccountManager::SCOPE_PUBLISHED); |
|
188 | - |
|
189 | - $this->plugin->handleGetProperties( |
|
190 | - $propFind, |
|
191 | - $node |
|
192 | - ); |
|
193 | - |
|
194 | - $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); |
|
195 | - $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); |
|
196 | - $this->assertEquals('123', $propFind->get(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME)); |
|
197 | - $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(FilesPlugin::CREATIONDATE_PROPERTYNAME)); |
|
198 | - $this->assertEquals(0, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); |
|
199 | - $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
200 | - $this->assertEquals('http://example.com/', $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
201 | - $this->assertEquals('foo', $propFind->get(FilesPlugin::OWNER_ID_PROPERTYNAME)); |
|
202 | - $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
203 | - $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
204 | - $this->assertEquals([], $propFind->get404Properties()); |
|
205 | - } |
|
206 | - |
|
207 | - public function testGetDisplayNamePropertyWhenNotPublished(): void { |
|
208 | - /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
209 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
210 | - |
|
211 | - $propFind = new PropFind( |
|
212 | - '/dummyPath', |
|
213 | - [ |
|
214 | - FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
215 | - ], |
|
216 | - 0 |
|
217 | - ); |
|
218 | - |
|
219 | - $this->userSession->expects($this->once()) |
|
220 | - ->method('getUser') |
|
221 | - ->willReturn(null); |
|
222 | - |
|
223 | - $user = $this->getMockBuilder(User::class) |
|
224 | - ->disableOriginalConstructor()->getMock(); |
|
225 | - |
|
226 | - $user |
|
227 | - ->expects($this->never()) |
|
228 | - ->method('getDisplayName'); |
|
229 | - |
|
230 | - $owner = $this->getMockBuilder(Account::class) |
|
231 | - ->disableOriginalConstructor()->getMock(); |
|
232 | - $this->accountManager->expects($this->once()) |
|
233 | - ->method('getAccount') |
|
234 | - ->with($user) |
|
235 | - ->willReturn($owner); |
|
236 | - |
|
237 | - $node->expects($this->once()) |
|
238 | - ->method('getOwner') |
|
239 | - ->willReturn($user); |
|
240 | - |
|
241 | - $displayNameProp = $this->getMockBuilder(AccountProperty::class) |
|
242 | - ->disableOriginalConstructor()->getMock(); |
|
243 | - $owner |
|
244 | - ->expects($this->once()) |
|
245 | - ->method('getProperty') |
|
246 | - ->with(IAccountManager::PROPERTY_DISPLAYNAME) |
|
247 | - ->willReturn($displayNameProp); |
|
248 | - $displayNameProp |
|
249 | - ->expects($this->once()) |
|
250 | - ->method('getScope') |
|
251 | - ->willReturn(IAccountManager::SCOPE_PRIVATE); |
|
252 | - |
|
253 | - $this->plugin->handleGetProperties( |
|
254 | - $propFind, |
|
255 | - $node |
|
256 | - ); |
|
257 | - |
|
258 | - $this->assertEquals(null, $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
259 | - } |
|
42 | + private Tree&MockObject $tree; |
|
43 | + private Server&MockObject $server; |
|
44 | + private IConfig&MockObject $config; |
|
45 | + private IRequest&MockObject $request; |
|
46 | + private IPreview&MockObject $previewManager; |
|
47 | + private IUserSession&MockObject $userSession; |
|
48 | + private IFilenameValidator&MockObject $filenameValidator; |
|
49 | + private IAccountManager&MockObject $accountManager; |
|
50 | + private FilesPlugin $plugin; |
|
51 | + |
|
52 | + protected function setUp(): void { |
|
53 | + parent::setUp(); |
|
54 | + $this->server = $this->createMock(Server::class); |
|
55 | + $this->tree = $this->createMock(Tree::class); |
|
56 | + $this->config = $this->createMock(IConfig::class); |
|
57 | + $this->config->expects($this->any())->method('getSystemValue') |
|
58 | + ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) |
|
59 | + ->willReturn('my_fingerprint'); |
|
60 | + $this->request = $this->createMock(IRequest::class); |
|
61 | + $this->previewManager = $this->createMock(IPreview::class); |
|
62 | + $this->userSession = $this->createMock(IUserSession::class); |
|
63 | + $this->filenameValidator = $this->createMock(IFilenameValidator::class); |
|
64 | + $this->accountManager = $this->createMock(IAccountManager::class); |
|
65 | + |
|
66 | + $this->plugin = new FilesPlugin( |
|
67 | + $this->tree, |
|
68 | + $this->config, |
|
69 | + $this->request, |
|
70 | + $this->previewManager, |
|
71 | + $this->userSession, |
|
72 | + $this->filenameValidator, |
|
73 | + $this->accountManager, |
|
74 | + ); |
|
75 | + |
|
76 | + $response = $this->getMockBuilder(ResponseInterface::class) |
|
77 | + ->disableOriginalConstructor() |
|
78 | + ->getMock(); |
|
79 | + $this->server->httpResponse = $response; |
|
80 | + $this->server->xml = new Service(); |
|
81 | + |
|
82 | + $this->plugin->initialize($this->server); |
|
83 | + } |
|
84 | + |
|
85 | + /** |
|
86 | + * @param string $class |
|
87 | + * @return \PHPUnit\Framework\MockObject\MockObject |
|
88 | + */ |
|
89 | + private function createTestNode($class, $path = '/dummypath') { |
|
90 | + $node = $this->getMockBuilder($class) |
|
91 | + ->disableOriginalConstructor() |
|
92 | + ->getMock(); |
|
93 | + |
|
94 | + $node->expects($this->any()) |
|
95 | + ->method('getId') |
|
96 | + ->willReturn(123); |
|
97 | + |
|
98 | + $this->tree->expects($this->any()) |
|
99 | + ->method('getNodeForPath') |
|
100 | + ->with($path) |
|
101 | + ->willReturn($node); |
|
102 | + |
|
103 | + $node->expects($this->any()) |
|
104 | + ->method('getFileId') |
|
105 | + ->willReturn('00000123instanceid'); |
|
106 | + $node->expects($this->any()) |
|
107 | + ->method('getInternalFileId') |
|
108 | + ->willReturn('123'); |
|
109 | + $node->expects($this->any()) |
|
110 | + ->method('getEtag') |
|
111 | + ->willReturn('"abc"'); |
|
112 | + $node->expects($this->any()) |
|
113 | + ->method('getDavPermissions') |
|
114 | + ->willReturn('DWCKMSR'); |
|
115 | + |
|
116 | + $fileInfo = $this->createMock(FileInfo::class); |
|
117 | + $fileInfo->expects($this->any()) |
|
118 | + ->method('isReadable') |
|
119 | + ->willReturn(true); |
|
120 | + $fileInfo->expects($this->any()) |
|
121 | + ->method('getCreationTime') |
|
122 | + ->willReturn(123456789); |
|
123 | + |
|
124 | + $node->expects($this->any()) |
|
125 | + ->method('getFileInfo') |
|
126 | + ->willReturn($fileInfo); |
|
127 | + |
|
128 | + return $node; |
|
129 | + } |
|
130 | + |
|
131 | + public function testGetPropertiesForFile(): void { |
|
132 | + /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
133 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
134 | + |
|
135 | + $propFind = new PropFind( |
|
136 | + '/dummyPath', |
|
137 | + [ |
|
138 | + FilesPlugin::GETETAG_PROPERTYNAME, |
|
139 | + FilesPlugin::FILEID_PROPERTYNAME, |
|
140 | + FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, |
|
141 | + FilesPlugin::SIZE_PROPERTYNAME, |
|
142 | + FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
143 | + FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
144 | + FilesPlugin::OWNER_ID_PROPERTYNAME, |
|
145 | + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
146 | + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
147 | + FilesPlugin::CREATIONDATE_PROPERTYNAME, |
|
148 | + ], |
|
149 | + 0 |
|
150 | + ); |
|
151 | + |
|
152 | + $user = $this->getMockBuilder(User::class) |
|
153 | + ->disableOriginalConstructor()->getMock(); |
|
154 | + $user |
|
155 | + ->expects($this->once()) |
|
156 | + ->method('getUID') |
|
157 | + ->willReturn('foo'); |
|
158 | + $user |
|
159 | + ->expects($this->once()) |
|
160 | + ->method('getDisplayName') |
|
161 | + ->willReturn('M. Foo'); |
|
162 | + |
|
163 | + $owner = $this->getMockBuilder(Account::class) |
|
164 | + ->disableOriginalConstructor()->getMock(); |
|
165 | + $this->accountManager->expects($this->once()) |
|
166 | + ->method('getAccount') |
|
167 | + ->with($user) |
|
168 | + ->willReturn($owner); |
|
169 | + |
|
170 | + $node->expects($this->once()) |
|
171 | + ->method('getDirectDownload') |
|
172 | + ->willReturn(['url' => 'http://example.com/']); |
|
173 | + $node->expects($this->exactly(2)) |
|
174 | + ->method('getOwner') |
|
175 | + ->willReturn($user); |
|
176 | + |
|
177 | + $displayNameProp = $this->getMockBuilder(AccountProperty::class) |
|
178 | + ->disableOriginalConstructor()->getMock(); |
|
179 | + $owner |
|
180 | + ->expects($this->once()) |
|
181 | + ->method('getProperty') |
|
182 | + ->with(IAccountManager::PROPERTY_DISPLAYNAME) |
|
183 | + ->willReturn($displayNameProp); |
|
184 | + $displayNameProp |
|
185 | + ->expects($this->once()) |
|
186 | + ->method('getScope') |
|
187 | + ->willReturn(IAccountManager::SCOPE_PUBLISHED); |
|
188 | + |
|
189 | + $this->plugin->handleGetProperties( |
|
190 | + $propFind, |
|
191 | + $node |
|
192 | + ); |
|
193 | + |
|
194 | + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); |
|
195 | + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); |
|
196 | + $this->assertEquals('123', $propFind->get(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME)); |
|
197 | + $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(FilesPlugin::CREATIONDATE_PROPERTYNAME)); |
|
198 | + $this->assertEquals(0, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); |
|
199 | + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
200 | + $this->assertEquals('http://example.com/', $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
201 | + $this->assertEquals('foo', $propFind->get(FilesPlugin::OWNER_ID_PROPERTYNAME)); |
|
202 | + $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
203 | + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
204 | + $this->assertEquals([], $propFind->get404Properties()); |
|
205 | + } |
|
206 | + |
|
207 | + public function testGetDisplayNamePropertyWhenNotPublished(): void { |
|
208 | + /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
209 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
210 | + |
|
211 | + $propFind = new PropFind( |
|
212 | + '/dummyPath', |
|
213 | + [ |
|
214 | + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
215 | + ], |
|
216 | + 0 |
|
217 | + ); |
|
218 | + |
|
219 | + $this->userSession->expects($this->once()) |
|
220 | + ->method('getUser') |
|
221 | + ->willReturn(null); |
|
222 | + |
|
223 | + $user = $this->getMockBuilder(User::class) |
|
224 | + ->disableOriginalConstructor()->getMock(); |
|
225 | + |
|
226 | + $user |
|
227 | + ->expects($this->never()) |
|
228 | + ->method('getDisplayName'); |
|
229 | + |
|
230 | + $owner = $this->getMockBuilder(Account::class) |
|
231 | + ->disableOriginalConstructor()->getMock(); |
|
232 | + $this->accountManager->expects($this->once()) |
|
233 | + ->method('getAccount') |
|
234 | + ->with($user) |
|
235 | + ->willReturn($owner); |
|
236 | + |
|
237 | + $node->expects($this->once()) |
|
238 | + ->method('getOwner') |
|
239 | + ->willReturn($user); |
|
240 | + |
|
241 | + $displayNameProp = $this->getMockBuilder(AccountProperty::class) |
|
242 | + ->disableOriginalConstructor()->getMock(); |
|
243 | + $owner |
|
244 | + ->expects($this->once()) |
|
245 | + ->method('getProperty') |
|
246 | + ->with(IAccountManager::PROPERTY_DISPLAYNAME) |
|
247 | + ->willReturn($displayNameProp); |
|
248 | + $displayNameProp |
|
249 | + ->expects($this->once()) |
|
250 | + ->method('getScope') |
|
251 | + ->willReturn(IAccountManager::SCOPE_PRIVATE); |
|
252 | + |
|
253 | + $this->plugin->handleGetProperties( |
|
254 | + $propFind, |
|
255 | + $node |
|
256 | + ); |
|
257 | + |
|
258 | + $this->assertEquals(null, $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
259 | + } |
|
260 | 260 | |
261 | - public function testGetDisplayNamePropertyWhenNotPublishedButLoggedIn(): void { |
|
262 | - /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
263 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
264 | - |
|
265 | - $propFind = new PropFind( |
|
266 | - '/dummyPath', |
|
267 | - [ |
|
268 | - FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
269 | - ], |
|
270 | - 0 |
|
271 | - ); |
|
272 | - |
|
273 | - $user = $this->getMockBuilder(User::class) |
|
274 | - ->disableOriginalConstructor()->getMock(); |
|
275 | - |
|
276 | - $node->expects($this->once()) |
|
277 | - ->method('getOwner') |
|
278 | - ->willReturn($user); |
|
279 | - |
|
280 | - $loggedInUser = $this->getMockBuilder(User::class) |
|
281 | - ->disableOriginalConstructor()->getMock(); |
|
282 | - $this->userSession->expects($this->once()) |
|
283 | - ->method('getUser') |
|
284 | - ->willReturn($loggedInUser); |
|
285 | - |
|
286 | - $user |
|
287 | - ->expects($this->once()) |
|
288 | - ->method('getDisplayName') |
|
289 | - ->willReturn('M. Foo'); |
|
290 | - |
|
291 | - $this->accountManager->expects($this->never()) |
|
292 | - ->method('getAccount'); |
|
293 | - |
|
294 | - $this->plugin->handleGetProperties( |
|
295 | - $propFind, |
|
296 | - $node |
|
297 | - ); |
|
298 | - |
|
299 | - $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
300 | - } |
|
301 | - |
|
302 | - public function testGetPropertiesStorageNotAvailable(): void { |
|
303 | - /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
304 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
305 | - |
|
306 | - $propFind = new PropFind( |
|
307 | - '/dummyPath', |
|
308 | - [ |
|
309 | - FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
310 | - ], |
|
311 | - 0 |
|
312 | - ); |
|
313 | - |
|
314 | - $node->expects($this->once()) |
|
315 | - ->method('getDirectDownload') |
|
316 | - ->will($this->throwException(new StorageNotAvailableException())); |
|
317 | - |
|
318 | - $this->plugin->handleGetProperties( |
|
319 | - $propFind, |
|
320 | - $node |
|
321 | - ); |
|
322 | - |
|
323 | - $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
324 | - } |
|
325 | - |
|
326 | - public function testGetPublicPermissions(): void { |
|
327 | - /** @var IRequest&MockObject */ |
|
328 | - $request = $this->getMockBuilder(IRequest::class) |
|
329 | - ->disableOriginalConstructor() |
|
330 | - ->getMock(); |
|
331 | - $this->plugin = new FilesPlugin( |
|
332 | - $this->tree, |
|
333 | - $this->config, |
|
334 | - $request, |
|
335 | - $this->previewManager, |
|
336 | - $this->userSession, |
|
337 | - $this->filenameValidator, |
|
338 | - $this->accountManager, |
|
339 | - true, |
|
340 | - ); |
|
341 | - $this->plugin->initialize($this->server); |
|
342 | - |
|
343 | - $propFind = new PropFind( |
|
344 | - '/dummyPath', |
|
345 | - [ |
|
346 | - FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
347 | - ], |
|
348 | - 0 |
|
349 | - ); |
|
350 | - |
|
351 | - /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
352 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
353 | - $node->expects($this->any()) |
|
354 | - ->method('getDavPermissions') |
|
355 | - ->willReturn('DWCKMSR'); |
|
356 | - |
|
357 | - $this->plugin->handleGetProperties( |
|
358 | - $propFind, |
|
359 | - $node |
|
360 | - ); |
|
361 | - |
|
362 | - $this->assertEquals('DWCKR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
363 | - } |
|
364 | - |
|
365 | - public function testGetPropertiesForDirectory(): void { |
|
366 | - /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
367 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); |
|
368 | - |
|
369 | - $propFind = new PropFind( |
|
370 | - '/dummyPath', |
|
371 | - [ |
|
372 | - FilesPlugin::GETETAG_PROPERTYNAME, |
|
373 | - FilesPlugin::FILEID_PROPERTYNAME, |
|
374 | - FilesPlugin::SIZE_PROPERTYNAME, |
|
375 | - FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
376 | - FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
377 | - FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
378 | - ], |
|
379 | - 0 |
|
380 | - ); |
|
381 | - |
|
382 | - $node->expects($this->once()) |
|
383 | - ->method('getSize') |
|
384 | - ->willReturn(1025); |
|
385 | - |
|
386 | - $this->plugin->handleGetProperties( |
|
387 | - $propFind, |
|
388 | - $node |
|
389 | - ); |
|
390 | - |
|
391 | - $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); |
|
392 | - $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); |
|
393 | - $this->assertEquals(1025, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); |
|
394 | - $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
395 | - $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
396 | - $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
397 | - $this->assertEquals([FilesPlugin::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); |
|
398 | - } |
|
399 | - |
|
400 | - public function testGetPropertiesForRootDirectory(): void { |
|
401 | - /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
402 | - $node = $this->getMockBuilder(Directory::class) |
|
403 | - ->disableOriginalConstructor() |
|
404 | - ->getMock(); |
|
405 | - $node->expects($this->any())->method('getPath')->willReturn('/'); |
|
406 | - |
|
407 | - $fileInfo = $this->createMock(FileInfo::class); |
|
408 | - $fileInfo->expects($this->any()) |
|
409 | - ->method('isReadable') |
|
410 | - ->willReturn(true); |
|
411 | - |
|
412 | - $node->expects($this->any()) |
|
413 | - ->method('getFileInfo') |
|
414 | - ->willReturn($fileInfo); |
|
415 | - |
|
416 | - $propFind = new PropFind( |
|
417 | - '/', |
|
418 | - [ |
|
419 | - FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
420 | - ], |
|
421 | - 0 |
|
422 | - ); |
|
423 | - |
|
424 | - $this->plugin->handleGetProperties( |
|
425 | - $propFind, |
|
426 | - $node |
|
427 | - ); |
|
428 | - |
|
429 | - $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
430 | - } |
|
431 | - |
|
432 | - public function testGetPropertiesWhenNoPermission(): void { |
|
433 | - // No read permissions can be caused by files access control. |
|
434 | - // But we still want to load the directory list, so this is okay for us. |
|
435 | - // $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
436 | - /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
437 | - $node = $this->getMockBuilder(Directory::class) |
|
438 | - ->disableOriginalConstructor() |
|
439 | - ->getMock(); |
|
440 | - $node->expects($this->any())->method('getPath')->willReturn('/'); |
|
441 | - |
|
442 | - $fileInfo = $this->createMock(FileInfo::class); |
|
443 | - $fileInfo->expects($this->any()) |
|
444 | - ->method('isReadable') |
|
445 | - ->willReturn(false); |
|
446 | - |
|
447 | - $node->expects($this->any()) |
|
448 | - ->method('getFileInfo') |
|
449 | - ->willReturn($fileInfo); |
|
450 | - |
|
451 | - $propFind = new PropFind( |
|
452 | - '/test', |
|
453 | - [ |
|
454 | - FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
455 | - ], |
|
456 | - 0 |
|
457 | - ); |
|
458 | - |
|
459 | - $this->plugin->handleGetProperties( |
|
460 | - $propFind, |
|
461 | - $node |
|
462 | - ); |
|
463 | - |
|
464 | - $this->addToAssertionCount(1); |
|
465 | - } |
|
466 | - |
|
467 | - public function testUpdateProps(): void { |
|
468 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
469 | - |
|
470 | - $testDate = 'Fri, 13 Feb 2015 00:01:02 GMT'; |
|
471 | - $testCreationDate = '2007-08-31T16:47+00:00'; |
|
472 | - |
|
473 | - $node->expects($this->once()) |
|
474 | - ->method('touch') |
|
475 | - ->with($testDate); |
|
476 | - |
|
477 | - $node->expects($this->once()) |
|
478 | - ->method('setEtag') |
|
479 | - ->with('newetag') |
|
480 | - ->willReturn(true); |
|
481 | - |
|
482 | - $node->expects($this->once()) |
|
483 | - ->method('setCreationTime') |
|
484 | - ->with('1188578820'); |
|
485 | - |
|
486 | - // properties to set |
|
487 | - $propPatch = new PropPatch([ |
|
488 | - FilesPlugin::GETETAG_PROPERTYNAME => 'newetag', |
|
489 | - FilesPlugin::LASTMODIFIED_PROPERTYNAME => $testDate, |
|
490 | - FilesPlugin::CREATIONDATE_PROPERTYNAME => $testCreationDate, |
|
491 | - ]); |
|
492 | - |
|
493 | - |
|
494 | - $this->plugin->handleUpdateProperties( |
|
495 | - '/dummypath', |
|
496 | - $propPatch |
|
497 | - ); |
|
498 | - |
|
499 | - $propPatch->commit(); |
|
500 | - |
|
501 | - $this->assertEmpty($propPatch->getRemainingMutations()); |
|
502 | - |
|
503 | - $result = $propPatch->getResult(); |
|
504 | - $this->assertEquals(200, $result[FilesPlugin::LASTMODIFIED_PROPERTYNAME]); |
|
505 | - $this->assertEquals(200, $result[FilesPlugin::GETETAG_PROPERTYNAME]); |
|
506 | - $this->assertEquals(200, $result[FilesPlugin::CREATIONDATE_PROPERTYNAME]); |
|
507 | - } |
|
508 | - |
|
509 | - public function testUpdatePropsForbidden(): void { |
|
510 | - $propPatch = new PropPatch([ |
|
511 | - FilesPlugin::OWNER_ID_PROPERTYNAME => 'user2', |
|
512 | - FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', |
|
513 | - FilesPlugin::FILEID_PROPERTYNAME => 12345, |
|
514 | - FilesPlugin::PERMISSIONS_PROPERTYNAME => 'C', |
|
515 | - FilesPlugin::SIZE_PROPERTYNAME => 123, |
|
516 | - FilesPlugin::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', |
|
517 | - ]); |
|
518 | - |
|
519 | - $this->plugin->handleUpdateProperties( |
|
520 | - '/dummypath', |
|
521 | - $propPatch |
|
522 | - ); |
|
523 | - |
|
524 | - $propPatch->commit(); |
|
525 | - |
|
526 | - $this->assertEmpty($propPatch->getRemainingMutations()); |
|
527 | - |
|
528 | - $result = $propPatch->getResult(); |
|
529 | - $this->assertEquals(403, $result[FilesPlugin::OWNER_ID_PROPERTYNAME]); |
|
530 | - $this->assertEquals(403, $result[FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME]); |
|
531 | - $this->assertEquals(403, $result[FilesPlugin::FILEID_PROPERTYNAME]); |
|
532 | - $this->assertEquals(403, $result[FilesPlugin::PERMISSIONS_PROPERTYNAME]); |
|
533 | - $this->assertEquals(403, $result[FilesPlugin::SIZE_PROPERTYNAME]); |
|
534 | - $this->assertEquals(403, $result[FilesPlugin::DOWNLOADURL_PROPERTYNAME]); |
|
535 | - } |
|
536 | - |
|
537 | - /** |
|
538 | - * Test case from https://github.com/owncloud/core/issues/5251 |
|
539 | - * |
|
540 | - * |-FolderA |
|
541 | - * |-text.txt |
|
542 | - * |-test.txt |
|
543 | - * |
|
544 | - * FolderA is an incoming shared folder and there are no delete permissions. |
|
545 | - * Thus moving /FolderA/test.txt to /test.txt should fail already on that check |
|
546 | - * |
|
547 | - */ |
|
548 | - public function testMoveSrcNotDeletable(): void { |
|
549 | - $this->expectException(\Sabre\DAV\Exception\Forbidden::class); |
|
550 | - $this->expectExceptionMessage('FolderA/test.txt cannot be deleted'); |
|
551 | - |
|
552 | - $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) |
|
553 | - ->disableOriginalConstructor() |
|
554 | - ->getMock(); |
|
555 | - $fileInfoFolderATestTXT->expects($this->once()) |
|
556 | - ->method('isDeletable') |
|
557 | - ->willReturn(false); |
|
558 | - |
|
559 | - $node = $this->getMockBuilder(Node::class) |
|
560 | - ->disableOriginalConstructor() |
|
561 | - ->getMock(); |
|
562 | - $node->expects($this->atLeastOnce()) |
|
563 | - ->method('getFileInfo') |
|
564 | - ->willReturn($fileInfoFolderATestTXT); |
|
565 | - |
|
566 | - $this->tree->expects($this->atLeastOnce()) |
|
567 | - ->method('getNodeForPath') |
|
568 | - ->willReturn($node); |
|
569 | - |
|
570 | - $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
571 | - } |
|
572 | - |
|
573 | - public function testMoveSrcDeletable(): void { |
|
574 | - $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) |
|
575 | - ->disableOriginalConstructor() |
|
576 | - ->getMock(); |
|
577 | - $fileInfoFolderATestTXT->expects($this->once()) |
|
578 | - ->method('isDeletable') |
|
579 | - ->willReturn(true); |
|
580 | - |
|
581 | - $node = $this->getMockBuilder(Node::class) |
|
582 | - ->disableOriginalConstructor() |
|
583 | - ->getMock(); |
|
584 | - $node->expects($this->atLeastOnce()) |
|
585 | - ->method('getFileInfo') |
|
586 | - ->willReturn($fileInfoFolderATestTXT); |
|
587 | - |
|
588 | - $this->tree->expects($this->atLeastOnce()) |
|
589 | - ->method('getNodeForPath') |
|
590 | - ->willReturn($node); |
|
591 | - |
|
592 | - $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
593 | - } |
|
594 | - |
|
595 | - public function testMoveSrcNotExist(): void { |
|
596 | - $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
597 | - $this->expectExceptionMessage('FolderA/test.txt does not exist'); |
|
598 | - |
|
599 | - $node = $this->getMockBuilder(Node::class) |
|
600 | - ->disableOriginalConstructor() |
|
601 | - ->getMock(); |
|
602 | - $node->expects($this->atLeastOnce()) |
|
603 | - ->method('getFileInfo') |
|
604 | - ->willReturn(null); |
|
605 | - |
|
606 | - $this->tree->expects($this->atLeastOnce()) |
|
607 | - ->method('getNodeForPath') |
|
608 | - ->willReturn($node); |
|
609 | - |
|
610 | - $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
611 | - } |
|
612 | - |
|
613 | - public function testMoveDestinationInvalid(): void { |
|
614 | - $this->expectException(InvalidPath::class); |
|
615 | - $this->expectExceptionMessage('Mocked exception'); |
|
616 | - |
|
617 | - $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); |
|
618 | - $fileInfoFolderATestTXT->expects(self::any()) |
|
619 | - ->method('isDeletable') |
|
620 | - ->willReturn(true); |
|
621 | - |
|
622 | - $node = $this->createMock(Node::class); |
|
623 | - $node->expects($this->atLeastOnce()) |
|
624 | - ->method('getFileInfo') |
|
625 | - ->willReturn($fileInfoFolderATestTXT); |
|
626 | - |
|
627 | - $this->tree->expects($this->atLeastOnce()) |
|
628 | - ->method('getNodeForPath') |
|
629 | - ->willReturn($node); |
|
630 | - |
|
631 | - $this->filenameValidator->expects(self::once()) |
|
632 | - ->method('validateFilename') |
|
633 | - ->with('invalid\\path.txt') |
|
634 | - ->willThrowException(new InvalidPathException('Mocked exception')); |
|
635 | - |
|
636 | - $this->plugin->checkMove('FolderA/test.txt', 'invalid\\path.txt'); |
|
637 | - } |
|
638 | - |
|
639 | - public function testCopySrcNotExist(): void { |
|
640 | - $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
641 | - $this->expectExceptionMessage('FolderA/test.txt does not exist'); |
|
642 | - |
|
643 | - $node = $this->createMock(Node::class); |
|
644 | - $node->expects($this->atLeastOnce()) |
|
645 | - ->method('getFileInfo') |
|
646 | - ->willReturn(null); |
|
647 | - |
|
648 | - $this->tree->expects($this->atLeastOnce()) |
|
649 | - ->method('getNodeForPath') |
|
650 | - ->willReturn($node); |
|
651 | - |
|
652 | - $this->plugin->checkCopy('FolderA/test.txt', 'test.txt'); |
|
653 | - } |
|
654 | - |
|
655 | - public function testCopyDestinationInvalid(): void { |
|
656 | - $this->expectException(InvalidPath::class); |
|
657 | - $this->expectExceptionMessage('Mocked exception'); |
|
658 | - |
|
659 | - $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); |
|
660 | - $node = $this->createMock(Node::class); |
|
661 | - $node->expects($this->atLeastOnce()) |
|
662 | - ->method('getFileInfo') |
|
663 | - ->willReturn($fileInfoFolderATestTXT); |
|
664 | - |
|
665 | - $this->tree->expects($this->atLeastOnce()) |
|
666 | - ->method('getNodeForPath') |
|
667 | - ->willReturn($node); |
|
668 | - |
|
669 | - $this->filenameValidator->expects(self::once()) |
|
670 | - ->method('validateFilename') |
|
671 | - ->with('invalid\\path.txt') |
|
672 | - ->willThrowException(new InvalidPathException('Mocked exception')); |
|
673 | - |
|
674 | - $this->plugin->checkCopy('FolderA/test.txt', 'invalid\\path.txt'); |
|
675 | - } |
|
676 | - |
|
677 | - public function downloadHeadersProvider() { |
|
678 | - return [ |
|
679 | - [ |
|
680 | - false, |
|
681 | - 'attachment; filename*=UTF-8\'\'somefile.xml; filename="somefile.xml"' |
|
682 | - ], |
|
683 | - [ |
|
684 | - true, |
|
685 | - 'attachment; filename="somefile.xml"' |
|
686 | - ], |
|
687 | - ]; |
|
688 | - } |
|
689 | - |
|
690 | - /** |
|
691 | - * @dataProvider downloadHeadersProvider |
|
692 | - */ |
|
693 | - public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader): void { |
|
694 | - $request = $this->getMockBuilder(RequestInterface::class) |
|
695 | - ->disableOriginalConstructor() |
|
696 | - ->getMock(); |
|
697 | - $response = $this->getMockBuilder(ResponseInterface::class) |
|
698 | - ->disableOriginalConstructor() |
|
699 | - ->getMock(); |
|
700 | - |
|
701 | - $request |
|
702 | - ->expects($this->once()) |
|
703 | - ->method('getPath') |
|
704 | - ->willReturn('test/somefile.xml'); |
|
705 | - |
|
706 | - $node = $this->getMockBuilder(File::class) |
|
707 | - ->disableOriginalConstructor() |
|
708 | - ->getMock(); |
|
709 | - $node |
|
710 | - ->expects($this->once()) |
|
711 | - ->method('getName') |
|
712 | - ->willReturn('somefile.xml'); |
|
713 | - |
|
714 | - $this->tree |
|
715 | - ->expects($this->once()) |
|
716 | - ->method('getNodeForPath') |
|
717 | - ->with('test/somefile.xml') |
|
718 | - ->willReturn($node); |
|
719 | - |
|
720 | - $this->request |
|
721 | - ->expects($this->once()) |
|
722 | - ->method('isUserAgent') |
|
723 | - ->willReturn($isClumsyAgent); |
|
724 | - |
|
725 | - $response |
|
726 | - ->expects($this->exactly(2)) |
|
727 | - ->method('addHeader') |
|
728 | - ->withConsecutive( |
|
729 | - ['Content-Disposition', $contentDispositionHeader], |
|
730 | - ['X-Accel-Buffering', 'no'] |
|
731 | - ); |
|
732 | - |
|
733 | - $this->plugin->httpGet($request, $response); |
|
734 | - } |
|
735 | - |
|
736 | - public function testHasPreview(): void { |
|
737 | - /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
738 | - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); |
|
739 | - |
|
740 | - $propFind = new PropFind( |
|
741 | - '/dummyPath', |
|
742 | - [ |
|
743 | - FilesPlugin::HAS_PREVIEW_PROPERTYNAME |
|
744 | - ], |
|
745 | - 0 |
|
746 | - ); |
|
747 | - |
|
748 | - $this->previewManager->expects($this->once()) |
|
749 | - ->method('isAvailable') |
|
750 | - ->willReturn(false); |
|
751 | - |
|
752 | - $this->plugin->handleGetProperties( |
|
753 | - $propFind, |
|
754 | - $node |
|
755 | - ); |
|
756 | - |
|
757 | - $this->assertEquals('false', $propFind->get(FilesPlugin::HAS_PREVIEW_PROPERTYNAME)); |
|
758 | - } |
|
261 | + public function testGetDisplayNamePropertyWhenNotPublishedButLoggedIn(): void { |
|
262 | + /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
263 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
264 | + |
|
265 | + $propFind = new PropFind( |
|
266 | + '/dummyPath', |
|
267 | + [ |
|
268 | + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, |
|
269 | + ], |
|
270 | + 0 |
|
271 | + ); |
|
272 | + |
|
273 | + $user = $this->getMockBuilder(User::class) |
|
274 | + ->disableOriginalConstructor()->getMock(); |
|
275 | + |
|
276 | + $node->expects($this->once()) |
|
277 | + ->method('getOwner') |
|
278 | + ->willReturn($user); |
|
279 | + |
|
280 | + $loggedInUser = $this->getMockBuilder(User::class) |
|
281 | + ->disableOriginalConstructor()->getMock(); |
|
282 | + $this->userSession->expects($this->once()) |
|
283 | + ->method('getUser') |
|
284 | + ->willReturn($loggedInUser); |
|
285 | + |
|
286 | + $user |
|
287 | + ->expects($this->once()) |
|
288 | + ->method('getDisplayName') |
|
289 | + ->willReturn('M. Foo'); |
|
290 | + |
|
291 | + $this->accountManager->expects($this->never()) |
|
292 | + ->method('getAccount'); |
|
293 | + |
|
294 | + $this->plugin->handleGetProperties( |
|
295 | + $propFind, |
|
296 | + $node |
|
297 | + ); |
|
298 | + |
|
299 | + $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); |
|
300 | + } |
|
301 | + |
|
302 | + public function testGetPropertiesStorageNotAvailable(): void { |
|
303 | + /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
304 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
305 | + |
|
306 | + $propFind = new PropFind( |
|
307 | + '/dummyPath', |
|
308 | + [ |
|
309 | + FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
310 | + ], |
|
311 | + 0 |
|
312 | + ); |
|
313 | + |
|
314 | + $node->expects($this->once()) |
|
315 | + ->method('getDirectDownload') |
|
316 | + ->will($this->throwException(new StorageNotAvailableException())); |
|
317 | + |
|
318 | + $this->plugin->handleGetProperties( |
|
319 | + $propFind, |
|
320 | + $node |
|
321 | + ); |
|
322 | + |
|
323 | + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
324 | + } |
|
325 | + |
|
326 | + public function testGetPublicPermissions(): void { |
|
327 | + /** @var IRequest&MockObject */ |
|
328 | + $request = $this->getMockBuilder(IRequest::class) |
|
329 | + ->disableOriginalConstructor() |
|
330 | + ->getMock(); |
|
331 | + $this->plugin = new FilesPlugin( |
|
332 | + $this->tree, |
|
333 | + $this->config, |
|
334 | + $request, |
|
335 | + $this->previewManager, |
|
336 | + $this->userSession, |
|
337 | + $this->filenameValidator, |
|
338 | + $this->accountManager, |
|
339 | + true, |
|
340 | + ); |
|
341 | + $this->plugin->initialize($this->server); |
|
342 | + |
|
343 | + $propFind = new PropFind( |
|
344 | + '/dummyPath', |
|
345 | + [ |
|
346 | + FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
347 | + ], |
|
348 | + 0 |
|
349 | + ); |
|
350 | + |
|
351 | + /** @var File|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
352 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
353 | + $node->expects($this->any()) |
|
354 | + ->method('getDavPermissions') |
|
355 | + ->willReturn('DWCKMSR'); |
|
356 | + |
|
357 | + $this->plugin->handleGetProperties( |
|
358 | + $propFind, |
|
359 | + $node |
|
360 | + ); |
|
361 | + |
|
362 | + $this->assertEquals('DWCKR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
363 | + } |
|
364 | + |
|
365 | + public function testGetPropertiesForDirectory(): void { |
|
366 | + /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
367 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); |
|
368 | + |
|
369 | + $propFind = new PropFind( |
|
370 | + '/dummyPath', |
|
371 | + [ |
|
372 | + FilesPlugin::GETETAG_PROPERTYNAME, |
|
373 | + FilesPlugin::FILEID_PROPERTYNAME, |
|
374 | + FilesPlugin::SIZE_PROPERTYNAME, |
|
375 | + FilesPlugin::PERMISSIONS_PROPERTYNAME, |
|
376 | + FilesPlugin::DOWNLOADURL_PROPERTYNAME, |
|
377 | + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
378 | + ], |
|
379 | + 0 |
|
380 | + ); |
|
381 | + |
|
382 | + $node->expects($this->once()) |
|
383 | + ->method('getSize') |
|
384 | + ->willReturn(1025); |
|
385 | + |
|
386 | + $this->plugin->handleGetProperties( |
|
387 | + $propFind, |
|
388 | + $node |
|
389 | + ); |
|
390 | + |
|
391 | + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); |
|
392 | + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); |
|
393 | + $this->assertEquals(1025, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); |
|
394 | + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); |
|
395 | + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); |
|
396 | + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
397 | + $this->assertEquals([FilesPlugin::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); |
|
398 | + } |
|
399 | + |
|
400 | + public function testGetPropertiesForRootDirectory(): void { |
|
401 | + /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
402 | + $node = $this->getMockBuilder(Directory::class) |
|
403 | + ->disableOriginalConstructor() |
|
404 | + ->getMock(); |
|
405 | + $node->expects($this->any())->method('getPath')->willReturn('/'); |
|
406 | + |
|
407 | + $fileInfo = $this->createMock(FileInfo::class); |
|
408 | + $fileInfo->expects($this->any()) |
|
409 | + ->method('isReadable') |
|
410 | + ->willReturn(true); |
|
411 | + |
|
412 | + $node->expects($this->any()) |
|
413 | + ->method('getFileInfo') |
|
414 | + ->willReturn($fileInfo); |
|
415 | + |
|
416 | + $propFind = new PropFind( |
|
417 | + '/', |
|
418 | + [ |
|
419 | + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
420 | + ], |
|
421 | + 0 |
|
422 | + ); |
|
423 | + |
|
424 | + $this->plugin->handleGetProperties( |
|
425 | + $propFind, |
|
426 | + $node |
|
427 | + ); |
|
428 | + |
|
429 | + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); |
|
430 | + } |
|
431 | + |
|
432 | + public function testGetPropertiesWhenNoPermission(): void { |
|
433 | + // No read permissions can be caused by files access control. |
|
434 | + // But we still want to load the directory list, so this is okay for us. |
|
435 | + // $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
436 | + /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
437 | + $node = $this->getMockBuilder(Directory::class) |
|
438 | + ->disableOriginalConstructor() |
|
439 | + ->getMock(); |
|
440 | + $node->expects($this->any())->method('getPath')->willReturn('/'); |
|
441 | + |
|
442 | + $fileInfo = $this->createMock(FileInfo::class); |
|
443 | + $fileInfo->expects($this->any()) |
|
444 | + ->method('isReadable') |
|
445 | + ->willReturn(false); |
|
446 | + |
|
447 | + $node->expects($this->any()) |
|
448 | + ->method('getFileInfo') |
|
449 | + ->willReturn($fileInfo); |
|
450 | + |
|
451 | + $propFind = new PropFind( |
|
452 | + '/test', |
|
453 | + [ |
|
454 | + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, |
|
455 | + ], |
|
456 | + 0 |
|
457 | + ); |
|
458 | + |
|
459 | + $this->plugin->handleGetProperties( |
|
460 | + $propFind, |
|
461 | + $node |
|
462 | + ); |
|
463 | + |
|
464 | + $this->addToAssertionCount(1); |
|
465 | + } |
|
466 | + |
|
467 | + public function testUpdateProps(): void { |
|
468 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); |
|
469 | + |
|
470 | + $testDate = 'Fri, 13 Feb 2015 00:01:02 GMT'; |
|
471 | + $testCreationDate = '2007-08-31T16:47+00:00'; |
|
472 | + |
|
473 | + $node->expects($this->once()) |
|
474 | + ->method('touch') |
|
475 | + ->with($testDate); |
|
476 | + |
|
477 | + $node->expects($this->once()) |
|
478 | + ->method('setEtag') |
|
479 | + ->with('newetag') |
|
480 | + ->willReturn(true); |
|
481 | + |
|
482 | + $node->expects($this->once()) |
|
483 | + ->method('setCreationTime') |
|
484 | + ->with('1188578820'); |
|
485 | + |
|
486 | + // properties to set |
|
487 | + $propPatch = new PropPatch([ |
|
488 | + FilesPlugin::GETETAG_PROPERTYNAME => 'newetag', |
|
489 | + FilesPlugin::LASTMODIFIED_PROPERTYNAME => $testDate, |
|
490 | + FilesPlugin::CREATIONDATE_PROPERTYNAME => $testCreationDate, |
|
491 | + ]); |
|
492 | + |
|
493 | + |
|
494 | + $this->plugin->handleUpdateProperties( |
|
495 | + '/dummypath', |
|
496 | + $propPatch |
|
497 | + ); |
|
498 | + |
|
499 | + $propPatch->commit(); |
|
500 | + |
|
501 | + $this->assertEmpty($propPatch->getRemainingMutations()); |
|
502 | + |
|
503 | + $result = $propPatch->getResult(); |
|
504 | + $this->assertEquals(200, $result[FilesPlugin::LASTMODIFIED_PROPERTYNAME]); |
|
505 | + $this->assertEquals(200, $result[FilesPlugin::GETETAG_PROPERTYNAME]); |
|
506 | + $this->assertEquals(200, $result[FilesPlugin::CREATIONDATE_PROPERTYNAME]); |
|
507 | + } |
|
508 | + |
|
509 | + public function testUpdatePropsForbidden(): void { |
|
510 | + $propPatch = new PropPatch([ |
|
511 | + FilesPlugin::OWNER_ID_PROPERTYNAME => 'user2', |
|
512 | + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', |
|
513 | + FilesPlugin::FILEID_PROPERTYNAME => 12345, |
|
514 | + FilesPlugin::PERMISSIONS_PROPERTYNAME => 'C', |
|
515 | + FilesPlugin::SIZE_PROPERTYNAME => 123, |
|
516 | + FilesPlugin::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', |
|
517 | + ]); |
|
518 | + |
|
519 | + $this->plugin->handleUpdateProperties( |
|
520 | + '/dummypath', |
|
521 | + $propPatch |
|
522 | + ); |
|
523 | + |
|
524 | + $propPatch->commit(); |
|
525 | + |
|
526 | + $this->assertEmpty($propPatch->getRemainingMutations()); |
|
527 | + |
|
528 | + $result = $propPatch->getResult(); |
|
529 | + $this->assertEquals(403, $result[FilesPlugin::OWNER_ID_PROPERTYNAME]); |
|
530 | + $this->assertEquals(403, $result[FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME]); |
|
531 | + $this->assertEquals(403, $result[FilesPlugin::FILEID_PROPERTYNAME]); |
|
532 | + $this->assertEquals(403, $result[FilesPlugin::PERMISSIONS_PROPERTYNAME]); |
|
533 | + $this->assertEquals(403, $result[FilesPlugin::SIZE_PROPERTYNAME]); |
|
534 | + $this->assertEquals(403, $result[FilesPlugin::DOWNLOADURL_PROPERTYNAME]); |
|
535 | + } |
|
536 | + |
|
537 | + /** |
|
538 | + * Test case from https://github.com/owncloud/core/issues/5251 |
|
539 | + * |
|
540 | + * |-FolderA |
|
541 | + * |-text.txt |
|
542 | + * |-test.txt |
|
543 | + * |
|
544 | + * FolderA is an incoming shared folder and there are no delete permissions. |
|
545 | + * Thus moving /FolderA/test.txt to /test.txt should fail already on that check |
|
546 | + * |
|
547 | + */ |
|
548 | + public function testMoveSrcNotDeletable(): void { |
|
549 | + $this->expectException(\Sabre\DAV\Exception\Forbidden::class); |
|
550 | + $this->expectExceptionMessage('FolderA/test.txt cannot be deleted'); |
|
551 | + |
|
552 | + $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) |
|
553 | + ->disableOriginalConstructor() |
|
554 | + ->getMock(); |
|
555 | + $fileInfoFolderATestTXT->expects($this->once()) |
|
556 | + ->method('isDeletable') |
|
557 | + ->willReturn(false); |
|
558 | + |
|
559 | + $node = $this->getMockBuilder(Node::class) |
|
560 | + ->disableOriginalConstructor() |
|
561 | + ->getMock(); |
|
562 | + $node->expects($this->atLeastOnce()) |
|
563 | + ->method('getFileInfo') |
|
564 | + ->willReturn($fileInfoFolderATestTXT); |
|
565 | + |
|
566 | + $this->tree->expects($this->atLeastOnce()) |
|
567 | + ->method('getNodeForPath') |
|
568 | + ->willReturn($node); |
|
569 | + |
|
570 | + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
571 | + } |
|
572 | + |
|
573 | + public function testMoveSrcDeletable(): void { |
|
574 | + $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) |
|
575 | + ->disableOriginalConstructor() |
|
576 | + ->getMock(); |
|
577 | + $fileInfoFolderATestTXT->expects($this->once()) |
|
578 | + ->method('isDeletable') |
|
579 | + ->willReturn(true); |
|
580 | + |
|
581 | + $node = $this->getMockBuilder(Node::class) |
|
582 | + ->disableOriginalConstructor() |
|
583 | + ->getMock(); |
|
584 | + $node->expects($this->atLeastOnce()) |
|
585 | + ->method('getFileInfo') |
|
586 | + ->willReturn($fileInfoFolderATestTXT); |
|
587 | + |
|
588 | + $this->tree->expects($this->atLeastOnce()) |
|
589 | + ->method('getNodeForPath') |
|
590 | + ->willReturn($node); |
|
591 | + |
|
592 | + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
593 | + } |
|
594 | + |
|
595 | + public function testMoveSrcNotExist(): void { |
|
596 | + $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
597 | + $this->expectExceptionMessage('FolderA/test.txt does not exist'); |
|
598 | + |
|
599 | + $node = $this->getMockBuilder(Node::class) |
|
600 | + ->disableOriginalConstructor() |
|
601 | + ->getMock(); |
|
602 | + $node->expects($this->atLeastOnce()) |
|
603 | + ->method('getFileInfo') |
|
604 | + ->willReturn(null); |
|
605 | + |
|
606 | + $this->tree->expects($this->atLeastOnce()) |
|
607 | + ->method('getNodeForPath') |
|
608 | + ->willReturn($node); |
|
609 | + |
|
610 | + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); |
|
611 | + } |
|
612 | + |
|
613 | + public function testMoveDestinationInvalid(): void { |
|
614 | + $this->expectException(InvalidPath::class); |
|
615 | + $this->expectExceptionMessage('Mocked exception'); |
|
616 | + |
|
617 | + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); |
|
618 | + $fileInfoFolderATestTXT->expects(self::any()) |
|
619 | + ->method('isDeletable') |
|
620 | + ->willReturn(true); |
|
621 | + |
|
622 | + $node = $this->createMock(Node::class); |
|
623 | + $node->expects($this->atLeastOnce()) |
|
624 | + ->method('getFileInfo') |
|
625 | + ->willReturn($fileInfoFolderATestTXT); |
|
626 | + |
|
627 | + $this->tree->expects($this->atLeastOnce()) |
|
628 | + ->method('getNodeForPath') |
|
629 | + ->willReturn($node); |
|
630 | + |
|
631 | + $this->filenameValidator->expects(self::once()) |
|
632 | + ->method('validateFilename') |
|
633 | + ->with('invalid\\path.txt') |
|
634 | + ->willThrowException(new InvalidPathException('Mocked exception')); |
|
635 | + |
|
636 | + $this->plugin->checkMove('FolderA/test.txt', 'invalid\\path.txt'); |
|
637 | + } |
|
638 | + |
|
639 | + public function testCopySrcNotExist(): void { |
|
640 | + $this->expectException(\Sabre\DAV\Exception\NotFound::class); |
|
641 | + $this->expectExceptionMessage('FolderA/test.txt does not exist'); |
|
642 | + |
|
643 | + $node = $this->createMock(Node::class); |
|
644 | + $node->expects($this->atLeastOnce()) |
|
645 | + ->method('getFileInfo') |
|
646 | + ->willReturn(null); |
|
647 | + |
|
648 | + $this->tree->expects($this->atLeastOnce()) |
|
649 | + ->method('getNodeForPath') |
|
650 | + ->willReturn($node); |
|
651 | + |
|
652 | + $this->plugin->checkCopy('FolderA/test.txt', 'test.txt'); |
|
653 | + } |
|
654 | + |
|
655 | + public function testCopyDestinationInvalid(): void { |
|
656 | + $this->expectException(InvalidPath::class); |
|
657 | + $this->expectExceptionMessage('Mocked exception'); |
|
658 | + |
|
659 | + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); |
|
660 | + $node = $this->createMock(Node::class); |
|
661 | + $node->expects($this->atLeastOnce()) |
|
662 | + ->method('getFileInfo') |
|
663 | + ->willReturn($fileInfoFolderATestTXT); |
|
664 | + |
|
665 | + $this->tree->expects($this->atLeastOnce()) |
|
666 | + ->method('getNodeForPath') |
|
667 | + ->willReturn($node); |
|
668 | + |
|
669 | + $this->filenameValidator->expects(self::once()) |
|
670 | + ->method('validateFilename') |
|
671 | + ->with('invalid\\path.txt') |
|
672 | + ->willThrowException(new InvalidPathException('Mocked exception')); |
|
673 | + |
|
674 | + $this->plugin->checkCopy('FolderA/test.txt', 'invalid\\path.txt'); |
|
675 | + } |
|
676 | + |
|
677 | + public function downloadHeadersProvider() { |
|
678 | + return [ |
|
679 | + [ |
|
680 | + false, |
|
681 | + 'attachment; filename*=UTF-8\'\'somefile.xml; filename="somefile.xml"' |
|
682 | + ], |
|
683 | + [ |
|
684 | + true, |
|
685 | + 'attachment; filename="somefile.xml"' |
|
686 | + ], |
|
687 | + ]; |
|
688 | + } |
|
689 | + |
|
690 | + /** |
|
691 | + * @dataProvider downloadHeadersProvider |
|
692 | + */ |
|
693 | + public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader): void { |
|
694 | + $request = $this->getMockBuilder(RequestInterface::class) |
|
695 | + ->disableOriginalConstructor() |
|
696 | + ->getMock(); |
|
697 | + $response = $this->getMockBuilder(ResponseInterface::class) |
|
698 | + ->disableOriginalConstructor() |
|
699 | + ->getMock(); |
|
700 | + |
|
701 | + $request |
|
702 | + ->expects($this->once()) |
|
703 | + ->method('getPath') |
|
704 | + ->willReturn('test/somefile.xml'); |
|
705 | + |
|
706 | + $node = $this->getMockBuilder(File::class) |
|
707 | + ->disableOriginalConstructor() |
|
708 | + ->getMock(); |
|
709 | + $node |
|
710 | + ->expects($this->once()) |
|
711 | + ->method('getName') |
|
712 | + ->willReturn('somefile.xml'); |
|
713 | + |
|
714 | + $this->tree |
|
715 | + ->expects($this->once()) |
|
716 | + ->method('getNodeForPath') |
|
717 | + ->with('test/somefile.xml') |
|
718 | + ->willReturn($node); |
|
719 | + |
|
720 | + $this->request |
|
721 | + ->expects($this->once()) |
|
722 | + ->method('isUserAgent') |
|
723 | + ->willReturn($isClumsyAgent); |
|
724 | + |
|
725 | + $response |
|
726 | + ->expects($this->exactly(2)) |
|
727 | + ->method('addHeader') |
|
728 | + ->withConsecutive( |
|
729 | + ['Content-Disposition', $contentDispositionHeader], |
|
730 | + ['X-Accel-Buffering', 'no'] |
|
731 | + ); |
|
732 | + |
|
733 | + $this->plugin->httpGet($request, $response); |
|
734 | + } |
|
735 | + |
|
736 | + public function testHasPreview(): void { |
|
737 | + /** @var Directory|\PHPUnit\Framework\MockObject\MockObject $node */ |
|
738 | + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); |
|
739 | + |
|
740 | + $propFind = new PropFind( |
|
741 | + '/dummyPath', |
|
742 | + [ |
|
743 | + FilesPlugin::HAS_PREVIEW_PROPERTYNAME |
|
744 | + ], |
|
745 | + 0 |
|
746 | + ); |
|
747 | + |
|
748 | + $this->previewManager->expects($this->once()) |
|
749 | + ->method('isAvailable') |
|
750 | + ->willReturn(false); |
|
751 | + |
|
752 | + $this->plugin->handleGetProperties( |
|
753 | + $propFind, |
|
754 | + $node |
|
755 | + ); |
|
756 | + |
|
757 | + $this->assertEquals('false', $propFind->get(FilesPlugin::HAS_PREVIEW_PROPERTYNAME)); |
|
758 | + } |
|
759 | 759 | } |
@@ -36,891 +36,891 @@ |
||
36 | 36 | |
37 | 37 | class FilesReportPluginTest extends \Test\TestCase { |
38 | 38 | |
39 | - private \Sabre\DAV\Server&MockObject $server; |
|
40 | - private Tree&MockObject $tree; |
|
41 | - private ISystemTagObjectMapper&MockObject $tagMapper; |
|
42 | - private ISystemTagManager&MockObject $tagManager; |
|
43 | - private ITags&MockObject $privateTags; |
|
44 | - private ITagManager&MockObject $privateTagManager; |
|
45 | - private IUserSession&MockObject $userSession; |
|
46 | - private FilesReportPluginImplementation $plugin; |
|
47 | - private View&MockObject $view; |
|
48 | - private IGroupManager&MockObject $groupManager; |
|
49 | - private Folder&MockObject $userFolder; |
|
50 | - private IPreview&MockObject $previewManager; |
|
51 | - private IAppManager&MockObject $appManager; |
|
52 | - |
|
53 | - protected function setUp(): void { |
|
54 | - parent::setUp(); |
|
55 | - $this->tree = $this->getMockBuilder(Tree::class) |
|
56 | - ->disableOriginalConstructor() |
|
57 | - ->getMock(); |
|
58 | - |
|
59 | - $this->view = $this->getMockBuilder(View::class) |
|
60 | - ->disableOriginalConstructor() |
|
61 | - ->getMock(); |
|
62 | - |
|
63 | - $this->server = $this->getMockBuilder('\Sabre\DAV\Server') |
|
64 | - ->setConstructorArgs([$this->tree]) |
|
65 | - ->onlyMethods(['getRequestUri', 'getBaseUri']) |
|
66 | - ->getMock(); |
|
67 | - |
|
68 | - $this->server->expects($this->any()) |
|
69 | - ->method('getBaseUri') |
|
70 | - ->willReturn('http://example.com/owncloud/remote.php/dav'); |
|
71 | - |
|
72 | - $this->groupManager = $this->getMockBuilder(IGroupManager::class) |
|
73 | - ->disableOriginalConstructor() |
|
74 | - ->getMock(); |
|
75 | - |
|
76 | - $this->userFolder = $this->getMockBuilder(Folder::class) |
|
77 | - ->disableOriginalConstructor() |
|
78 | - ->getMock(); |
|
79 | - |
|
80 | - $this->previewManager = $this->getMockBuilder(IPreview::class) |
|
81 | - ->disableOriginalConstructor() |
|
82 | - ->getMock(); |
|
83 | - |
|
84 | - $this->appManager = $this->getMockBuilder(IAppManager::class) |
|
85 | - ->disableOriginalConstructor() |
|
86 | - ->getMock(); |
|
87 | - |
|
88 | - $this->tagManager = $this->createMock(ISystemTagManager::class); |
|
89 | - $this->tagMapper = $this->createMock(ISystemTagObjectMapper::class); |
|
90 | - $this->userSession = $this->createMock(IUserSession::class); |
|
91 | - $this->privateTags = $this->createMock(ITags::class); |
|
92 | - $this->privateTagManager = $this->createMock(ITagManager::class); |
|
93 | - $this->privateTagManager->expects($this->any()) |
|
94 | - ->method('load') |
|
95 | - ->with('files') |
|
96 | - ->willReturn($this->privateTags); |
|
97 | - |
|
98 | - $user = $this->getMockBuilder(IUser::class) |
|
99 | - ->disableOriginalConstructor() |
|
100 | - ->getMock(); |
|
101 | - $user->expects($this->any()) |
|
102 | - ->method('getUID') |
|
103 | - ->willReturn('testuser'); |
|
104 | - $this->userSession->expects($this->any()) |
|
105 | - ->method('getUser') |
|
106 | - ->willReturn($user); |
|
107 | - |
|
108 | - $this->plugin = new FilesReportPluginImplementation( |
|
109 | - $this->tree, |
|
110 | - $this->view, |
|
111 | - $this->tagManager, |
|
112 | - $this->tagMapper, |
|
113 | - $this->privateTagManager, |
|
114 | - $this->userSession, |
|
115 | - $this->groupManager, |
|
116 | - $this->userFolder, |
|
117 | - $this->appManager |
|
118 | - ); |
|
119 | - } |
|
120 | - |
|
121 | - public function testOnReportInvalidNode(): void { |
|
122 | - $path = 'totally/unrelated/13'; |
|
123 | - |
|
124 | - $this->tree->expects($this->any()) |
|
125 | - ->method('getNodeForPath') |
|
126 | - ->with('/' . $path) |
|
127 | - ->willReturn( |
|
128 | - $this->getMockBuilder(INode::class) |
|
129 | - ->disableOriginalConstructor() |
|
130 | - ->getMock() |
|
131 | - ); |
|
132 | - |
|
133 | - $this->server->expects($this->any()) |
|
134 | - ->method('getRequestUri') |
|
135 | - ->willReturn($path); |
|
136 | - $this->plugin->initialize($this->server); |
|
137 | - |
|
138 | - $this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path)); |
|
139 | - } |
|
140 | - |
|
141 | - public function testOnReportInvalidReportName(): void { |
|
142 | - $path = 'test'; |
|
143 | - |
|
144 | - $this->tree->expects($this->any()) |
|
145 | - ->method('getNodeForPath') |
|
146 | - ->with('/' . $path) |
|
147 | - ->willReturn( |
|
148 | - $this->getMockBuilder(INode::class) |
|
149 | - ->disableOriginalConstructor() |
|
150 | - ->getMock() |
|
151 | - ); |
|
152 | - |
|
153 | - $this->server->expects($this->any()) |
|
154 | - ->method('getRequestUri') |
|
155 | - ->willReturn($path); |
|
156 | - $this->plugin->initialize($this->server); |
|
157 | - |
|
158 | - $this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path)); |
|
159 | - } |
|
160 | - |
|
161 | - public function testOnReport(): void { |
|
162 | - $path = 'test'; |
|
163 | - |
|
164 | - $parameters = [ |
|
165 | - [ |
|
166 | - 'name' => '{DAV:}prop', |
|
167 | - 'value' => [ |
|
168 | - ['name' => '{DAV:}getcontentlength', 'value' => ''], |
|
169 | - ['name' => '{http://owncloud.org/ns}size', 'value' => ''], |
|
170 | - ], |
|
171 | - ], |
|
172 | - [ |
|
173 | - 'name' => '{http://owncloud.org/ns}filter-rules', |
|
174 | - 'value' => [ |
|
175 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
176 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
177 | - ], |
|
178 | - ], |
|
179 | - ]; |
|
180 | - |
|
181 | - $this->groupManager->expects($this->any()) |
|
182 | - ->method('isAdmin') |
|
183 | - ->willReturn(true); |
|
184 | - |
|
185 | - $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
186 | - ->disableOriginalConstructor() |
|
187 | - ->getMock(); |
|
188 | - $reportTargetNode->expects($this->any()) |
|
189 | - ->method('getPath') |
|
190 | - ->willReturn(''); |
|
191 | - |
|
192 | - $response = $this->getMockBuilder(ResponseInterface::class) |
|
193 | - ->disableOriginalConstructor() |
|
194 | - ->getMock(); |
|
195 | - |
|
196 | - $response->expects($this->once()) |
|
197 | - ->method('setHeader') |
|
198 | - ->with('Content-Type', 'application/xml; charset=utf-8'); |
|
199 | - |
|
200 | - $response->expects($this->once()) |
|
201 | - ->method('setStatus') |
|
202 | - ->with(207); |
|
203 | - |
|
204 | - $response->expects($this->once()) |
|
205 | - ->method('setBody'); |
|
206 | - |
|
207 | - $this->tree->expects($this->any()) |
|
208 | - ->method('getNodeForPath') |
|
209 | - ->with('/' . $path) |
|
210 | - ->willReturn($reportTargetNode); |
|
211 | - |
|
212 | - $filesNode1 = $this->createMock(File::class); |
|
213 | - $filesNode1->expects($this->any()) |
|
214 | - ->method('getSize') |
|
215 | - ->willReturn(12); |
|
216 | - $filesNode2 = $this->createMock(Folder::class); |
|
217 | - $filesNode2->expects($this->any()) |
|
218 | - ->method('getSize') |
|
219 | - ->willReturn(10); |
|
220 | - |
|
221 | - $tag123 = $this->createMock(ISystemTag::class); |
|
222 | - $tag123->expects($this->any()) |
|
223 | - ->method('getName') |
|
224 | - ->willReturn('OneTwoThree'); |
|
225 | - $tag123->expects($this->any()) |
|
226 | - ->method('isUserVisible') |
|
227 | - ->willReturn(true); |
|
228 | - $tag456 = $this->createMock(ISystemTag::class); |
|
229 | - $tag456->expects($this->any()) |
|
230 | - ->method('getName') |
|
231 | - ->willReturn('FourFiveSix'); |
|
232 | - $tag456->expects($this->any()) |
|
233 | - ->method('isUserVisible') |
|
234 | - ->willReturn(true); |
|
235 | - |
|
236 | - $this->tagManager->expects($this->once()) |
|
237 | - ->method('getTagsByIds') |
|
238 | - ->with(['123', '456']) |
|
239 | - ->willReturn([$tag123, $tag456]); |
|
240 | - |
|
241 | - $this->userFolder->expects($this->exactly(2)) |
|
242 | - ->method('searchBySystemTag') |
|
243 | - ->withConsecutive( |
|
244 | - ['OneTwoThree'], |
|
245 | - ['FourFiveSix'], |
|
246 | - ) |
|
247 | - ->willReturnOnConsecutiveCalls( |
|
248 | - [$filesNode1], |
|
249 | - [$filesNode2], |
|
250 | - ); |
|
251 | - |
|
252 | - $this->server->expects($this->any()) |
|
253 | - ->method('getRequestUri') |
|
254 | - ->willReturn($path); |
|
255 | - $this->server->httpResponse = $response; |
|
256 | - $this->plugin->initialize($this->server); |
|
257 | - |
|
258 | - $this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path)); |
|
259 | - } |
|
260 | - |
|
261 | - public function testFindNodesByFileIdsRoot(): void { |
|
262 | - $filesNode1 = $this->getMockBuilder(Folder::class) |
|
263 | - ->disableOriginalConstructor() |
|
264 | - ->getMock(); |
|
265 | - $filesNode1->expects($this->once()) |
|
266 | - ->method('getName') |
|
267 | - ->willReturn('first node'); |
|
268 | - |
|
269 | - $filesNode2 = $this->getMockBuilder(File::class) |
|
270 | - ->disableOriginalConstructor() |
|
271 | - ->getMock(); |
|
272 | - $filesNode2->expects($this->once()) |
|
273 | - ->method('getName') |
|
274 | - ->willReturn('second node'); |
|
275 | - |
|
276 | - $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
277 | - ->disableOriginalConstructor() |
|
278 | - ->getMock(); |
|
279 | - $reportTargetNode->expects($this->any()) |
|
280 | - ->method('getPath') |
|
281 | - ->willReturn('/'); |
|
282 | - |
|
283 | - $this->userFolder->expects($this->exactly(2)) |
|
284 | - ->method('getFirstNodeById') |
|
285 | - ->withConsecutive( |
|
286 | - ['111'], |
|
287 | - ['222'], |
|
288 | - ) |
|
289 | - ->willReturnOnConsecutiveCalls( |
|
290 | - $filesNode1, |
|
291 | - $filesNode2, |
|
292 | - ); |
|
293 | - |
|
294 | - /** @var Directory&MockObject $reportTargetNode */ |
|
295 | - $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); |
|
296 | - |
|
297 | - $this->assertCount(2, $result); |
|
298 | - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); |
|
299 | - $this->assertEquals('first node', $result[0]->getName()); |
|
300 | - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); |
|
301 | - $this->assertEquals('second node', $result[1]->getName()); |
|
302 | - } |
|
303 | - |
|
304 | - public function testFindNodesByFileIdsSubDir(): void { |
|
305 | - $filesNode1 = $this->getMockBuilder(Folder::class) |
|
306 | - ->disableOriginalConstructor() |
|
307 | - ->getMock(); |
|
308 | - $filesNode1->expects($this->once()) |
|
309 | - ->method('getName') |
|
310 | - ->willReturn('first node'); |
|
311 | - |
|
312 | - $filesNode2 = $this->getMockBuilder(File::class) |
|
313 | - ->disableOriginalConstructor() |
|
314 | - ->getMock(); |
|
315 | - $filesNode2->expects($this->once()) |
|
316 | - ->method('getName') |
|
317 | - ->willReturn('second node'); |
|
318 | - |
|
319 | - $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
320 | - ->disableOriginalConstructor() |
|
321 | - ->getMock(); |
|
322 | - $reportTargetNode->expects($this->any()) |
|
323 | - ->method('getPath') |
|
324 | - ->willReturn('/sub1/sub2'); |
|
325 | - |
|
326 | - |
|
327 | - $subNode = $this->getMockBuilder(Folder::class) |
|
328 | - ->disableOriginalConstructor() |
|
329 | - ->getMock(); |
|
330 | - |
|
331 | - $this->userFolder->expects($this->once()) |
|
332 | - ->method('get') |
|
333 | - ->with('/sub1/sub2') |
|
334 | - ->willReturn($subNode); |
|
335 | - |
|
336 | - $subNode->expects($this->exactly(2)) |
|
337 | - ->method('getFirstNodeById') |
|
338 | - ->withConsecutive( |
|
339 | - ['111'], |
|
340 | - ['222'], |
|
341 | - ) |
|
342 | - ->willReturnOnConsecutiveCalls( |
|
343 | - $filesNode1, |
|
344 | - $filesNode2, |
|
345 | - ); |
|
346 | - |
|
347 | - /** @var Directory&MockObject $reportTargetNode */ |
|
348 | - $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); |
|
349 | - |
|
350 | - $this->assertCount(2, $result); |
|
351 | - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); |
|
352 | - $this->assertEquals('first node', $result[0]->getName()); |
|
353 | - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); |
|
354 | - $this->assertEquals('second node', $result[1]->getName()); |
|
355 | - } |
|
356 | - |
|
357 | - public function testPrepareResponses(): void { |
|
358 | - $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype']; |
|
359 | - |
|
360 | - $fileInfo = $this->createMock(FileInfo::class); |
|
361 | - $fileInfo->method('isReadable')->willReturn(true); |
|
362 | - |
|
363 | - $node1 = $this->getMockBuilder(Directory::class) |
|
364 | - ->disableOriginalConstructor() |
|
365 | - ->getMock(); |
|
366 | - $node2 = $this->getMockBuilder(\OCA\DAV\Connector\Sabre\File::class) |
|
367 | - ->disableOriginalConstructor() |
|
368 | - ->getMock(); |
|
369 | - |
|
370 | - $node1->expects($this->once()) |
|
371 | - ->method('getInternalFileId') |
|
372 | - ->willReturn('111'); |
|
373 | - $node1->expects($this->any()) |
|
374 | - ->method('getPath') |
|
375 | - ->willReturn('/node1'); |
|
376 | - $node1->method('getFileInfo')->willReturn($fileInfo); |
|
377 | - $node2->expects($this->once()) |
|
378 | - ->method('getInternalFileId') |
|
379 | - ->willReturn('222'); |
|
380 | - $node2->expects($this->once()) |
|
381 | - ->method('getSize') |
|
382 | - ->willReturn(1024); |
|
383 | - $node2->expects($this->any()) |
|
384 | - ->method('getPath') |
|
385 | - ->willReturn('/sub/node2'); |
|
386 | - $node2->method('getFileInfo')->willReturn($fileInfo); |
|
387 | - |
|
388 | - $config = $this->getMockBuilder(IConfig::class) |
|
389 | - ->disableOriginalConstructor() |
|
390 | - ->getMock(); |
|
391 | - |
|
392 | - $validator = $this->createMock(IFilenameValidator::class); |
|
393 | - $accountManager = $this->createMock(IAccountManager::class); |
|
394 | - |
|
395 | - $this->server->addPlugin( |
|
396 | - new FilesPlugin( |
|
397 | - $this->tree, |
|
398 | - $config, |
|
399 | - $this->createMock(IRequest::class), |
|
400 | - $this->previewManager, |
|
401 | - $this->createMock(IUserSession::class), |
|
402 | - $validator, |
|
403 | - $accountManager, |
|
404 | - ) |
|
405 | - ); |
|
406 | - $this->plugin->initialize($this->server); |
|
407 | - $responses = $this->plugin->prepareResponses('/files/username', $requestedProps, [$node1, $node2]); |
|
408 | - |
|
409 | - $this->assertCount(2, $responses); |
|
410 | - |
|
411 | - $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/node1', $responses[0]->getHref()); |
|
412 | - $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/sub/node2', $responses[1]->getHref()); |
|
413 | - |
|
414 | - $props1 = $responses[0]->getResponseProperties(); |
|
415 | - $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']); |
|
416 | - $this->assertNull($props1[404]['{DAV:}getcontentlength']); |
|
417 | - $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']); |
|
418 | - $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue(); |
|
419 | - $this->assertEquals('{DAV:}collection', $resourceType1[0]); |
|
420 | - |
|
421 | - $props2 = $responses[1]->getResponseProperties(); |
|
422 | - $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']); |
|
423 | - $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']); |
|
424 | - $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']); |
|
425 | - $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue()); |
|
426 | - } |
|
427 | - |
|
428 | - public function testProcessFilterRulesSingle(): void { |
|
429 | - $this->groupManager->expects($this->any()) |
|
430 | - ->method('isAdmin') |
|
431 | - ->willReturn(true); |
|
432 | - |
|
433 | - $rules = [ |
|
434 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
435 | - ]; |
|
436 | - |
|
437 | - $filesNode1 = $this->createMock(File::class); |
|
438 | - $filesNode1->expects($this->any()) |
|
439 | - ->method('getSize') |
|
440 | - ->willReturn(12); |
|
441 | - $filesNode2 = $this->createMock(Folder::class); |
|
442 | - $filesNode2->expects($this->any()) |
|
443 | - ->method('getSize') |
|
444 | - ->willReturn(10); |
|
445 | - |
|
446 | - $tag123 = $this->createMock(ISystemTag::class); |
|
447 | - $tag123->expects($this->any()) |
|
448 | - ->method('getName') |
|
449 | - ->willReturn('OneTwoThree'); |
|
450 | - $tag123->expects($this->any()) |
|
451 | - ->method('isUserVisible') |
|
452 | - ->willReturn(true); |
|
453 | - |
|
454 | - $this->tagManager->expects($this->once()) |
|
455 | - ->method('getTagsByIds') |
|
456 | - ->with(['123']) |
|
457 | - ->willReturn([$tag123]); |
|
458 | - |
|
459 | - $this->userFolder->expects($this->once()) |
|
460 | - ->method('searchBySystemTag') |
|
461 | - ->with('OneTwoThree') |
|
462 | - ->willReturn([$filesNode1, $filesNode2]); |
|
463 | - |
|
464 | - $this->assertEquals([$filesNode1, $filesNode2], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, 0, 0])); |
|
465 | - } |
|
466 | - |
|
467 | - public function testProcessFilterRulesAndCondition(): void { |
|
468 | - $this->groupManager->expects($this->any()) |
|
469 | - ->method('isAdmin') |
|
470 | - ->willReturn(true); |
|
471 | - |
|
472 | - $filesNode1 = $this->createMock(File::class); |
|
473 | - $filesNode1->expects($this->any()) |
|
474 | - ->method('getSize') |
|
475 | - ->willReturn(12); |
|
476 | - $filesNode1->expects($this->any()) |
|
477 | - ->method('getId') |
|
478 | - ->willReturn(111); |
|
479 | - $filesNode2 = $this->createMock(Folder::class); |
|
480 | - $filesNode2->expects($this->any()) |
|
481 | - ->method('getSize') |
|
482 | - ->willReturn(10); |
|
483 | - $filesNode2->expects($this->any()) |
|
484 | - ->method('getId') |
|
485 | - ->willReturn(222); |
|
486 | - $filesNode3 = $this->createMock(File::class); |
|
487 | - $filesNode3->expects($this->any()) |
|
488 | - ->method('getSize') |
|
489 | - ->willReturn(14); |
|
490 | - $filesNode3->expects($this->any()) |
|
491 | - ->method('getId') |
|
492 | - ->willReturn(333); |
|
493 | - |
|
494 | - $tag123 = $this->createMock(ISystemTag::class); |
|
495 | - $tag123->expects($this->any()) |
|
496 | - ->method('getName') |
|
497 | - ->willReturn('OneTwoThree'); |
|
498 | - $tag123->expects($this->any()) |
|
499 | - ->method('isUserVisible') |
|
500 | - ->willReturn(true); |
|
501 | - $tag456 = $this->createMock(ISystemTag::class); |
|
502 | - $tag456->expects($this->any()) |
|
503 | - ->method('getName') |
|
504 | - ->willReturn('FourFiveSix'); |
|
505 | - $tag456->expects($this->any()) |
|
506 | - ->method('isUserVisible') |
|
507 | - ->willReturn(true); |
|
508 | - |
|
509 | - $this->tagManager->expects($this->once()) |
|
510 | - ->method('getTagsByIds') |
|
511 | - ->with(['123', '456']) |
|
512 | - ->willReturn([$tag123, $tag456]); |
|
513 | - |
|
514 | - $this->userFolder->expects($this->exactly(2)) |
|
515 | - ->method('searchBySystemTag') |
|
516 | - ->withConsecutive( |
|
517 | - ['OneTwoThree'], |
|
518 | - ['FourFiveSix'], |
|
519 | - ) |
|
520 | - ->willReturnOnConsecutiveCalls( |
|
521 | - [$filesNode1, $filesNode2], |
|
522 | - [$filesNode2, $filesNode3], |
|
523 | - ); |
|
524 | - |
|
525 | - $rules = [ |
|
526 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
527 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
528 | - ]; |
|
529 | - |
|
530 | - $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
531 | - } |
|
532 | - |
|
533 | - public function testProcessFilterRulesAndConditionWithOneEmptyResult(): void { |
|
534 | - $this->groupManager->expects($this->any()) |
|
535 | - ->method('isAdmin') |
|
536 | - ->willReturn(true); |
|
537 | - |
|
538 | - $filesNode1 = $this->createMock(File::class); |
|
539 | - $filesNode1->expects($this->any()) |
|
540 | - ->method('getSize') |
|
541 | - ->willReturn(12); |
|
542 | - $filesNode1->expects($this->any()) |
|
543 | - ->method('getId') |
|
544 | - ->willReturn(111); |
|
545 | - $filesNode2 = $this->createMock(Folder::class); |
|
546 | - $filesNode2->expects($this->any()) |
|
547 | - ->method('getSize') |
|
548 | - ->willReturn(10); |
|
549 | - $filesNode2->expects($this->any()) |
|
550 | - ->method('getId') |
|
551 | - ->willReturn(222); |
|
552 | - |
|
553 | - $tag123 = $this->createMock(ISystemTag::class); |
|
554 | - $tag123->expects($this->any()) |
|
555 | - ->method('getName') |
|
556 | - ->willReturn('OneTwoThree'); |
|
557 | - $tag123->expects($this->any()) |
|
558 | - ->method('isUserVisible') |
|
559 | - ->willReturn(true); |
|
560 | - $tag456 = $this->createMock(ISystemTag::class); |
|
561 | - $tag456->expects($this->any()) |
|
562 | - ->method('getName') |
|
563 | - ->willReturn('FourFiveSix'); |
|
564 | - $tag456->expects($this->any()) |
|
565 | - ->method('isUserVisible') |
|
566 | - ->willReturn(true); |
|
567 | - |
|
568 | - $this->tagManager->expects($this->once()) |
|
569 | - ->method('getTagsByIds') |
|
570 | - ->with(['123', '456']) |
|
571 | - ->willReturn([$tag123, $tag456]); |
|
572 | - |
|
573 | - $this->userFolder->expects($this->exactly(2)) |
|
574 | - ->method('searchBySystemTag') |
|
575 | - ->withConsecutive( |
|
576 | - ['OneTwoThree'], |
|
577 | - ['FourFiveSix'], |
|
578 | - ) |
|
579 | - ->willReturnOnConsecutiveCalls( |
|
580 | - [$filesNode1, $filesNode2], |
|
581 | - [], |
|
582 | - ); |
|
583 | - |
|
584 | - $rules = [ |
|
585 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
586 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
587 | - ]; |
|
588 | - |
|
589 | - $this->assertEquals([], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); |
|
590 | - } |
|
591 | - |
|
592 | - public function testProcessFilterRulesAndConditionWithFirstEmptyResult(): void { |
|
593 | - $this->groupManager->expects($this->any()) |
|
594 | - ->method('isAdmin') |
|
595 | - ->willReturn(true); |
|
596 | - |
|
597 | - $filesNode1 = $this->createMock(File::class); |
|
598 | - $filesNode1->expects($this->any()) |
|
599 | - ->method('getSize') |
|
600 | - ->willReturn(12); |
|
601 | - $filesNode1->expects($this->any()) |
|
602 | - ->method('getId') |
|
603 | - ->willReturn(111); |
|
604 | - $filesNode2 = $this->createMock(Folder::class); |
|
605 | - $filesNode2->expects($this->any()) |
|
606 | - ->method('getSize') |
|
607 | - ->willReturn(10); |
|
608 | - $filesNode2->expects($this->any()) |
|
609 | - ->method('getId') |
|
610 | - ->willReturn(222); |
|
611 | - |
|
612 | - $tag123 = $this->createMock(ISystemTag::class); |
|
613 | - $tag123->expects($this->any()) |
|
614 | - ->method('getName') |
|
615 | - ->willReturn('OneTwoThree'); |
|
616 | - $tag123->expects($this->any()) |
|
617 | - ->method('isUserVisible') |
|
618 | - ->willReturn(true); |
|
619 | - $tag456 = $this->createMock(ISystemTag::class); |
|
620 | - $tag456->expects($this->any()) |
|
621 | - ->method('getName') |
|
622 | - ->willReturn('FourFiveSix'); |
|
623 | - $tag456->expects($this->any()) |
|
624 | - ->method('isUserVisible') |
|
625 | - ->willReturn(true); |
|
626 | - |
|
627 | - $this->tagManager->expects($this->once()) |
|
628 | - ->method('getTagsByIds') |
|
629 | - ->with(['123', '456']) |
|
630 | - ->willReturn([$tag123, $tag456]); |
|
631 | - |
|
632 | - $this->userFolder->expects($this->once()) |
|
633 | - ->method('searchBySystemTag') |
|
634 | - ->with('OneTwoThree') |
|
635 | - ->willReturnOnConsecutiveCalls( |
|
636 | - [], |
|
637 | - [$filesNode1, $filesNode2], |
|
638 | - ); |
|
639 | - |
|
640 | - $rules = [ |
|
641 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
642 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
643 | - ]; |
|
644 | - |
|
645 | - $this->assertEquals([], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); |
|
646 | - } |
|
647 | - |
|
648 | - public function testProcessFilterRulesAndConditionWithEmptyMidResult(): void { |
|
649 | - $this->groupManager->expects($this->any()) |
|
650 | - ->method('isAdmin') |
|
651 | - ->willReturn(true); |
|
652 | - |
|
653 | - $filesNode1 = $this->createMock(File::class); |
|
654 | - $filesNode1->expects($this->any()) |
|
655 | - ->method('getSize') |
|
656 | - ->willReturn(12); |
|
657 | - $filesNode1->expects($this->any()) |
|
658 | - ->method('getId') |
|
659 | - ->willReturn(111); |
|
660 | - $filesNode2 = $this->createMock(Folder::class); |
|
661 | - $filesNode2->expects($this->any()) |
|
662 | - ->method('getSize') |
|
663 | - ->willReturn(10); |
|
664 | - $filesNode2->expects($this->any()) |
|
665 | - ->method('getId') |
|
666 | - ->willReturn(222); |
|
667 | - $filesNode3 = $this->createMock(Folder::class); |
|
668 | - $filesNode3->expects($this->any()) |
|
669 | - ->method('getSize') |
|
670 | - ->willReturn(13); |
|
671 | - $filesNode3->expects($this->any()) |
|
672 | - ->method('getId') |
|
673 | - ->willReturn(333); |
|
674 | - |
|
675 | - $tag123 = $this->createMock(ISystemTag::class); |
|
676 | - $tag123->expects($this->any()) |
|
677 | - ->method('getName') |
|
678 | - ->willReturn('OneTwoThree'); |
|
679 | - $tag123->expects($this->any()) |
|
680 | - ->method('isUserVisible') |
|
681 | - ->willReturn(true); |
|
682 | - $tag456 = $this->createMock(ISystemTag::class); |
|
683 | - $tag456->expects($this->any()) |
|
684 | - ->method('getName') |
|
685 | - ->willReturn('FourFiveSix'); |
|
686 | - $tag456->expects($this->any()) |
|
687 | - ->method('isUserVisible') |
|
688 | - ->willReturn(true); |
|
689 | - $tag789 = $this->createMock(ISystemTag::class); |
|
690 | - $tag789->expects($this->any()) |
|
691 | - ->method('getName') |
|
692 | - ->willReturn('SevenEightNein'); |
|
693 | - $tag789->expects($this->any()) |
|
694 | - ->method('isUserVisible') |
|
695 | - ->willReturn(true); |
|
696 | - |
|
697 | - $this->tagManager->expects($this->once()) |
|
698 | - ->method('getTagsByIds') |
|
699 | - ->with(['123', '456', '789']) |
|
700 | - ->willReturn([$tag123, $tag456, $tag789]); |
|
701 | - |
|
702 | - $this->userFolder->expects($this->exactly(2)) |
|
703 | - ->method('searchBySystemTag') |
|
704 | - ->withConsecutive(['OneTwoThree'], ['FourFiveSix'], ['SevenEightNein']) |
|
705 | - ->willReturnOnConsecutiveCalls( |
|
706 | - [$filesNode1, $filesNode2], |
|
707 | - [$filesNode3], |
|
708 | - [$filesNode1, $filesNode2], |
|
709 | - ); |
|
710 | - |
|
711 | - $rules = [ |
|
712 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
713 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
714 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'], |
|
715 | - ]; |
|
716 | - |
|
717 | - $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
718 | - } |
|
719 | - |
|
720 | - public function testProcessFilterRulesInvisibleTagAsAdmin(): void { |
|
721 | - $this->groupManager->expects($this->any()) |
|
722 | - ->method('isAdmin') |
|
723 | - ->willReturn(true); |
|
724 | - |
|
725 | - $filesNode1 = $this->createMock(File::class); |
|
726 | - $filesNode1->expects($this->any()) |
|
727 | - ->method('getSize') |
|
728 | - ->willReturn(12); |
|
729 | - $filesNode1->expects($this->any()) |
|
730 | - ->method('getId') |
|
731 | - ->willReturn(111); |
|
732 | - $filesNode2 = $this->createMock(Folder::class); |
|
733 | - $filesNode2->expects($this->any()) |
|
734 | - ->method('getSize') |
|
735 | - ->willReturn(10); |
|
736 | - $filesNode2->expects($this->any()) |
|
737 | - ->method('getId') |
|
738 | - ->willReturn(222); |
|
739 | - $filesNode3 = $this->createMock(Folder::class); |
|
740 | - $filesNode3->expects($this->any()) |
|
741 | - ->method('getSize') |
|
742 | - ->willReturn(13); |
|
743 | - $filesNode3->expects($this->any()) |
|
744 | - ->method('getId') |
|
745 | - ->willReturn(333); |
|
746 | - |
|
747 | - $tag123 = $this->createMock(ISystemTag::class); |
|
748 | - $tag123->expects($this->any()) |
|
749 | - ->method('getName') |
|
750 | - ->willReturn('OneTwoThree'); |
|
751 | - $tag123->expects($this->any()) |
|
752 | - ->method('isUserVisible') |
|
753 | - ->willReturn(true); |
|
754 | - $tag456 = $this->createMock(ISystemTag::class); |
|
755 | - $tag456->expects($this->any()) |
|
756 | - ->method('getName') |
|
757 | - ->willReturn('FourFiveSix'); |
|
758 | - $tag456->expects($this->any()) |
|
759 | - ->method('isUserVisible') |
|
760 | - ->willReturn(false); |
|
761 | - |
|
762 | - $this->tagManager->expects($this->once()) |
|
763 | - ->method('getTagsByIds') |
|
764 | - ->with(['123', '456']) |
|
765 | - ->willReturn([$tag123, $tag456]); |
|
766 | - |
|
767 | - $this->userFolder->expects($this->exactly(2)) |
|
768 | - ->method('searchBySystemTag') |
|
769 | - ->withConsecutive(['OneTwoThree'], ['FourFiveSix']) |
|
770 | - ->willReturnOnConsecutiveCalls( |
|
771 | - [$filesNode1, $filesNode2], |
|
772 | - [$filesNode2, $filesNode3], |
|
773 | - ); |
|
774 | - |
|
775 | - $rules = [ |
|
776 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
777 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
778 | - ]; |
|
779 | - |
|
780 | - $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
781 | - } |
|
782 | - |
|
783 | - |
|
784 | - public function testProcessFilterRulesInvisibleTagAsUser(): void { |
|
785 | - $this->expectException(TagNotFoundException::class); |
|
786 | - |
|
787 | - $this->groupManager->expects($this->any()) |
|
788 | - ->method('isAdmin') |
|
789 | - ->willReturn(false); |
|
790 | - |
|
791 | - $tag123 = $this->createMock(ISystemTag::class); |
|
792 | - $tag123->expects($this->any()) |
|
793 | - ->method('getName') |
|
794 | - ->willReturn('OneTwoThree'); |
|
795 | - $tag123->expects($this->any()) |
|
796 | - ->method('isUserVisible') |
|
797 | - ->willReturn(true); |
|
798 | - $tag456 = $this->createMock(ISystemTag::class); |
|
799 | - $tag456->expects($this->any()) |
|
800 | - ->method('getName') |
|
801 | - ->willReturn('FourFiveSix'); |
|
802 | - $tag456->expects($this->any()) |
|
803 | - ->method('isUserVisible') |
|
804 | - ->willReturn(false); |
|
805 | - |
|
806 | - $this->tagManager->expects($this->once()) |
|
807 | - ->method('getTagsByIds') |
|
808 | - ->with(['123', '456']) |
|
809 | - ->willThrowException(new TagNotFoundException()); |
|
810 | - |
|
811 | - $this->userFolder->expects($this->never()) |
|
812 | - ->method('searchBySystemTag'); |
|
813 | - |
|
814 | - $rules = [ |
|
815 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
816 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
817 | - ]; |
|
818 | - |
|
819 | - $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]); |
|
820 | - } |
|
821 | - |
|
822 | - public function testProcessFilterRulesVisibleTagAsUser(): void { |
|
823 | - $this->groupManager->expects($this->any()) |
|
824 | - ->method('isAdmin') |
|
825 | - ->willReturn(false); |
|
826 | - |
|
827 | - $tag1 = $this->createMock(ISystemTag::class); |
|
828 | - $tag1->expects($this->any()) |
|
829 | - ->method('getId') |
|
830 | - ->willReturn('123'); |
|
831 | - $tag1->expects($this->any()) |
|
832 | - ->method('isUserVisible') |
|
833 | - ->willReturn(true); |
|
834 | - $tag1->expects($this->any()) |
|
835 | - ->method('getName') |
|
836 | - ->willReturn('OneTwoThree'); |
|
837 | - |
|
838 | - $tag2 = $this->createMock(ISystemTag::class); |
|
839 | - $tag2->expects($this->any()) |
|
840 | - ->method('getId') |
|
841 | - ->willReturn('123'); |
|
842 | - $tag2->expects($this->any()) |
|
843 | - ->method('isUserVisible') |
|
844 | - ->willReturn(true); |
|
845 | - $tag2->expects($this->any()) |
|
846 | - ->method('getName') |
|
847 | - ->willReturn('FourFiveSix'); |
|
848 | - |
|
849 | - $this->tagManager->expects($this->once()) |
|
850 | - ->method('getTagsByIds') |
|
851 | - ->with(['123', '456']) |
|
852 | - ->willReturn([$tag1, $tag2]); |
|
853 | - |
|
854 | - $filesNode1 = $this->createMock(File::class); |
|
855 | - $filesNode1->expects($this->any()) |
|
856 | - ->method('getId') |
|
857 | - ->willReturn(111); |
|
858 | - $filesNode1->expects($this->any()) |
|
859 | - ->method('getSize') |
|
860 | - ->willReturn(12); |
|
861 | - $filesNode2 = $this->createMock(Folder::class); |
|
862 | - $filesNode2->expects($this->any()) |
|
863 | - ->method('getId') |
|
864 | - ->willReturn(222); |
|
865 | - $filesNode2->expects($this->any()) |
|
866 | - ->method('getSize') |
|
867 | - ->willReturn(10); |
|
868 | - $filesNode3 = $this->createMock(Folder::class); |
|
869 | - $filesNode3->expects($this->any()) |
|
870 | - ->method('getId') |
|
871 | - ->willReturn(333); |
|
872 | - $filesNode3->expects($this->any()) |
|
873 | - ->method('getSize') |
|
874 | - ->willReturn(33); |
|
875 | - |
|
876 | - $this->tagManager->expects($this->once()) |
|
877 | - ->method('getTagsByIds') |
|
878 | - ->with(['123', '456']) |
|
879 | - ->willReturn([$tag1, $tag2]); |
|
880 | - |
|
881 | - // main assertion: only user visible tags are being passed through. |
|
882 | - $this->userFolder->expects($this->exactly(2)) |
|
883 | - ->method('searchBySystemTag') |
|
884 | - ->withConsecutive(['OneTwoThree'], ['FourFiveSix']) |
|
885 | - ->willReturnOnConsecutiveCalls( |
|
886 | - [$filesNode1, $filesNode2], |
|
887 | - [$filesNode2, $filesNode3], |
|
888 | - ); |
|
889 | - |
|
890 | - $rules = [ |
|
891 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
892 | - ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
893 | - ]; |
|
894 | - |
|
895 | - $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
896 | - } |
|
897 | - |
|
898 | - public function testProcessFavoriteFilter(): void { |
|
899 | - $rules = [ |
|
900 | - ['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'], |
|
901 | - ]; |
|
902 | - |
|
903 | - $this->privateTags->expects($this->once()) |
|
904 | - ->method('getFavorites') |
|
905 | - ->willReturn(['456', '789']); |
|
906 | - |
|
907 | - $this->assertEquals(['456', '789'], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileIDs', [$rules]))); |
|
908 | - } |
|
909 | - |
|
910 | - public function filesBaseUriProvider() { |
|
911 | - return [ |
|
912 | - ['', '', ''], |
|
913 | - ['files/username', '', '/files/username'], |
|
914 | - ['files/username/test', '/test', '/files/username'], |
|
915 | - ['files/username/test/sub', '/test/sub', '/files/username'], |
|
916 | - ['test', '/test', ''], |
|
917 | - ]; |
|
918 | - } |
|
919 | - |
|
920 | - /** |
|
921 | - * @dataProvider filesBaseUriProvider |
|
922 | - */ |
|
923 | - public function testFilesBaseUri($uri, $reportPath, $expectedUri): void { |
|
924 | - $this->assertEquals($expectedUri, $this->invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath])); |
|
925 | - } |
|
39 | + private \Sabre\DAV\Server&MockObject $server; |
|
40 | + private Tree&MockObject $tree; |
|
41 | + private ISystemTagObjectMapper&MockObject $tagMapper; |
|
42 | + private ISystemTagManager&MockObject $tagManager; |
|
43 | + private ITags&MockObject $privateTags; |
|
44 | + private ITagManager&MockObject $privateTagManager; |
|
45 | + private IUserSession&MockObject $userSession; |
|
46 | + private FilesReportPluginImplementation $plugin; |
|
47 | + private View&MockObject $view; |
|
48 | + private IGroupManager&MockObject $groupManager; |
|
49 | + private Folder&MockObject $userFolder; |
|
50 | + private IPreview&MockObject $previewManager; |
|
51 | + private IAppManager&MockObject $appManager; |
|
52 | + |
|
53 | + protected function setUp(): void { |
|
54 | + parent::setUp(); |
|
55 | + $this->tree = $this->getMockBuilder(Tree::class) |
|
56 | + ->disableOriginalConstructor() |
|
57 | + ->getMock(); |
|
58 | + |
|
59 | + $this->view = $this->getMockBuilder(View::class) |
|
60 | + ->disableOriginalConstructor() |
|
61 | + ->getMock(); |
|
62 | + |
|
63 | + $this->server = $this->getMockBuilder('\Sabre\DAV\Server') |
|
64 | + ->setConstructorArgs([$this->tree]) |
|
65 | + ->onlyMethods(['getRequestUri', 'getBaseUri']) |
|
66 | + ->getMock(); |
|
67 | + |
|
68 | + $this->server->expects($this->any()) |
|
69 | + ->method('getBaseUri') |
|
70 | + ->willReturn('http://example.com/owncloud/remote.php/dav'); |
|
71 | + |
|
72 | + $this->groupManager = $this->getMockBuilder(IGroupManager::class) |
|
73 | + ->disableOriginalConstructor() |
|
74 | + ->getMock(); |
|
75 | + |
|
76 | + $this->userFolder = $this->getMockBuilder(Folder::class) |
|
77 | + ->disableOriginalConstructor() |
|
78 | + ->getMock(); |
|
79 | + |
|
80 | + $this->previewManager = $this->getMockBuilder(IPreview::class) |
|
81 | + ->disableOriginalConstructor() |
|
82 | + ->getMock(); |
|
83 | + |
|
84 | + $this->appManager = $this->getMockBuilder(IAppManager::class) |
|
85 | + ->disableOriginalConstructor() |
|
86 | + ->getMock(); |
|
87 | + |
|
88 | + $this->tagManager = $this->createMock(ISystemTagManager::class); |
|
89 | + $this->tagMapper = $this->createMock(ISystemTagObjectMapper::class); |
|
90 | + $this->userSession = $this->createMock(IUserSession::class); |
|
91 | + $this->privateTags = $this->createMock(ITags::class); |
|
92 | + $this->privateTagManager = $this->createMock(ITagManager::class); |
|
93 | + $this->privateTagManager->expects($this->any()) |
|
94 | + ->method('load') |
|
95 | + ->with('files') |
|
96 | + ->willReturn($this->privateTags); |
|
97 | + |
|
98 | + $user = $this->getMockBuilder(IUser::class) |
|
99 | + ->disableOriginalConstructor() |
|
100 | + ->getMock(); |
|
101 | + $user->expects($this->any()) |
|
102 | + ->method('getUID') |
|
103 | + ->willReturn('testuser'); |
|
104 | + $this->userSession->expects($this->any()) |
|
105 | + ->method('getUser') |
|
106 | + ->willReturn($user); |
|
107 | + |
|
108 | + $this->plugin = new FilesReportPluginImplementation( |
|
109 | + $this->tree, |
|
110 | + $this->view, |
|
111 | + $this->tagManager, |
|
112 | + $this->tagMapper, |
|
113 | + $this->privateTagManager, |
|
114 | + $this->userSession, |
|
115 | + $this->groupManager, |
|
116 | + $this->userFolder, |
|
117 | + $this->appManager |
|
118 | + ); |
|
119 | + } |
|
120 | + |
|
121 | + public function testOnReportInvalidNode(): void { |
|
122 | + $path = 'totally/unrelated/13'; |
|
123 | + |
|
124 | + $this->tree->expects($this->any()) |
|
125 | + ->method('getNodeForPath') |
|
126 | + ->with('/' . $path) |
|
127 | + ->willReturn( |
|
128 | + $this->getMockBuilder(INode::class) |
|
129 | + ->disableOriginalConstructor() |
|
130 | + ->getMock() |
|
131 | + ); |
|
132 | + |
|
133 | + $this->server->expects($this->any()) |
|
134 | + ->method('getRequestUri') |
|
135 | + ->willReturn($path); |
|
136 | + $this->plugin->initialize($this->server); |
|
137 | + |
|
138 | + $this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path)); |
|
139 | + } |
|
140 | + |
|
141 | + public function testOnReportInvalidReportName(): void { |
|
142 | + $path = 'test'; |
|
143 | + |
|
144 | + $this->tree->expects($this->any()) |
|
145 | + ->method('getNodeForPath') |
|
146 | + ->with('/' . $path) |
|
147 | + ->willReturn( |
|
148 | + $this->getMockBuilder(INode::class) |
|
149 | + ->disableOriginalConstructor() |
|
150 | + ->getMock() |
|
151 | + ); |
|
152 | + |
|
153 | + $this->server->expects($this->any()) |
|
154 | + ->method('getRequestUri') |
|
155 | + ->willReturn($path); |
|
156 | + $this->plugin->initialize($this->server); |
|
157 | + |
|
158 | + $this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path)); |
|
159 | + } |
|
160 | + |
|
161 | + public function testOnReport(): void { |
|
162 | + $path = 'test'; |
|
163 | + |
|
164 | + $parameters = [ |
|
165 | + [ |
|
166 | + 'name' => '{DAV:}prop', |
|
167 | + 'value' => [ |
|
168 | + ['name' => '{DAV:}getcontentlength', 'value' => ''], |
|
169 | + ['name' => '{http://owncloud.org/ns}size', 'value' => ''], |
|
170 | + ], |
|
171 | + ], |
|
172 | + [ |
|
173 | + 'name' => '{http://owncloud.org/ns}filter-rules', |
|
174 | + 'value' => [ |
|
175 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
176 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
177 | + ], |
|
178 | + ], |
|
179 | + ]; |
|
180 | + |
|
181 | + $this->groupManager->expects($this->any()) |
|
182 | + ->method('isAdmin') |
|
183 | + ->willReturn(true); |
|
184 | + |
|
185 | + $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
186 | + ->disableOriginalConstructor() |
|
187 | + ->getMock(); |
|
188 | + $reportTargetNode->expects($this->any()) |
|
189 | + ->method('getPath') |
|
190 | + ->willReturn(''); |
|
191 | + |
|
192 | + $response = $this->getMockBuilder(ResponseInterface::class) |
|
193 | + ->disableOriginalConstructor() |
|
194 | + ->getMock(); |
|
195 | + |
|
196 | + $response->expects($this->once()) |
|
197 | + ->method('setHeader') |
|
198 | + ->with('Content-Type', 'application/xml; charset=utf-8'); |
|
199 | + |
|
200 | + $response->expects($this->once()) |
|
201 | + ->method('setStatus') |
|
202 | + ->with(207); |
|
203 | + |
|
204 | + $response->expects($this->once()) |
|
205 | + ->method('setBody'); |
|
206 | + |
|
207 | + $this->tree->expects($this->any()) |
|
208 | + ->method('getNodeForPath') |
|
209 | + ->with('/' . $path) |
|
210 | + ->willReturn($reportTargetNode); |
|
211 | + |
|
212 | + $filesNode1 = $this->createMock(File::class); |
|
213 | + $filesNode1->expects($this->any()) |
|
214 | + ->method('getSize') |
|
215 | + ->willReturn(12); |
|
216 | + $filesNode2 = $this->createMock(Folder::class); |
|
217 | + $filesNode2->expects($this->any()) |
|
218 | + ->method('getSize') |
|
219 | + ->willReturn(10); |
|
220 | + |
|
221 | + $tag123 = $this->createMock(ISystemTag::class); |
|
222 | + $tag123->expects($this->any()) |
|
223 | + ->method('getName') |
|
224 | + ->willReturn('OneTwoThree'); |
|
225 | + $tag123->expects($this->any()) |
|
226 | + ->method('isUserVisible') |
|
227 | + ->willReturn(true); |
|
228 | + $tag456 = $this->createMock(ISystemTag::class); |
|
229 | + $tag456->expects($this->any()) |
|
230 | + ->method('getName') |
|
231 | + ->willReturn('FourFiveSix'); |
|
232 | + $tag456->expects($this->any()) |
|
233 | + ->method('isUserVisible') |
|
234 | + ->willReturn(true); |
|
235 | + |
|
236 | + $this->tagManager->expects($this->once()) |
|
237 | + ->method('getTagsByIds') |
|
238 | + ->with(['123', '456']) |
|
239 | + ->willReturn([$tag123, $tag456]); |
|
240 | + |
|
241 | + $this->userFolder->expects($this->exactly(2)) |
|
242 | + ->method('searchBySystemTag') |
|
243 | + ->withConsecutive( |
|
244 | + ['OneTwoThree'], |
|
245 | + ['FourFiveSix'], |
|
246 | + ) |
|
247 | + ->willReturnOnConsecutiveCalls( |
|
248 | + [$filesNode1], |
|
249 | + [$filesNode2], |
|
250 | + ); |
|
251 | + |
|
252 | + $this->server->expects($this->any()) |
|
253 | + ->method('getRequestUri') |
|
254 | + ->willReturn($path); |
|
255 | + $this->server->httpResponse = $response; |
|
256 | + $this->plugin->initialize($this->server); |
|
257 | + |
|
258 | + $this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path)); |
|
259 | + } |
|
260 | + |
|
261 | + public function testFindNodesByFileIdsRoot(): void { |
|
262 | + $filesNode1 = $this->getMockBuilder(Folder::class) |
|
263 | + ->disableOriginalConstructor() |
|
264 | + ->getMock(); |
|
265 | + $filesNode1->expects($this->once()) |
|
266 | + ->method('getName') |
|
267 | + ->willReturn('first node'); |
|
268 | + |
|
269 | + $filesNode2 = $this->getMockBuilder(File::class) |
|
270 | + ->disableOriginalConstructor() |
|
271 | + ->getMock(); |
|
272 | + $filesNode2->expects($this->once()) |
|
273 | + ->method('getName') |
|
274 | + ->willReturn('second node'); |
|
275 | + |
|
276 | + $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
277 | + ->disableOriginalConstructor() |
|
278 | + ->getMock(); |
|
279 | + $reportTargetNode->expects($this->any()) |
|
280 | + ->method('getPath') |
|
281 | + ->willReturn('/'); |
|
282 | + |
|
283 | + $this->userFolder->expects($this->exactly(2)) |
|
284 | + ->method('getFirstNodeById') |
|
285 | + ->withConsecutive( |
|
286 | + ['111'], |
|
287 | + ['222'], |
|
288 | + ) |
|
289 | + ->willReturnOnConsecutiveCalls( |
|
290 | + $filesNode1, |
|
291 | + $filesNode2, |
|
292 | + ); |
|
293 | + |
|
294 | + /** @var Directory&MockObject $reportTargetNode */ |
|
295 | + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); |
|
296 | + |
|
297 | + $this->assertCount(2, $result); |
|
298 | + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); |
|
299 | + $this->assertEquals('first node', $result[0]->getName()); |
|
300 | + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); |
|
301 | + $this->assertEquals('second node', $result[1]->getName()); |
|
302 | + } |
|
303 | + |
|
304 | + public function testFindNodesByFileIdsSubDir(): void { |
|
305 | + $filesNode1 = $this->getMockBuilder(Folder::class) |
|
306 | + ->disableOriginalConstructor() |
|
307 | + ->getMock(); |
|
308 | + $filesNode1->expects($this->once()) |
|
309 | + ->method('getName') |
|
310 | + ->willReturn('first node'); |
|
311 | + |
|
312 | + $filesNode2 = $this->getMockBuilder(File::class) |
|
313 | + ->disableOriginalConstructor() |
|
314 | + ->getMock(); |
|
315 | + $filesNode2->expects($this->once()) |
|
316 | + ->method('getName') |
|
317 | + ->willReturn('second node'); |
|
318 | + |
|
319 | + $reportTargetNode = $this->getMockBuilder(Directory::class) |
|
320 | + ->disableOriginalConstructor() |
|
321 | + ->getMock(); |
|
322 | + $reportTargetNode->expects($this->any()) |
|
323 | + ->method('getPath') |
|
324 | + ->willReturn('/sub1/sub2'); |
|
325 | + |
|
326 | + |
|
327 | + $subNode = $this->getMockBuilder(Folder::class) |
|
328 | + ->disableOriginalConstructor() |
|
329 | + ->getMock(); |
|
330 | + |
|
331 | + $this->userFolder->expects($this->once()) |
|
332 | + ->method('get') |
|
333 | + ->with('/sub1/sub2') |
|
334 | + ->willReturn($subNode); |
|
335 | + |
|
336 | + $subNode->expects($this->exactly(2)) |
|
337 | + ->method('getFirstNodeById') |
|
338 | + ->withConsecutive( |
|
339 | + ['111'], |
|
340 | + ['222'], |
|
341 | + ) |
|
342 | + ->willReturnOnConsecutiveCalls( |
|
343 | + $filesNode1, |
|
344 | + $filesNode2, |
|
345 | + ); |
|
346 | + |
|
347 | + /** @var Directory&MockObject $reportTargetNode */ |
|
348 | + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); |
|
349 | + |
|
350 | + $this->assertCount(2, $result); |
|
351 | + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); |
|
352 | + $this->assertEquals('first node', $result[0]->getName()); |
|
353 | + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); |
|
354 | + $this->assertEquals('second node', $result[1]->getName()); |
|
355 | + } |
|
356 | + |
|
357 | + public function testPrepareResponses(): void { |
|
358 | + $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype']; |
|
359 | + |
|
360 | + $fileInfo = $this->createMock(FileInfo::class); |
|
361 | + $fileInfo->method('isReadable')->willReturn(true); |
|
362 | + |
|
363 | + $node1 = $this->getMockBuilder(Directory::class) |
|
364 | + ->disableOriginalConstructor() |
|
365 | + ->getMock(); |
|
366 | + $node2 = $this->getMockBuilder(\OCA\DAV\Connector\Sabre\File::class) |
|
367 | + ->disableOriginalConstructor() |
|
368 | + ->getMock(); |
|
369 | + |
|
370 | + $node1->expects($this->once()) |
|
371 | + ->method('getInternalFileId') |
|
372 | + ->willReturn('111'); |
|
373 | + $node1->expects($this->any()) |
|
374 | + ->method('getPath') |
|
375 | + ->willReturn('/node1'); |
|
376 | + $node1->method('getFileInfo')->willReturn($fileInfo); |
|
377 | + $node2->expects($this->once()) |
|
378 | + ->method('getInternalFileId') |
|
379 | + ->willReturn('222'); |
|
380 | + $node2->expects($this->once()) |
|
381 | + ->method('getSize') |
|
382 | + ->willReturn(1024); |
|
383 | + $node2->expects($this->any()) |
|
384 | + ->method('getPath') |
|
385 | + ->willReturn('/sub/node2'); |
|
386 | + $node2->method('getFileInfo')->willReturn($fileInfo); |
|
387 | + |
|
388 | + $config = $this->getMockBuilder(IConfig::class) |
|
389 | + ->disableOriginalConstructor() |
|
390 | + ->getMock(); |
|
391 | + |
|
392 | + $validator = $this->createMock(IFilenameValidator::class); |
|
393 | + $accountManager = $this->createMock(IAccountManager::class); |
|
394 | + |
|
395 | + $this->server->addPlugin( |
|
396 | + new FilesPlugin( |
|
397 | + $this->tree, |
|
398 | + $config, |
|
399 | + $this->createMock(IRequest::class), |
|
400 | + $this->previewManager, |
|
401 | + $this->createMock(IUserSession::class), |
|
402 | + $validator, |
|
403 | + $accountManager, |
|
404 | + ) |
|
405 | + ); |
|
406 | + $this->plugin->initialize($this->server); |
|
407 | + $responses = $this->plugin->prepareResponses('/files/username', $requestedProps, [$node1, $node2]); |
|
408 | + |
|
409 | + $this->assertCount(2, $responses); |
|
410 | + |
|
411 | + $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/node1', $responses[0]->getHref()); |
|
412 | + $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/sub/node2', $responses[1]->getHref()); |
|
413 | + |
|
414 | + $props1 = $responses[0]->getResponseProperties(); |
|
415 | + $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']); |
|
416 | + $this->assertNull($props1[404]['{DAV:}getcontentlength']); |
|
417 | + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']); |
|
418 | + $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue(); |
|
419 | + $this->assertEquals('{DAV:}collection', $resourceType1[0]); |
|
420 | + |
|
421 | + $props2 = $responses[1]->getResponseProperties(); |
|
422 | + $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']); |
|
423 | + $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']); |
|
424 | + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']); |
|
425 | + $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue()); |
|
426 | + } |
|
427 | + |
|
428 | + public function testProcessFilterRulesSingle(): void { |
|
429 | + $this->groupManager->expects($this->any()) |
|
430 | + ->method('isAdmin') |
|
431 | + ->willReturn(true); |
|
432 | + |
|
433 | + $rules = [ |
|
434 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
435 | + ]; |
|
436 | + |
|
437 | + $filesNode1 = $this->createMock(File::class); |
|
438 | + $filesNode1->expects($this->any()) |
|
439 | + ->method('getSize') |
|
440 | + ->willReturn(12); |
|
441 | + $filesNode2 = $this->createMock(Folder::class); |
|
442 | + $filesNode2->expects($this->any()) |
|
443 | + ->method('getSize') |
|
444 | + ->willReturn(10); |
|
445 | + |
|
446 | + $tag123 = $this->createMock(ISystemTag::class); |
|
447 | + $tag123->expects($this->any()) |
|
448 | + ->method('getName') |
|
449 | + ->willReturn('OneTwoThree'); |
|
450 | + $tag123->expects($this->any()) |
|
451 | + ->method('isUserVisible') |
|
452 | + ->willReturn(true); |
|
453 | + |
|
454 | + $this->tagManager->expects($this->once()) |
|
455 | + ->method('getTagsByIds') |
|
456 | + ->with(['123']) |
|
457 | + ->willReturn([$tag123]); |
|
458 | + |
|
459 | + $this->userFolder->expects($this->once()) |
|
460 | + ->method('searchBySystemTag') |
|
461 | + ->with('OneTwoThree') |
|
462 | + ->willReturn([$filesNode1, $filesNode2]); |
|
463 | + |
|
464 | + $this->assertEquals([$filesNode1, $filesNode2], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, 0, 0])); |
|
465 | + } |
|
466 | + |
|
467 | + public function testProcessFilterRulesAndCondition(): void { |
|
468 | + $this->groupManager->expects($this->any()) |
|
469 | + ->method('isAdmin') |
|
470 | + ->willReturn(true); |
|
471 | + |
|
472 | + $filesNode1 = $this->createMock(File::class); |
|
473 | + $filesNode1->expects($this->any()) |
|
474 | + ->method('getSize') |
|
475 | + ->willReturn(12); |
|
476 | + $filesNode1->expects($this->any()) |
|
477 | + ->method('getId') |
|
478 | + ->willReturn(111); |
|
479 | + $filesNode2 = $this->createMock(Folder::class); |
|
480 | + $filesNode2->expects($this->any()) |
|
481 | + ->method('getSize') |
|
482 | + ->willReturn(10); |
|
483 | + $filesNode2->expects($this->any()) |
|
484 | + ->method('getId') |
|
485 | + ->willReturn(222); |
|
486 | + $filesNode3 = $this->createMock(File::class); |
|
487 | + $filesNode3->expects($this->any()) |
|
488 | + ->method('getSize') |
|
489 | + ->willReturn(14); |
|
490 | + $filesNode3->expects($this->any()) |
|
491 | + ->method('getId') |
|
492 | + ->willReturn(333); |
|
493 | + |
|
494 | + $tag123 = $this->createMock(ISystemTag::class); |
|
495 | + $tag123->expects($this->any()) |
|
496 | + ->method('getName') |
|
497 | + ->willReturn('OneTwoThree'); |
|
498 | + $tag123->expects($this->any()) |
|
499 | + ->method('isUserVisible') |
|
500 | + ->willReturn(true); |
|
501 | + $tag456 = $this->createMock(ISystemTag::class); |
|
502 | + $tag456->expects($this->any()) |
|
503 | + ->method('getName') |
|
504 | + ->willReturn('FourFiveSix'); |
|
505 | + $tag456->expects($this->any()) |
|
506 | + ->method('isUserVisible') |
|
507 | + ->willReturn(true); |
|
508 | + |
|
509 | + $this->tagManager->expects($this->once()) |
|
510 | + ->method('getTagsByIds') |
|
511 | + ->with(['123', '456']) |
|
512 | + ->willReturn([$tag123, $tag456]); |
|
513 | + |
|
514 | + $this->userFolder->expects($this->exactly(2)) |
|
515 | + ->method('searchBySystemTag') |
|
516 | + ->withConsecutive( |
|
517 | + ['OneTwoThree'], |
|
518 | + ['FourFiveSix'], |
|
519 | + ) |
|
520 | + ->willReturnOnConsecutiveCalls( |
|
521 | + [$filesNode1, $filesNode2], |
|
522 | + [$filesNode2, $filesNode3], |
|
523 | + ); |
|
524 | + |
|
525 | + $rules = [ |
|
526 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
527 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
528 | + ]; |
|
529 | + |
|
530 | + $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
531 | + } |
|
532 | + |
|
533 | + public function testProcessFilterRulesAndConditionWithOneEmptyResult(): void { |
|
534 | + $this->groupManager->expects($this->any()) |
|
535 | + ->method('isAdmin') |
|
536 | + ->willReturn(true); |
|
537 | + |
|
538 | + $filesNode1 = $this->createMock(File::class); |
|
539 | + $filesNode1->expects($this->any()) |
|
540 | + ->method('getSize') |
|
541 | + ->willReturn(12); |
|
542 | + $filesNode1->expects($this->any()) |
|
543 | + ->method('getId') |
|
544 | + ->willReturn(111); |
|
545 | + $filesNode2 = $this->createMock(Folder::class); |
|
546 | + $filesNode2->expects($this->any()) |
|
547 | + ->method('getSize') |
|
548 | + ->willReturn(10); |
|
549 | + $filesNode2->expects($this->any()) |
|
550 | + ->method('getId') |
|
551 | + ->willReturn(222); |
|
552 | + |
|
553 | + $tag123 = $this->createMock(ISystemTag::class); |
|
554 | + $tag123->expects($this->any()) |
|
555 | + ->method('getName') |
|
556 | + ->willReturn('OneTwoThree'); |
|
557 | + $tag123->expects($this->any()) |
|
558 | + ->method('isUserVisible') |
|
559 | + ->willReturn(true); |
|
560 | + $tag456 = $this->createMock(ISystemTag::class); |
|
561 | + $tag456->expects($this->any()) |
|
562 | + ->method('getName') |
|
563 | + ->willReturn('FourFiveSix'); |
|
564 | + $tag456->expects($this->any()) |
|
565 | + ->method('isUserVisible') |
|
566 | + ->willReturn(true); |
|
567 | + |
|
568 | + $this->tagManager->expects($this->once()) |
|
569 | + ->method('getTagsByIds') |
|
570 | + ->with(['123', '456']) |
|
571 | + ->willReturn([$tag123, $tag456]); |
|
572 | + |
|
573 | + $this->userFolder->expects($this->exactly(2)) |
|
574 | + ->method('searchBySystemTag') |
|
575 | + ->withConsecutive( |
|
576 | + ['OneTwoThree'], |
|
577 | + ['FourFiveSix'], |
|
578 | + ) |
|
579 | + ->willReturnOnConsecutiveCalls( |
|
580 | + [$filesNode1, $filesNode2], |
|
581 | + [], |
|
582 | + ); |
|
583 | + |
|
584 | + $rules = [ |
|
585 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
586 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
587 | + ]; |
|
588 | + |
|
589 | + $this->assertEquals([], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); |
|
590 | + } |
|
591 | + |
|
592 | + public function testProcessFilterRulesAndConditionWithFirstEmptyResult(): void { |
|
593 | + $this->groupManager->expects($this->any()) |
|
594 | + ->method('isAdmin') |
|
595 | + ->willReturn(true); |
|
596 | + |
|
597 | + $filesNode1 = $this->createMock(File::class); |
|
598 | + $filesNode1->expects($this->any()) |
|
599 | + ->method('getSize') |
|
600 | + ->willReturn(12); |
|
601 | + $filesNode1->expects($this->any()) |
|
602 | + ->method('getId') |
|
603 | + ->willReturn(111); |
|
604 | + $filesNode2 = $this->createMock(Folder::class); |
|
605 | + $filesNode2->expects($this->any()) |
|
606 | + ->method('getSize') |
|
607 | + ->willReturn(10); |
|
608 | + $filesNode2->expects($this->any()) |
|
609 | + ->method('getId') |
|
610 | + ->willReturn(222); |
|
611 | + |
|
612 | + $tag123 = $this->createMock(ISystemTag::class); |
|
613 | + $tag123->expects($this->any()) |
|
614 | + ->method('getName') |
|
615 | + ->willReturn('OneTwoThree'); |
|
616 | + $tag123->expects($this->any()) |
|
617 | + ->method('isUserVisible') |
|
618 | + ->willReturn(true); |
|
619 | + $tag456 = $this->createMock(ISystemTag::class); |
|
620 | + $tag456->expects($this->any()) |
|
621 | + ->method('getName') |
|
622 | + ->willReturn('FourFiveSix'); |
|
623 | + $tag456->expects($this->any()) |
|
624 | + ->method('isUserVisible') |
|
625 | + ->willReturn(true); |
|
626 | + |
|
627 | + $this->tagManager->expects($this->once()) |
|
628 | + ->method('getTagsByIds') |
|
629 | + ->with(['123', '456']) |
|
630 | + ->willReturn([$tag123, $tag456]); |
|
631 | + |
|
632 | + $this->userFolder->expects($this->once()) |
|
633 | + ->method('searchBySystemTag') |
|
634 | + ->with('OneTwoThree') |
|
635 | + ->willReturnOnConsecutiveCalls( |
|
636 | + [], |
|
637 | + [$filesNode1, $filesNode2], |
|
638 | + ); |
|
639 | + |
|
640 | + $rules = [ |
|
641 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
642 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
643 | + ]; |
|
644 | + |
|
645 | + $this->assertEquals([], $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); |
|
646 | + } |
|
647 | + |
|
648 | + public function testProcessFilterRulesAndConditionWithEmptyMidResult(): void { |
|
649 | + $this->groupManager->expects($this->any()) |
|
650 | + ->method('isAdmin') |
|
651 | + ->willReturn(true); |
|
652 | + |
|
653 | + $filesNode1 = $this->createMock(File::class); |
|
654 | + $filesNode1->expects($this->any()) |
|
655 | + ->method('getSize') |
|
656 | + ->willReturn(12); |
|
657 | + $filesNode1->expects($this->any()) |
|
658 | + ->method('getId') |
|
659 | + ->willReturn(111); |
|
660 | + $filesNode2 = $this->createMock(Folder::class); |
|
661 | + $filesNode2->expects($this->any()) |
|
662 | + ->method('getSize') |
|
663 | + ->willReturn(10); |
|
664 | + $filesNode2->expects($this->any()) |
|
665 | + ->method('getId') |
|
666 | + ->willReturn(222); |
|
667 | + $filesNode3 = $this->createMock(Folder::class); |
|
668 | + $filesNode3->expects($this->any()) |
|
669 | + ->method('getSize') |
|
670 | + ->willReturn(13); |
|
671 | + $filesNode3->expects($this->any()) |
|
672 | + ->method('getId') |
|
673 | + ->willReturn(333); |
|
674 | + |
|
675 | + $tag123 = $this->createMock(ISystemTag::class); |
|
676 | + $tag123->expects($this->any()) |
|
677 | + ->method('getName') |
|
678 | + ->willReturn('OneTwoThree'); |
|
679 | + $tag123->expects($this->any()) |
|
680 | + ->method('isUserVisible') |
|
681 | + ->willReturn(true); |
|
682 | + $tag456 = $this->createMock(ISystemTag::class); |
|
683 | + $tag456->expects($this->any()) |
|
684 | + ->method('getName') |
|
685 | + ->willReturn('FourFiveSix'); |
|
686 | + $tag456->expects($this->any()) |
|
687 | + ->method('isUserVisible') |
|
688 | + ->willReturn(true); |
|
689 | + $tag789 = $this->createMock(ISystemTag::class); |
|
690 | + $tag789->expects($this->any()) |
|
691 | + ->method('getName') |
|
692 | + ->willReturn('SevenEightNein'); |
|
693 | + $tag789->expects($this->any()) |
|
694 | + ->method('isUserVisible') |
|
695 | + ->willReturn(true); |
|
696 | + |
|
697 | + $this->tagManager->expects($this->once()) |
|
698 | + ->method('getTagsByIds') |
|
699 | + ->with(['123', '456', '789']) |
|
700 | + ->willReturn([$tag123, $tag456, $tag789]); |
|
701 | + |
|
702 | + $this->userFolder->expects($this->exactly(2)) |
|
703 | + ->method('searchBySystemTag') |
|
704 | + ->withConsecutive(['OneTwoThree'], ['FourFiveSix'], ['SevenEightNein']) |
|
705 | + ->willReturnOnConsecutiveCalls( |
|
706 | + [$filesNode1, $filesNode2], |
|
707 | + [$filesNode3], |
|
708 | + [$filesNode1, $filesNode2], |
|
709 | + ); |
|
710 | + |
|
711 | + $rules = [ |
|
712 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
713 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
714 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'], |
|
715 | + ]; |
|
716 | + |
|
717 | + $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
718 | + } |
|
719 | + |
|
720 | + public function testProcessFilterRulesInvisibleTagAsAdmin(): void { |
|
721 | + $this->groupManager->expects($this->any()) |
|
722 | + ->method('isAdmin') |
|
723 | + ->willReturn(true); |
|
724 | + |
|
725 | + $filesNode1 = $this->createMock(File::class); |
|
726 | + $filesNode1->expects($this->any()) |
|
727 | + ->method('getSize') |
|
728 | + ->willReturn(12); |
|
729 | + $filesNode1->expects($this->any()) |
|
730 | + ->method('getId') |
|
731 | + ->willReturn(111); |
|
732 | + $filesNode2 = $this->createMock(Folder::class); |
|
733 | + $filesNode2->expects($this->any()) |
|
734 | + ->method('getSize') |
|
735 | + ->willReturn(10); |
|
736 | + $filesNode2->expects($this->any()) |
|
737 | + ->method('getId') |
|
738 | + ->willReturn(222); |
|
739 | + $filesNode3 = $this->createMock(Folder::class); |
|
740 | + $filesNode3->expects($this->any()) |
|
741 | + ->method('getSize') |
|
742 | + ->willReturn(13); |
|
743 | + $filesNode3->expects($this->any()) |
|
744 | + ->method('getId') |
|
745 | + ->willReturn(333); |
|
746 | + |
|
747 | + $tag123 = $this->createMock(ISystemTag::class); |
|
748 | + $tag123->expects($this->any()) |
|
749 | + ->method('getName') |
|
750 | + ->willReturn('OneTwoThree'); |
|
751 | + $tag123->expects($this->any()) |
|
752 | + ->method('isUserVisible') |
|
753 | + ->willReturn(true); |
|
754 | + $tag456 = $this->createMock(ISystemTag::class); |
|
755 | + $tag456->expects($this->any()) |
|
756 | + ->method('getName') |
|
757 | + ->willReturn('FourFiveSix'); |
|
758 | + $tag456->expects($this->any()) |
|
759 | + ->method('isUserVisible') |
|
760 | + ->willReturn(false); |
|
761 | + |
|
762 | + $this->tagManager->expects($this->once()) |
|
763 | + ->method('getTagsByIds') |
|
764 | + ->with(['123', '456']) |
|
765 | + ->willReturn([$tag123, $tag456]); |
|
766 | + |
|
767 | + $this->userFolder->expects($this->exactly(2)) |
|
768 | + ->method('searchBySystemTag') |
|
769 | + ->withConsecutive(['OneTwoThree'], ['FourFiveSix']) |
|
770 | + ->willReturnOnConsecutiveCalls( |
|
771 | + [$filesNode1, $filesNode2], |
|
772 | + [$filesNode2, $filesNode3], |
|
773 | + ); |
|
774 | + |
|
775 | + $rules = [ |
|
776 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
777 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
778 | + ]; |
|
779 | + |
|
780 | + $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
781 | + } |
|
782 | + |
|
783 | + |
|
784 | + public function testProcessFilterRulesInvisibleTagAsUser(): void { |
|
785 | + $this->expectException(TagNotFoundException::class); |
|
786 | + |
|
787 | + $this->groupManager->expects($this->any()) |
|
788 | + ->method('isAdmin') |
|
789 | + ->willReturn(false); |
|
790 | + |
|
791 | + $tag123 = $this->createMock(ISystemTag::class); |
|
792 | + $tag123->expects($this->any()) |
|
793 | + ->method('getName') |
|
794 | + ->willReturn('OneTwoThree'); |
|
795 | + $tag123->expects($this->any()) |
|
796 | + ->method('isUserVisible') |
|
797 | + ->willReturn(true); |
|
798 | + $tag456 = $this->createMock(ISystemTag::class); |
|
799 | + $tag456->expects($this->any()) |
|
800 | + ->method('getName') |
|
801 | + ->willReturn('FourFiveSix'); |
|
802 | + $tag456->expects($this->any()) |
|
803 | + ->method('isUserVisible') |
|
804 | + ->willReturn(false); |
|
805 | + |
|
806 | + $this->tagManager->expects($this->once()) |
|
807 | + ->method('getTagsByIds') |
|
808 | + ->with(['123', '456']) |
|
809 | + ->willThrowException(new TagNotFoundException()); |
|
810 | + |
|
811 | + $this->userFolder->expects($this->never()) |
|
812 | + ->method('searchBySystemTag'); |
|
813 | + |
|
814 | + $rules = [ |
|
815 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
816 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
817 | + ]; |
|
818 | + |
|
819 | + $this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]); |
|
820 | + } |
|
821 | + |
|
822 | + public function testProcessFilterRulesVisibleTagAsUser(): void { |
|
823 | + $this->groupManager->expects($this->any()) |
|
824 | + ->method('isAdmin') |
|
825 | + ->willReturn(false); |
|
826 | + |
|
827 | + $tag1 = $this->createMock(ISystemTag::class); |
|
828 | + $tag1->expects($this->any()) |
|
829 | + ->method('getId') |
|
830 | + ->willReturn('123'); |
|
831 | + $tag1->expects($this->any()) |
|
832 | + ->method('isUserVisible') |
|
833 | + ->willReturn(true); |
|
834 | + $tag1->expects($this->any()) |
|
835 | + ->method('getName') |
|
836 | + ->willReturn('OneTwoThree'); |
|
837 | + |
|
838 | + $tag2 = $this->createMock(ISystemTag::class); |
|
839 | + $tag2->expects($this->any()) |
|
840 | + ->method('getId') |
|
841 | + ->willReturn('123'); |
|
842 | + $tag2->expects($this->any()) |
|
843 | + ->method('isUserVisible') |
|
844 | + ->willReturn(true); |
|
845 | + $tag2->expects($this->any()) |
|
846 | + ->method('getName') |
|
847 | + ->willReturn('FourFiveSix'); |
|
848 | + |
|
849 | + $this->tagManager->expects($this->once()) |
|
850 | + ->method('getTagsByIds') |
|
851 | + ->with(['123', '456']) |
|
852 | + ->willReturn([$tag1, $tag2]); |
|
853 | + |
|
854 | + $filesNode1 = $this->createMock(File::class); |
|
855 | + $filesNode1->expects($this->any()) |
|
856 | + ->method('getId') |
|
857 | + ->willReturn(111); |
|
858 | + $filesNode1->expects($this->any()) |
|
859 | + ->method('getSize') |
|
860 | + ->willReturn(12); |
|
861 | + $filesNode2 = $this->createMock(Folder::class); |
|
862 | + $filesNode2->expects($this->any()) |
|
863 | + ->method('getId') |
|
864 | + ->willReturn(222); |
|
865 | + $filesNode2->expects($this->any()) |
|
866 | + ->method('getSize') |
|
867 | + ->willReturn(10); |
|
868 | + $filesNode3 = $this->createMock(Folder::class); |
|
869 | + $filesNode3->expects($this->any()) |
|
870 | + ->method('getId') |
|
871 | + ->willReturn(333); |
|
872 | + $filesNode3->expects($this->any()) |
|
873 | + ->method('getSize') |
|
874 | + ->willReturn(33); |
|
875 | + |
|
876 | + $this->tagManager->expects($this->once()) |
|
877 | + ->method('getTagsByIds') |
|
878 | + ->with(['123', '456']) |
|
879 | + ->willReturn([$tag1, $tag2]); |
|
880 | + |
|
881 | + // main assertion: only user visible tags are being passed through. |
|
882 | + $this->userFolder->expects($this->exactly(2)) |
|
883 | + ->method('searchBySystemTag') |
|
884 | + ->withConsecutive(['OneTwoThree'], ['FourFiveSix']) |
|
885 | + ->willReturnOnConsecutiveCalls( |
|
886 | + [$filesNode1, $filesNode2], |
|
887 | + [$filesNode2, $filesNode3], |
|
888 | + ); |
|
889 | + |
|
890 | + $rules = [ |
|
891 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], |
|
892 | + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], |
|
893 | + ]; |
|
894 | + |
|
895 | + $this->assertEquals([$filesNode2], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); |
|
896 | + } |
|
897 | + |
|
898 | + public function testProcessFavoriteFilter(): void { |
|
899 | + $rules = [ |
|
900 | + ['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'], |
|
901 | + ]; |
|
902 | + |
|
903 | + $this->privateTags->expects($this->once()) |
|
904 | + ->method('getFavorites') |
|
905 | + ->willReturn(['456', '789']); |
|
906 | + |
|
907 | + $this->assertEquals(['456', '789'], array_values($this->invokePrivate($this->plugin, 'processFilterRulesForFileIDs', [$rules]))); |
|
908 | + } |
|
909 | + |
|
910 | + public function filesBaseUriProvider() { |
|
911 | + return [ |
|
912 | + ['', '', ''], |
|
913 | + ['files/username', '', '/files/username'], |
|
914 | + ['files/username/test', '/test', '/files/username'], |
|
915 | + ['files/username/test/sub', '/test/sub', '/files/username'], |
|
916 | + ['test', '/test', ''], |
|
917 | + ]; |
|
918 | + } |
|
919 | + |
|
920 | + /** |
|
921 | + * @dataProvider filesBaseUriProvider |
|
922 | + */ |
|
923 | + public function testFilesBaseUri($uri, $reportPath, $expectedUri): void { |
|
924 | + $this->assertEquals($expectedUri, $this->invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath])); |
|
925 | + } |
|
926 | 926 | } |
@@ -100,332 +100,332 @@ |
||
100 | 100 | use SearchDAV\DAV\SearchPlugin; |
101 | 101 | |
102 | 102 | class Server { |
103 | - public Connector\Sabre\Server $server; |
|
104 | - private IProfiler $profiler; |
|
105 | - |
|
106 | - public function __construct( |
|
107 | - private IRequest $request, |
|
108 | - private string $baseUri, |
|
109 | - ) { |
|
110 | - $this->profiler = \OCP\Server::get(IProfiler::class); |
|
111 | - if ($this->profiler->isEnabled()) { |
|
112 | - /** @var IEventLogger $eventLogger */ |
|
113 | - $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
114 | - $eventLogger->start('runtime', 'DAV Runtime'); |
|
115 | - } |
|
116 | - |
|
117 | - $logger = \OCP\Server::get(LoggerInterface::class); |
|
118 | - $eventDispatcher = \OCP\Server::get(IEventDispatcher::class); |
|
119 | - |
|
120 | - $root = new RootCollection(); |
|
121 | - $this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root)); |
|
122 | - |
|
123 | - // Add maintenance plugin |
|
124 | - $this->server->addPlugin(new MaintenancePlugin(\OCP\Server::get(IConfig::class), \OC::$server->getL10N('dav'))); |
|
125 | - |
|
126 | - $this->server->addPlugin(new AppleQuirksPlugin()); |
|
127 | - |
|
128 | - // Backends |
|
129 | - $authBackend = new Auth( |
|
130 | - \OCP\Server::get(ISession::class), |
|
131 | - \OCP\Server::get(IUserSession::class), |
|
132 | - \OCP\Server::get(IRequest::class), |
|
133 | - \OCP\Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
134 | - \OCP\Server::get(IThrottler::class) |
|
135 | - ); |
|
136 | - |
|
137 | - // Set URL explicitly due to reverse-proxy situations |
|
138 | - $this->server->httpRequest->setUrl($this->request->getRequestUri()); |
|
139 | - $this->server->setBaseUri($this->baseUri); |
|
140 | - |
|
141 | - $this->server->addPlugin(new ProfilerPlugin($this->request)); |
|
142 | - $this->server->addPlugin(new BlockLegacyClientPlugin( |
|
143 | - \OCP\Server::get(IConfig::class), |
|
144 | - \OCP\Server::get(ThemingDefaults::class), |
|
145 | - )); |
|
146 | - $this->server->addPlugin(new AnonymousOptionsPlugin()); |
|
147 | - $authPlugin = new Plugin(); |
|
148 | - $authPlugin->addBackend(new PublicAuth()); |
|
149 | - $this->server->addPlugin($authPlugin); |
|
150 | - |
|
151 | - // allow setup of additional auth backends |
|
152 | - $event = new SabrePluginEvent($this->server); |
|
153 | - $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); |
|
154 | - |
|
155 | - $newAuthEvent = new SabrePluginAuthInitEvent($this->server); |
|
156 | - $eventDispatcher->dispatchTyped($newAuthEvent); |
|
157 | - |
|
158 | - $bearerAuthBackend = new BearerAuth( |
|
159 | - \OCP\Server::get(IUserSession::class), |
|
160 | - \OCP\Server::get(ISession::class), |
|
161 | - \OCP\Server::get(IRequest::class), |
|
162 | - \OCP\Server::get(IConfig::class), |
|
163 | - ); |
|
164 | - $authPlugin->addBackend($bearerAuthBackend); |
|
165 | - // because we are throwing exceptions this plugin has to be the last one |
|
166 | - $authPlugin->addBackend($authBackend); |
|
167 | - |
|
168 | - // debugging |
|
169 | - if (\OCP\Server::get(IConfig::class)->getSystemValue('debug', false)) { |
|
170 | - $this->server->addPlugin(new \Sabre\DAV\Browser\Plugin()); |
|
171 | - } else { |
|
172 | - $this->server->addPlugin(new DummyGetResponsePlugin()); |
|
173 | - } |
|
174 | - |
|
175 | - $this->server->addPlugin(new ExceptionLoggerPlugin('webdav', $logger)); |
|
176 | - $this->server->addPlugin(new LockPlugin()); |
|
177 | - $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); |
|
178 | - |
|
179 | - // acl |
|
180 | - $acl = new DavAclPlugin(); |
|
181 | - $acl->principalCollectionSet = [ |
|
182 | - 'principals/users', |
|
183 | - 'principals/groups', |
|
184 | - 'principals/calendar-resources', |
|
185 | - 'principals/calendar-rooms', |
|
186 | - ]; |
|
187 | - $this->server->addPlugin($acl); |
|
188 | - |
|
189 | - // calendar plugins |
|
190 | - if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) { |
|
191 | - $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
192 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); |
|
193 | - $this->server->addPlugin(new ICSExportPlugin(\OCP\Server::get(IConfig::class), $logger)); |
|
194 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OCP\Server::get(IConfig::class), \OCP\Server::get(LoggerInterface::class), \OCP\Server::get(DefaultCalendarValidator::class))); |
|
195 | - |
|
196 | - $this->server->addPlugin(\OCP\Server::get(\OCA\DAV\CalDAV\Trashbin\Plugin::class)); |
|
197 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\WebcalCaching\Plugin($this->request)); |
|
198 | - if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'allow_calendar_link_subscriptions', 'yes') === 'yes') { |
|
199 | - $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); |
|
200 | - } |
|
201 | - |
|
202 | - $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); |
|
203 | - $this->server->addPlugin(new PublishPlugin( |
|
204 | - \OCP\Server::get(IConfig::class), |
|
205 | - \OCP\Server::get(IURLGenerator::class) |
|
206 | - )); |
|
207 | - |
|
208 | - $this->server->addPlugin(\OCP\Server::get(RateLimitingPlugin::class)); |
|
209 | - $this->server->addPlugin(\OCP\Server::get(CalDavValidatePlugin::class)); |
|
210 | - } |
|
211 | - |
|
212 | - // addressbook plugins |
|
213 | - if ($this->requestIsForSubtree(['addressbooks', 'principals'])) { |
|
214 | - $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
215 | - $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); |
|
216 | - $this->server->addPlugin(new VCFExportPlugin()); |
|
217 | - $this->server->addPlugin(new MultiGetExportPlugin()); |
|
218 | - $this->server->addPlugin(new HasPhotoPlugin()); |
|
219 | - $this->server->addPlugin(new ImageExportPlugin(new PhotoCache( |
|
220 | - \OCP\Server::get(IAppDataFactory::class)->get('dav-photocache'), |
|
221 | - $logger) |
|
222 | - )); |
|
223 | - |
|
224 | - $this->server->addPlugin(\OCP\Server::get(CardDavRateLimitingPlugin::class)); |
|
225 | - $this->server->addPlugin(\OCP\Server::get(CardDavValidatePlugin::class)); |
|
226 | - } |
|
227 | - |
|
228 | - // system tags plugins |
|
229 | - $this->server->addPlugin(\OCP\Server::get(SystemTagPlugin::class)); |
|
230 | - |
|
231 | - // comments plugin |
|
232 | - $this->server->addPlugin(new CommentsPlugin( |
|
233 | - \OCP\Server::get(ICommentsManager::class), |
|
234 | - \OCP\Server::get(IUserSession::class) |
|
235 | - )); |
|
236 | - |
|
237 | - $this->server->addPlugin(new CopyEtagHeaderPlugin()); |
|
238 | - $this->server->addPlugin(new RequestIdHeaderPlugin(\OCP\Server::get(IRequest::class))); |
|
239 | - $this->server->addPlugin(new ChunkingV2Plugin(\OCP\Server::get(ICacheFactory::class))); |
|
240 | - $this->server->addPlugin(new ChunkingPlugin()); |
|
241 | - $this->server->addPlugin(new ZipFolderPlugin( |
|
242 | - $this->server->tree, |
|
243 | - $logger, |
|
244 | - $eventDispatcher, |
|
245 | - )); |
|
246 | - $this->server->addPlugin(\OCP\Server::get(PaginatePlugin::class)); |
|
247 | - |
|
248 | - // allow setup of additional plugins |
|
249 | - $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event); |
|
250 | - $typedEvent = new SabrePluginAddEvent($this->server); |
|
251 | - $eventDispatcher->dispatchTyped($typedEvent); |
|
252 | - |
|
253 | - // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
254 | - // we do not provide locking we emulate it using a fake locking plugin. |
|
255 | - if ($this->request->isUserAgent([ |
|
256 | - '/WebDAVFS/', |
|
257 | - '/OneNote/', |
|
258 | - '/^Microsoft-WebDAV/',// Microsoft-WebDAV-MiniRedir/6.1.7601 |
|
259 | - ])) { |
|
260 | - $this->server->addPlugin(new FakeLockerPlugin()); |
|
261 | - } |
|
262 | - |
|
263 | - if (BrowserErrorPagePlugin::isBrowserRequest($request)) { |
|
264 | - $this->server->addPlugin(new BrowserErrorPagePlugin()); |
|
265 | - } |
|
266 | - |
|
267 | - $lazySearchBackend = new LazySearchBackend(); |
|
268 | - $this->server->addPlugin(new SearchPlugin($lazySearchBackend)); |
|
269 | - |
|
270 | - // wait with registering these until auth is handled and the filesystem is setup |
|
271 | - $this->server->on('beforeMethod:*', function () use ($root, $lazySearchBackend, $logger): void { |
|
272 | - // Allow view-only plugin for webdav requests |
|
273 | - $this->server->addPlugin(new ViewOnlyPlugin( |
|
274 | - \OC::$server->getUserFolder(), |
|
275 | - )); |
|
276 | - |
|
277 | - // custom properties plugin must be the last one |
|
278 | - $userSession = \OCP\Server::get(IUserSession::class); |
|
279 | - $user = $userSession->getUser(); |
|
280 | - if ($user !== null) { |
|
281 | - $view = Filesystem::getView(); |
|
282 | - $config = \OCP\Server::get(IConfig::class); |
|
283 | - $this->server->addPlugin( |
|
284 | - new FilesPlugin( |
|
285 | - $this->server->tree, |
|
286 | - $config, |
|
287 | - $this->request, |
|
288 | - \OCP\Server::get(IPreview::class), |
|
289 | - \OCP\Server::get(IUserSession::class), |
|
290 | - \OCP\Server::get(IFilenameValidator::class), |
|
291 | - \OCP\Server::get(IAccountManager::class), |
|
292 | - false, |
|
293 | - $config->getSystemValueBool('debug', false) === false, |
|
294 | - ) |
|
295 | - ); |
|
296 | - $this->server->addPlugin(new ChecksumUpdatePlugin()); |
|
297 | - |
|
298 | - $this->server->addPlugin( |
|
299 | - new \Sabre\DAV\PropertyStorage\Plugin( |
|
300 | - new CustomPropertiesBackend( |
|
301 | - $this->server, |
|
302 | - $this->server->tree, |
|
303 | - \OCP\Server::get(IDBConnection::class), |
|
304 | - \OCP\Server::get(IUserSession::class)->getUser(), |
|
305 | - \OCP\Server::get(DefaultCalendarValidator::class), |
|
306 | - ) |
|
307 | - ) |
|
308 | - ); |
|
309 | - if ($view !== null) { |
|
310 | - $this->server->addPlugin( |
|
311 | - new QuotaPlugin($view)); |
|
312 | - } |
|
313 | - $this->server->addPlugin( |
|
314 | - new TagsPlugin( |
|
315 | - $this->server->tree, \OCP\Server::get(ITagManager::class), \OCP\Server::get(IEventDispatcher::class), \OCP\Server::get(IUserSession::class) |
|
316 | - ) |
|
317 | - ); |
|
318 | - |
|
319 | - // TODO: switch to LazyUserFolder |
|
320 | - $userFolder = \OC::$server->getUserFolder(); |
|
321 | - $shareManager = \OCP\Server::get(\OCP\Share\IManager::class); |
|
322 | - $this->server->addPlugin(new SharesPlugin( |
|
323 | - $this->server->tree, |
|
324 | - $userSession, |
|
325 | - $userFolder, |
|
326 | - $shareManager, |
|
327 | - )); |
|
328 | - $this->server->addPlugin(new CommentPropertiesPlugin( |
|
329 | - \OCP\Server::get(ICommentsManager::class), |
|
330 | - $userSession |
|
331 | - )); |
|
332 | - if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') { |
|
333 | - $this->server->addPlugin(new IMipPlugin( |
|
334 | - \OCP\Server::get(IAppConfig::class), |
|
335 | - \OCP\Server::get(IMailer::class), |
|
336 | - \OCP\Server::get(LoggerInterface::class), |
|
337 | - \OCP\Server::get(ITimeFactory::class), |
|
338 | - \OCP\Server::get(Defaults::class), |
|
339 | - $userSession, |
|
340 | - \OCP\Server::get(IMipService::class), |
|
341 | - \OCP\Server::get(EventComparisonService::class), |
|
342 | - \OCP\Server::get(\OCP\Mail\Provider\IManager::class) |
|
343 | - )); |
|
344 | - } |
|
345 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin()); |
|
346 | - if ($view !== null) { |
|
347 | - $this->server->addPlugin(new FilesReportPlugin( |
|
348 | - $this->server->tree, |
|
349 | - $view, |
|
350 | - \OCP\Server::get(ISystemTagManager::class), |
|
351 | - \OCP\Server::get(ISystemTagObjectMapper::class), |
|
352 | - \OCP\Server::get(ITagManager::class), |
|
353 | - $userSession, |
|
354 | - \OCP\Server::get(IGroupManager::class), |
|
355 | - $userFolder, |
|
356 | - \OCP\Server::get(IAppManager::class) |
|
357 | - )); |
|
358 | - $lazySearchBackend->setBackend(new FileSearchBackend( |
|
359 | - $this->server->tree, |
|
360 | - $user, |
|
361 | - \OCP\Server::get(IRootFolder::class), |
|
362 | - $shareManager, |
|
363 | - $view, |
|
364 | - \OCP\Server::get(IFilesMetadataManager::class) |
|
365 | - )); |
|
366 | - $this->server->addPlugin( |
|
367 | - new BulkUploadPlugin( |
|
368 | - $userFolder, |
|
369 | - $logger |
|
370 | - ) |
|
371 | - ); |
|
372 | - } |
|
373 | - $this->server->addPlugin(new EnablePlugin( |
|
374 | - \OCP\Server::get(IConfig::class), |
|
375 | - \OCP\Server::get(BirthdayService::class), |
|
376 | - $user |
|
377 | - )); |
|
378 | - $this->server->addPlugin(new AppleProvisioningPlugin( |
|
379 | - \OCP\Server::get(IUserSession::class), |
|
380 | - \OCP\Server::get(IURLGenerator::class), |
|
381 | - \OCP\Server::get(ThemingDefaults::class), |
|
382 | - \OCP\Server::get(IRequest::class), |
|
383 | - \OC::$server->getL10N('dav'), |
|
384 | - function () { |
|
385 | - return UUIDUtil::getUUID(); |
|
386 | - } |
|
387 | - )); |
|
388 | - } |
|
389 | - |
|
390 | - // register plugins from apps |
|
391 | - $pluginManager = new PluginManager( |
|
392 | - \OC::$server, |
|
393 | - \OCP\Server::get(IAppManager::class) |
|
394 | - ); |
|
395 | - foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
396 | - $this->server->addPlugin($appPlugin); |
|
397 | - } |
|
398 | - foreach ($pluginManager->getAppCollections() as $appCollection) { |
|
399 | - $root->addChild($appCollection); |
|
400 | - } |
|
401 | - }); |
|
402 | - |
|
403 | - $this->server->addPlugin( |
|
404 | - new PropfindCompressionPlugin() |
|
405 | - ); |
|
406 | - } |
|
407 | - |
|
408 | - public function exec() { |
|
409 | - /** @var IEventLogger $eventLogger */ |
|
410 | - $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
411 | - $eventLogger->start('dav_server_exec', ''); |
|
412 | - $this->server->start(); |
|
413 | - $eventLogger->end('dav_server_exec'); |
|
414 | - if ($this->profiler->isEnabled()) { |
|
415 | - $eventLogger->end('runtime'); |
|
416 | - $profile = $this->profiler->collect(\OCP\Server::get(IRequest::class), new Response()); |
|
417 | - $this->profiler->saveProfile($profile); |
|
418 | - } |
|
419 | - } |
|
420 | - |
|
421 | - private function requestIsForSubtree(array $subTrees): bool { |
|
422 | - foreach ($subTrees as $subTree) { |
|
423 | - $subTree = trim($subTree, ' /'); |
|
424 | - if (str_starts_with($this->server->getRequestUri(), $subTree . '/')) { |
|
425 | - return true; |
|
426 | - } |
|
427 | - } |
|
428 | - return false; |
|
429 | - } |
|
103 | + public Connector\Sabre\Server $server; |
|
104 | + private IProfiler $profiler; |
|
105 | + |
|
106 | + public function __construct( |
|
107 | + private IRequest $request, |
|
108 | + private string $baseUri, |
|
109 | + ) { |
|
110 | + $this->profiler = \OCP\Server::get(IProfiler::class); |
|
111 | + if ($this->profiler->isEnabled()) { |
|
112 | + /** @var IEventLogger $eventLogger */ |
|
113 | + $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
114 | + $eventLogger->start('runtime', 'DAV Runtime'); |
|
115 | + } |
|
116 | + |
|
117 | + $logger = \OCP\Server::get(LoggerInterface::class); |
|
118 | + $eventDispatcher = \OCP\Server::get(IEventDispatcher::class); |
|
119 | + |
|
120 | + $root = new RootCollection(); |
|
121 | + $this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root)); |
|
122 | + |
|
123 | + // Add maintenance plugin |
|
124 | + $this->server->addPlugin(new MaintenancePlugin(\OCP\Server::get(IConfig::class), \OC::$server->getL10N('dav'))); |
|
125 | + |
|
126 | + $this->server->addPlugin(new AppleQuirksPlugin()); |
|
127 | + |
|
128 | + // Backends |
|
129 | + $authBackend = new Auth( |
|
130 | + \OCP\Server::get(ISession::class), |
|
131 | + \OCP\Server::get(IUserSession::class), |
|
132 | + \OCP\Server::get(IRequest::class), |
|
133 | + \OCP\Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
134 | + \OCP\Server::get(IThrottler::class) |
|
135 | + ); |
|
136 | + |
|
137 | + // Set URL explicitly due to reverse-proxy situations |
|
138 | + $this->server->httpRequest->setUrl($this->request->getRequestUri()); |
|
139 | + $this->server->setBaseUri($this->baseUri); |
|
140 | + |
|
141 | + $this->server->addPlugin(new ProfilerPlugin($this->request)); |
|
142 | + $this->server->addPlugin(new BlockLegacyClientPlugin( |
|
143 | + \OCP\Server::get(IConfig::class), |
|
144 | + \OCP\Server::get(ThemingDefaults::class), |
|
145 | + )); |
|
146 | + $this->server->addPlugin(new AnonymousOptionsPlugin()); |
|
147 | + $authPlugin = new Plugin(); |
|
148 | + $authPlugin->addBackend(new PublicAuth()); |
|
149 | + $this->server->addPlugin($authPlugin); |
|
150 | + |
|
151 | + // allow setup of additional auth backends |
|
152 | + $event = new SabrePluginEvent($this->server); |
|
153 | + $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); |
|
154 | + |
|
155 | + $newAuthEvent = new SabrePluginAuthInitEvent($this->server); |
|
156 | + $eventDispatcher->dispatchTyped($newAuthEvent); |
|
157 | + |
|
158 | + $bearerAuthBackend = new BearerAuth( |
|
159 | + \OCP\Server::get(IUserSession::class), |
|
160 | + \OCP\Server::get(ISession::class), |
|
161 | + \OCP\Server::get(IRequest::class), |
|
162 | + \OCP\Server::get(IConfig::class), |
|
163 | + ); |
|
164 | + $authPlugin->addBackend($bearerAuthBackend); |
|
165 | + // because we are throwing exceptions this plugin has to be the last one |
|
166 | + $authPlugin->addBackend($authBackend); |
|
167 | + |
|
168 | + // debugging |
|
169 | + if (\OCP\Server::get(IConfig::class)->getSystemValue('debug', false)) { |
|
170 | + $this->server->addPlugin(new \Sabre\DAV\Browser\Plugin()); |
|
171 | + } else { |
|
172 | + $this->server->addPlugin(new DummyGetResponsePlugin()); |
|
173 | + } |
|
174 | + |
|
175 | + $this->server->addPlugin(new ExceptionLoggerPlugin('webdav', $logger)); |
|
176 | + $this->server->addPlugin(new LockPlugin()); |
|
177 | + $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); |
|
178 | + |
|
179 | + // acl |
|
180 | + $acl = new DavAclPlugin(); |
|
181 | + $acl->principalCollectionSet = [ |
|
182 | + 'principals/users', |
|
183 | + 'principals/groups', |
|
184 | + 'principals/calendar-resources', |
|
185 | + 'principals/calendar-rooms', |
|
186 | + ]; |
|
187 | + $this->server->addPlugin($acl); |
|
188 | + |
|
189 | + // calendar plugins |
|
190 | + if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) { |
|
191 | + $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
192 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); |
|
193 | + $this->server->addPlugin(new ICSExportPlugin(\OCP\Server::get(IConfig::class), $logger)); |
|
194 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OCP\Server::get(IConfig::class), \OCP\Server::get(LoggerInterface::class), \OCP\Server::get(DefaultCalendarValidator::class))); |
|
195 | + |
|
196 | + $this->server->addPlugin(\OCP\Server::get(\OCA\DAV\CalDAV\Trashbin\Plugin::class)); |
|
197 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\WebcalCaching\Plugin($this->request)); |
|
198 | + if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'allow_calendar_link_subscriptions', 'yes') === 'yes') { |
|
199 | + $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); |
|
200 | + } |
|
201 | + |
|
202 | + $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); |
|
203 | + $this->server->addPlugin(new PublishPlugin( |
|
204 | + \OCP\Server::get(IConfig::class), |
|
205 | + \OCP\Server::get(IURLGenerator::class) |
|
206 | + )); |
|
207 | + |
|
208 | + $this->server->addPlugin(\OCP\Server::get(RateLimitingPlugin::class)); |
|
209 | + $this->server->addPlugin(\OCP\Server::get(CalDavValidatePlugin::class)); |
|
210 | + } |
|
211 | + |
|
212 | + // addressbook plugins |
|
213 | + if ($this->requestIsForSubtree(['addressbooks', 'principals'])) { |
|
214 | + $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
215 | + $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); |
|
216 | + $this->server->addPlugin(new VCFExportPlugin()); |
|
217 | + $this->server->addPlugin(new MultiGetExportPlugin()); |
|
218 | + $this->server->addPlugin(new HasPhotoPlugin()); |
|
219 | + $this->server->addPlugin(new ImageExportPlugin(new PhotoCache( |
|
220 | + \OCP\Server::get(IAppDataFactory::class)->get('dav-photocache'), |
|
221 | + $logger) |
|
222 | + )); |
|
223 | + |
|
224 | + $this->server->addPlugin(\OCP\Server::get(CardDavRateLimitingPlugin::class)); |
|
225 | + $this->server->addPlugin(\OCP\Server::get(CardDavValidatePlugin::class)); |
|
226 | + } |
|
227 | + |
|
228 | + // system tags plugins |
|
229 | + $this->server->addPlugin(\OCP\Server::get(SystemTagPlugin::class)); |
|
230 | + |
|
231 | + // comments plugin |
|
232 | + $this->server->addPlugin(new CommentsPlugin( |
|
233 | + \OCP\Server::get(ICommentsManager::class), |
|
234 | + \OCP\Server::get(IUserSession::class) |
|
235 | + )); |
|
236 | + |
|
237 | + $this->server->addPlugin(new CopyEtagHeaderPlugin()); |
|
238 | + $this->server->addPlugin(new RequestIdHeaderPlugin(\OCP\Server::get(IRequest::class))); |
|
239 | + $this->server->addPlugin(new ChunkingV2Plugin(\OCP\Server::get(ICacheFactory::class))); |
|
240 | + $this->server->addPlugin(new ChunkingPlugin()); |
|
241 | + $this->server->addPlugin(new ZipFolderPlugin( |
|
242 | + $this->server->tree, |
|
243 | + $logger, |
|
244 | + $eventDispatcher, |
|
245 | + )); |
|
246 | + $this->server->addPlugin(\OCP\Server::get(PaginatePlugin::class)); |
|
247 | + |
|
248 | + // allow setup of additional plugins |
|
249 | + $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event); |
|
250 | + $typedEvent = new SabrePluginAddEvent($this->server); |
|
251 | + $eventDispatcher->dispatchTyped($typedEvent); |
|
252 | + |
|
253 | + // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
254 | + // we do not provide locking we emulate it using a fake locking plugin. |
|
255 | + if ($this->request->isUserAgent([ |
|
256 | + '/WebDAVFS/', |
|
257 | + '/OneNote/', |
|
258 | + '/^Microsoft-WebDAV/',// Microsoft-WebDAV-MiniRedir/6.1.7601 |
|
259 | + ])) { |
|
260 | + $this->server->addPlugin(new FakeLockerPlugin()); |
|
261 | + } |
|
262 | + |
|
263 | + if (BrowserErrorPagePlugin::isBrowserRequest($request)) { |
|
264 | + $this->server->addPlugin(new BrowserErrorPagePlugin()); |
|
265 | + } |
|
266 | + |
|
267 | + $lazySearchBackend = new LazySearchBackend(); |
|
268 | + $this->server->addPlugin(new SearchPlugin($lazySearchBackend)); |
|
269 | + |
|
270 | + // wait with registering these until auth is handled and the filesystem is setup |
|
271 | + $this->server->on('beforeMethod:*', function () use ($root, $lazySearchBackend, $logger): void { |
|
272 | + // Allow view-only plugin for webdav requests |
|
273 | + $this->server->addPlugin(new ViewOnlyPlugin( |
|
274 | + \OC::$server->getUserFolder(), |
|
275 | + )); |
|
276 | + |
|
277 | + // custom properties plugin must be the last one |
|
278 | + $userSession = \OCP\Server::get(IUserSession::class); |
|
279 | + $user = $userSession->getUser(); |
|
280 | + if ($user !== null) { |
|
281 | + $view = Filesystem::getView(); |
|
282 | + $config = \OCP\Server::get(IConfig::class); |
|
283 | + $this->server->addPlugin( |
|
284 | + new FilesPlugin( |
|
285 | + $this->server->tree, |
|
286 | + $config, |
|
287 | + $this->request, |
|
288 | + \OCP\Server::get(IPreview::class), |
|
289 | + \OCP\Server::get(IUserSession::class), |
|
290 | + \OCP\Server::get(IFilenameValidator::class), |
|
291 | + \OCP\Server::get(IAccountManager::class), |
|
292 | + false, |
|
293 | + $config->getSystemValueBool('debug', false) === false, |
|
294 | + ) |
|
295 | + ); |
|
296 | + $this->server->addPlugin(new ChecksumUpdatePlugin()); |
|
297 | + |
|
298 | + $this->server->addPlugin( |
|
299 | + new \Sabre\DAV\PropertyStorage\Plugin( |
|
300 | + new CustomPropertiesBackend( |
|
301 | + $this->server, |
|
302 | + $this->server->tree, |
|
303 | + \OCP\Server::get(IDBConnection::class), |
|
304 | + \OCP\Server::get(IUserSession::class)->getUser(), |
|
305 | + \OCP\Server::get(DefaultCalendarValidator::class), |
|
306 | + ) |
|
307 | + ) |
|
308 | + ); |
|
309 | + if ($view !== null) { |
|
310 | + $this->server->addPlugin( |
|
311 | + new QuotaPlugin($view)); |
|
312 | + } |
|
313 | + $this->server->addPlugin( |
|
314 | + new TagsPlugin( |
|
315 | + $this->server->tree, \OCP\Server::get(ITagManager::class), \OCP\Server::get(IEventDispatcher::class), \OCP\Server::get(IUserSession::class) |
|
316 | + ) |
|
317 | + ); |
|
318 | + |
|
319 | + // TODO: switch to LazyUserFolder |
|
320 | + $userFolder = \OC::$server->getUserFolder(); |
|
321 | + $shareManager = \OCP\Server::get(\OCP\Share\IManager::class); |
|
322 | + $this->server->addPlugin(new SharesPlugin( |
|
323 | + $this->server->tree, |
|
324 | + $userSession, |
|
325 | + $userFolder, |
|
326 | + $shareManager, |
|
327 | + )); |
|
328 | + $this->server->addPlugin(new CommentPropertiesPlugin( |
|
329 | + \OCP\Server::get(ICommentsManager::class), |
|
330 | + $userSession |
|
331 | + )); |
|
332 | + if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') { |
|
333 | + $this->server->addPlugin(new IMipPlugin( |
|
334 | + \OCP\Server::get(IAppConfig::class), |
|
335 | + \OCP\Server::get(IMailer::class), |
|
336 | + \OCP\Server::get(LoggerInterface::class), |
|
337 | + \OCP\Server::get(ITimeFactory::class), |
|
338 | + \OCP\Server::get(Defaults::class), |
|
339 | + $userSession, |
|
340 | + \OCP\Server::get(IMipService::class), |
|
341 | + \OCP\Server::get(EventComparisonService::class), |
|
342 | + \OCP\Server::get(\OCP\Mail\Provider\IManager::class) |
|
343 | + )); |
|
344 | + } |
|
345 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin()); |
|
346 | + if ($view !== null) { |
|
347 | + $this->server->addPlugin(new FilesReportPlugin( |
|
348 | + $this->server->tree, |
|
349 | + $view, |
|
350 | + \OCP\Server::get(ISystemTagManager::class), |
|
351 | + \OCP\Server::get(ISystemTagObjectMapper::class), |
|
352 | + \OCP\Server::get(ITagManager::class), |
|
353 | + $userSession, |
|
354 | + \OCP\Server::get(IGroupManager::class), |
|
355 | + $userFolder, |
|
356 | + \OCP\Server::get(IAppManager::class) |
|
357 | + )); |
|
358 | + $lazySearchBackend->setBackend(new FileSearchBackend( |
|
359 | + $this->server->tree, |
|
360 | + $user, |
|
361 | + \OCP\Server::get(IRootFolder::class), |
|
362 | + $shareManager, |
|
363 | + $view, |
|
364 | + \OCP\Server::get(IFilesMetadataManager::class) |
|
365 | + )); |
|
366 | + $this->server->addPlugin( |
|
367 | + new BulkUploadPlugin( |
|
368 | + $userFolder, |
|
369 | + $logger |
|
370 | + ) |
|
371 | + ); |
|
372 | + } |
|
373 | + $this->server->addPlugin(new EnablePlugin( |
|
374 | + \OCP\Server::get(IConfig::class), |
|
375 | + \OCP\Server::get(BirthdayService::class), |
|
376 | + $user |
|
377 | + )); |
|
378 | + $this->server->addPlugin(new AppleProvisioningPlugin( |
|
379 | + \OCP\Server::get(IUserSession::class), |
|
380 | + \OCP\Server::get(IURLGenerator::class), |
|
381 | + \OCP\Server::get(ThemingDefaults::class), |
|
382 | + \OCP\Server::get(IRequest::class), |
|
383 | + \OC::$server->getL10N('dav'), |
|
384 | + function () { |
|
385 | + return UUIDUtil::getUUID(); |
|
386 | + } |
|
387 | + )); |
|
388 | + } |
|
389 | + |
|
390 | + // register plugins from apps |
|
391 | + $pluginManager = new PluginManager( |
|
392 | + \OC::$server, |
|
393 | + \OCP\Server::get(IAppManager::class) |
|
394 | + ); |
|
395 | + foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
396 | + $this->server->addPlugin($appPlugin); |
|
397 | + } |
|
398 | + foreach ($pluginManager->getAppCollections() as $appCollection) { |
|
399 | + $root->addChild($appCollection); |
|
400 | + } |
|
401 | + }); |
|
402 | + |
|
403 | + $this->server->addPlugin( |
|
404 | + new PropfindCompressionPlugin() |
|
405 | + ); |
|
406 | + } |
|
407 | + |
|
408 | + public function exec() { |
|
409 | + /** @var IEventLogger $eventLogger */ |
|
410 | + $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
411 | + $eventLogger->start('dav_server_exec', ''); |
|
412 | + $this->server->start(); |
|
413 | + $eventLogger->end('dav_server_exec'); |
|
414 | + if ($this->profiler->isEnabled()) { |
|
415 | + $eventLogger->end('runtime'); |
|
416 | + $profile = $this->profiler->collect(\OCP\Server::get(IRequest::class), new Response()); |
|
417 | + $this->profiler->saveProfile($profile); |
|
418 | + } |
|
419 | + } |
|
420 | + |
|
421 | + private function requestIsForSubtree(array $subTrees): bool { |
|
422 | + foreach ($subTrees as $subTree) { |
|
423 | + $subTree = trim($subTree, ' /'); |
|
424 | + if (str_starts_with($this->server->getRequestUri(), $subTree . '/')) { |
|
425 | + return true; |
|
426 | + } |
|
427 | + } |
|
428 | + return false; |
|
429 | + } |
|
430 | 430 | |
431 | 431 | } |
@@ -37,157 +37,157 @@ |
||
37 | 37 | |
38 | 38 | class ServerFactory { |
39 | 39 | |
40 | - public function __construct( |
|
41 | - private IConfig $config, |
|
42 | - private LoggerInterface $logger, |
|
43 | - private IDBConnection $databaseConnection, |
|
44 | - private IUserSession $userSession, |
|
45 | - private IMountManager $mountManager, |
|
46 | - private ITagManager $tagManager, |
|
47 | - private IRequest $request, |
|
48 | - private IPreview $previewManager, |
|
49 | - private IEventDispatcher $eventDispatcher, |
|
50 | - private IL10N $l10n, |
|
51 | - ) { |
|
52 | - } |
|
53 | - |
|
54 | - /** |
|
55 | - * @param callable $viewCallBack callback that should return the view for the dav endpoint |
|
56 | - */ |
|
57 | - public function createServer(string $baseUri, |
|
58 | - string $requestUri, |
|
59 | - Plugin $authPlugin, |
|
60 | - callable $viewCallBack): Server { |
|
61 | - // Fire up server |
|
62 | - $objectTree = new ObjectTree(); |
|
63 | - $server = new Server($objectTree); |
|
64 | - // Set URL explicitly due to reverse-proxy situations |
|
65 | - $server->httpRequest->setUrl($requestUri); |
|
66 | - $server->setBaseUri($baseUri); |
|
67 | - |
|
68 | - // Load plugins |
|
69 | - $server->addPlugin(new MaintenancePlugin($this->config, $this->l10n)); |
|
70 | - $server->addPlugin(new BlockLegacyClientPlugin( |
|
71 | - $this->config, |
|
72 | - \OCP\Server::get(ThemingDefaults::class), |
|
73 | - )); |
|
74 | - $server->addPlugin(new AnonymousOptionsPlugin()); |
|
75 | - $server->addPlugin($authPlugin); |
|
76 | - // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / |
|
77 | - $server->addPlugin(new DummyGetResponsePlugin()); |
|
78 | - $server->addPlugin(new ExceptionLoggerPlugin('webdav', $this->logger)); |
|
79 | - $server->addPlugin(new LockPlugin()); |
|
80 | - |
|
81 | - $server->addPlugin(new RequestIdHeaderPlugin($this->request)); |
|
82 | - |
|
83 | - $server->addPlugin(new ZipFolderPlugin( |
|
84 | - $objectTree, |
|
85 | - $this->logger, |
|
86 | - $this->eventDispatcher, |
|
87 | - )); |
|
88 | - |
|
89 | - // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
90 | - // we do not provide locking we emulate it using a fake locking plugin. |
|
91 | - if ($this->request->isUserAgent([ |
|
92 | - '/WebDAVFS/', |
|
93 | - '/OneNote/', |
|
94 | - '/Microsoft-WebDAV-MiniRedir/', |
|
95 | - ])) { |
|
96 | - $server->addPlugin(new FakeLockerPlugin()); |
|
97 | - } |
|
98 | - |
|
99 | - if (BrowserErrorPagePlugin::isBrowserRequest($this->request)) { |
|
100 | - $server->addPlugin(new BrowserErrorPagePlugin()); |
|
101 | - } |
|
102 | - |
|
103 | - // wait with registering these until auth is handled and the filesystem is setup |
|
104 | - $server->on('beforeMethod:*', function () use ($server, $objectTree, $viewCallBack): void { |
|
105 | - // ensure the skeleton is copied |
|
106 | - $userFolder = \OC::$server->getUserFolder(); |
|
107 | - |
|
108 | - /** @var View $view */ |
|
109 | - $view = $viewCallBack($server); |
|
110 | - if ($userFolder instanceof Folder && $userFolder->getPath() === $view->getRoot()) { |
|
111 | - $rootInfo = $userFolder; |
|
112 | - } else { |
|
113 | - $rootInfo = $view->getFileInfo(''); |
|
114 | - } |
|
115 | - |
|
116 | - // Create Nextcloud Dir |
|
117 | - if ($rootInfo->getType() === 'dir') { |
|
118 | - $root = new Directory($view, $rootInfo, $objectTree); |
|
119 | - } else { |
|
120 | - $root = new File($view, $rootInfo); |
|
121 | - } |
|
122 | - $objectTree->init($root, $view, $this->mountManager); |
|
123 | - |
|
124 | - $server->addPlugin( |
|
125 | - new FilesPlugin( |
|
126 | - $objectTree, |
|
127 | - $this->config, |
|
128 | - $this->request, |
|
129 | - $this->previewManager, |
|
130 | - $this->userSession, |
|
131 | - \OCP\Server::get(IFilenameValidator::class), |
|
132 | - \OCP\Server::get(IAccountManager::class), |
|
133 | - false, |
|
134 | - !$this->config->getSystemValue('debug', false) |
|
135 | - ) |
|
136 | - ); |
|
137 | - $server->addPlugin(new QuotaPlugin($view, true)); |
|
138 | - $server->addPlugin(new ChecksumUpdatePlugin()); |
|
139 | - |
|
140 | - // Allow view-only plugin for webdav requests |
|
141 | - $server->addPlugin(new ViewOnlyPlugin( |
|
142 | - $userFolder, |
|
143 | - )); |
|
144 | - |
|
145 | - if ($this->userSession->isLoggedIn()) { |
|
146 | - $server->addPlugin(new TagsPlugin($objectTree, $this->tagManager, $this->eventDispatcher, $this->userSession)); |
|
147 | - $server->addPlugin(new SharesPlugin( |
|
148 | - $objectTree, |
|
149 | - $this->userSession, |
|
150 | - $userFolder, |
|
151 | - \OCP\Server::get(\OCP\Share\IManager::class) |
|
152 | - )); |
|
153 | - $server->addPlugin(new CommentPropertiesPlugin(\OCP\Server::get(ICommentsManager::class), $this->userSession)); |
|
154 | - $server->addPlugin(new FilesReportPlugin( |
|
155 | - $objectTree, |
|
156 | - $view, |
|
157 | - \OCP\Server::get(ISystemTagManager::class), |
|
158 | - \OCP\Server::get(ISystemTagObjectMapper::class), |
|
159 | - \OCP\Server::get(ITagManager::class), |
|
160 | - $this->userSession, |
|
161 | - \OCP\Server::get(IGroupManager::class), |
|
162 | - $userFolder, |
|
163 | - \OCP\Server::get(IAppManager::class) |
|
164 | - )); |
|
165 | - // custom properties plugin must be the last one |
|
166 | - $server->addPlugin( |
|
167 | - new \Sabre\DAV\PropertyStorage\Plugin( |
|
168 | - new CustomPropertiesBackend( |
|
169 | - $server, |
|
170 | - $objectTree, |
|
171 | - $this->databaseConnection, |
|
172 | - $this->userSession->getUser(), |
|
173 | - \OCP\Server::get(DefaultCalendarValidator::class), |
|
174 | - ) |
|
175 | - ) |
|
176 | - ); |
|
177 | - } |
|
178 | - $server->addPlugin(new CopyEtagHeaderPlugin()); |
|
179 | - |
|
180 | - // Load dav plugins from apps |
|
181 | - $event = new SabrePluginEvent($server); |
|
182 | - $this->eventDispatcher->dispatchTyped($event); |
|
183 | - $pluginManager = new PluginManager( |
|
184 | - \OC::$server, |
|
185 | - \OCP\Server::get(IAppManager::class) |
|
186 | - ); |
|
187 | - foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
188 | - $server->addPlugin($appPlugin); |
|
189 | - } |
|
190 | - }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request |
|
191 | - return $server; |
|
192 | - } |
|
40 | + public function __construct( |
|
41 | + private IConfig $config, |
|
42 | + private LoggerInterface $logger, |
|
43 | + private IDBConnection $databaseConnection, |
|
44 | + private IUserSession $userSession, |
|
45 | + private IMountManager $mountManager, |
|
46 | + private ITagManager $tagManager, |
|
47 | + private IRequest $request, |
|
48 | + private IPreview $previewManager, |
|
49 | + private IEventDispatcher $eventDispatcher, |
|
50 | + private IL10N $l10n, |
|
51 | + ) { |
|
52 | + } |
|
53 | + |
|
54 | + /** |
|
55 | + * @param callable $viewCallBack callback that should return the view for the dav endpoint |
|
56 | + */ |
|
57 | + public function createServer(string $baseUri, |
|
58 | + string $requestUri, |
|
59 | + Plugin $authPlugin, |
|
60 | + callable $viewCallBack): Server { |
|
61 | + // Fire up server |
|
62 | + $objectTree = new ObjectTree(); |
|
63 | + $server = new Server($objectTree); |
|
64 | + // Set URL explicitly due to reverse-proxy situations |
|
65 | + $server->httpRequest->setUrl($requestUri); |
|
66 | + $server->setBaseUri($baseUri); |
|
67 | + |
|
68 | + // Load plugins |
|
69 | + $server->addPlugin(new MaintenancePlugin($this->config, $this->l10n)); |
|
70 | + $server->addPlugin(new BlockLegacyClientPlugin( |
|
71 | + $this->config, |
|
72 | + \OCP\Server::get(ThemingDefaults::class), |
|
73 | + )); |
|
74 | + $server->addPlugin(new AnonymousOptionsPlugin()); |
|
75 | + $server->addPlugin($authPlugin); |
|
76 | + // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / |
|
77 | + $server->addPlugin(new DummyGetResponsePlugin()); |
|
78 | + $server->addPlugin(new ExceptionLoggerPlugin('webdav', $this->logger)); |
|
79 | + $server->addPlugin(new LockPlugin()); |
|
80 | + |
|
81 | + $server->addPlugin(new RequestIdHeaderPlugin($this->request)); |
|
82 | + |
|
83 | + $server->addPlugin(new ZipFolderPlugin( |
|
84 | + $objectTree, |
|
85 | + $this->logger, |
|
86 | + $this->eventDispatcher, |
|
87 | + )); |
|
88 | + |
|
89 | + // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
90 | + // we do not provide locking we emulate it using a fake locking plugin. |
|
91 | + if ($this->request->isUserAgent([ |
|
92 | + '/WebDAVFS/', |
|
93 | + '/OneNote/', |
|
94 | + '/Microsoft-WebDAV-MiniRedir/', |
|
95 | + ])) { |
|
96 | + $server->addPlugin(new FakeLockerPlugin()); |
|
97 | + } |
|
98 | + |
|
99 | + if (BrowserErrorPagePlugin::isBrowserRequest($this->request)) { |
|
100 | + $server->addPlugin(new BrowserErrorPagePlugin()); |
|
101 | + } |
|
102 | + |
|
103 | + // wait with registering these until auth is handled and the filesystem is setup |
|
104 | + $server->on('beforeMethod:*', function () use ($server, $objectTree, $viewCallBack): void { |
|
105 | + // ensure the skeleton is copied |
|
106 | + $userFolder = \OC::$server->getUserFolder(); |
|
107 | + |
|
108 | + /** @var View $view */ |
|
109 | + $view = $viewCallBack($server); |
|
110 | + if ($userFolder instanceof Folder && $userFolder->getPath() === $view->getRoot()) { |
|
111 | + $rootInfo = $userFolder; |
|
112 | + } else { |
|
113 | + $rootInfo = $view->getFileInfo(''); |
|
114 | + } |
|
115 | + |
|
116 | + // Create Nextcloud Dir |
|
117 | + if ($rootInfo->getType() === 'dir') { |
|
118 | + $root = new Directory($view, $rootInfo, $objectTree); |
|
119 | + } else { |
|
120 | + $root = new File($view, $rootInfo); |
|
121 | + } |
|
122 | + $objectTree->init($root, $view, $this->mountManager); |
|
123 | + |
|
124 | + $server->addPlugin( |
|
125 | + new FilesPlugin( |
|
126 | + $objectTree, |
|
127 | + $this->config, |
|
128 | + $this->request, |
|
129 | + $this->previewManager, |
|
130 | + $this->userSession, |
|
131 | + \OCP\Server::get(IFilenameValidator::class), |
|
132 | + \OCP\Server::get(IAccountManager::class), |
|
133 | + false, |
|
134 | + !$this->config->getSystemValue('debug', false) |
|
135 | + ) |
|
136 | + ); |
|
137 | + $server->addPlugin(new QuotaPlugin($view, true)); |
|
138 | + $server->addPlugin(new ChecksumUpdatePlugin()); |
|
139 | + |
|
140 | + // Allow view-only plugin for webdav requests |
|
141 | + $server->addPlugin(new ViewOnlyPlugin( |
|
142 | + $userFolder, |
|
143 | + )); |
|
144 | + |
|
145 | + if ($this->userSession->isLoggedIn()) { |
|
146 | + $server->addPlugin(new TagsPlugin($objectTree, $this->tagManager, $this->eventDispatcher, $this->userSession)); |
|
147 | + $server->addPlugin(new SharesPlugin( |
|
148 | + $objectTree, |
|
149 | + $this->userSession, |
|
150 | + $userFolder, |
|
151 | + \OCP\Server::get(\OCP\Share\IManager::class) |
|
152 | + )); |
|
153 | + $server->addPlugin(new CommentPropertiesPlugin(\OCP\Server::get(ICommentsManager::class), $this->userSession)); |
|
154 | + $server->addPlugin(new FilesReportPlugin( |
|
155 | + $objectTree, |
|
156 | + $view, |
|
157 | + \OCP\Server::get(ISystemTagManager::class), |
|
158 | + \OCP\Server::get(ISystemTagObjectMapper::class), |
|
159 | + \OCP\Server::get(ITagManager::class), |
|
160 | + $this->userSession, |
|
161 | + \OCP\Server::get(IGroupManager::class), |
|
162 | + $userFolder, |
|
163 | + \OCP\Server::get(IAppManager::class) |
|
164 | + )); |
|
165 | + // custom properties plugin must be the last one |
|
166 | + $server->addPlugin( |
|
167 | + new \Sabre\DAV\PropertyStorage\Plugin( |
|
168 | + new CustomPropertiesBackend( |
|
169 | + $server, |
|
170 | + $objectTree, |
|
171 | + $this->databaseConnection, |
|
172 | + $this->userSession->getUser(), |
|
173 | + \OCP\Server::get(DefaultCalendarValidator::class), |
|
174 | + ) |
|
175 | + ) |
|
176 | + ); |
|
177 | + } |
|
178 | + $server->addPlugin(new CopyEtagHeaderPlugin()); |
|
179 | + |
|
180 | + // Load dav plugins from apps |
|
181 | + $event = new SabrePluginEvent($server); |
|
182 | + $this->eventDispatcher->dispatchTyped($event); |
|
183 | + $pluginManager = new PluginManager( |
|
184 | + \OC::$server, |
|
185 | + \OCP\Server::get(IAppManager::class) |
|
186 | + ); |
|
187 | + foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
188 | + $server->addPlugin($appPlugin); |
|
189 | + } |
|
190 | + }, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request |
|
191 | + return $server; |
|
192 | + } |
|
193 | 193 | } |
@@ -39,696 +39,696 @@ |
||
39 | 39 | use Sabre\HTTP\ResponseInterface; |
40 | 40 | |
41 | 41 | class FilesPlugin extends ServerPlugin { |
42 | - // namespace |
|
43 | - public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
44 | - public const NS_NEXTCLOUD = 'http://nextcloud.org/ns'; |
|
45 | - public const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id'; |
|
46 | - public const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid'; |
|
47 | - public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions'; |
|
48 | - public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions'; |
|
49 | - public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions'; |
|
50 | - public const SHARE_ATTRIBUTES_PROPERTYNAME = '{http://nextcloud.org/ns}share-attributes'; |
|
51 | - public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL'; |
|
52 | - public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size'; |
|
53 | - public const GETETAG_PROPERTYNAME = '{DAV:}getetag'; |
|
54 | - public const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; |
|
55 | - public const CREATIONDATE_PROPERTYNAME = '{DAV:}creationdate'; |
|
56 | - public const DISPLAYNAME_PROPERTYNAME = '{DAV:}displayname'; |
|
57 | - public const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id'; |
|
58 | - public const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name'; |
|
59 | - public const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums'; |
|
60 | - public const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint'; |
|
61 | - public const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview'; |
|
62 | - public const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type'; |
|
63 | - public const MOUNT_ROOT_PROPERTYNAME = '{http://nextcloud.org/ns}is-mount-root'; |
|
64 | - public const IS_FEDERATED_PROPERTYNAME = '{http://nextcloud.org/ns}is-federated'; |
|
65 | - public const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag'; |
|
66 | - public const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time'; |
|
67 | - public const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time'; |
|
68 | - public const SHARE_NOTE = '{http://nextcloud.org/ns}note'; |
|
69 | - public const SHARE_HIDE_DOWNLOAD_PROPERTYNAME = '{http://nextcloud.org/ns}hide-download'; |
|
70 | - public const SUBFOLDER_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-folder-count'; |
|
71 | - public const SUBFILE_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-file-count'; |
|
72 | - public const FILE_METADATA_PREFIX = '{http://nextcloud.org/ns}metadata-'; |
|
73 | - public const HIDDEN_PROPERTYNAME = '{http://nextcloud.org/ns}hidden'; |
|
74 | - |
|
75 | - /** Reference to main server object */ |
|
76 | - private ?Server $server = null; |
|
77 | - |
|
78 | - /** |
|
79 | - * @param Tree $tree |
|
80 | - * @param IConfig $config |
|
81 | - * @param IRequest $request |
|
82 | - * @param IPreview $previewManager |
|
83 | - * @param IUserSession $userSession |
|
84 | - * @param bool $isPublic Whether this is public WebDAV. If true, some returned information will be stripped off. |
|
85 | - * @param bool $downloadAttachment |
|
86 | - * @return void |
|
87 | - */ |
|
88 | - public function __construct( |
|
89 | - private Tree $tree, |
|
90 | - private IConfig $config, |
|
91 | - private IRequest $request, |
|
92 | - private IPreview $previewManager, |
|
93 | - private IUserSession $userSession, |
|
94 | - private IFilenameValidator $validator, |
|
95 | - private IAccountManager $accountManager, |
|
96 | - private bool $isPublic = false, |
|
97 | - private bool $downloadAttachment = true, |
|
98 | - ) { |
|
99 | - } |
|
100 | - |
|
101 | - /** |
|
102 | - * This initializes the plugin. |
|
103 | - * |
|
104 | - * This function is called by \Sabre\DAV\Server, after |
|
105 | - * addPlugin is called. |
|
106 | - * |
|
107 | - * This method should set up the required event subscriptions. |
|
108 | - * |
|
109 | - * @return void |
|
110 | - */ |
|
111 | - public function initialize(Server $server) { |
|
112 | - $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; |
|
113 | - $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc'; |
|
114 | - $server->protectedProperties[] = self::FILEID_PROPERTYNAME; |
|
115 | - $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME; |
|
116 | - $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME; |
|
117 | - $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME; |
|
118 | - $server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME; |
|
119 | - $server->protectedProperties[] = self::SHARE_ATTRIBUTES_PROPERTYNAME; |
|
120 | - $server->protectedProperties[] = self::SIZE_PROPERTYNAME; |
|
121 | - $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME; |
|
122 | - $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME; |
|
123 | - $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME; |
|
124 | - $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME; |
|
125 | - $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME; |
|
126 | - $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME; |
|
127 | - $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME; |
|
128 | - $server->protectedProperties[] = self::IS_FEDERATED_PROPERTYNAME; |
|
129 | - $server->protectedProperties[] = self::SHARE_NOTE; |
|
130 | - |
|
131 | - // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH |
|
132 | - $allowedProperties = ['{DAV:}getetag']; |
|
133 | - $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties); |
|
134 | - |
|
135 | - $this->server = $server; |
|
136 | - $this->server->on('propFind', [$this, 'handleGetProperties']); |
|
137 | - $this->server->on('propPatch', [$this, 'handleUpdateProperties']); |
|
138 | - $this->server->on('afterBind', [$this, 'sendFileIdHeader']); |
|
139 | - $this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']); |
|
140 | - $this->server->on('afterMethod:GET', [$this,'httpGet']); |
|
141 | - $this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']); |
|
142 | - $this->server->on('afterResponse', function ($request, ResponseInterface $response): void { |
|
143 | - $body = $response->getBody(); |
|
144 | - if (is_resource($body)) { |
|
145 | - fclose($body); |
|
146 | - } |
|
147 | - }); |
|
148 | - $this->server->on('beforeMove', [$this, 'checkMove']); |
|
149 | - $this->server->on('beforeCopy', [$this, 'checkCopy']); |
|
150 | - } |
|
151 | - |
|
152 | - /** |
|
153 | - * Plugin that checks if a copy can actually be performed. |
|
154 | - * |
|
155 | - * @param string $source source path |
|
156 | - * @param string $target target path |
|
157 | - * @throws NotFound If the source does not exist |
|
158 | - * @throws InvalidPath If the target is invalid |
|
159 | - */ |
|
160 | - public function checkCopy($source, $target): void { |
|
161 | - $sourceNode = $this->tree->getNodeForPath($source); |
|
162 | - if (!$sourceNode instanceof Node) { |
|
163 | - return; |
|
164 | - } |
|
165 | - |
|
166 | - // Ensure source exists |
|
167 | - $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
|
168 | - if ($sourceNodeFileInfo === null) { |
|
169 | - throw new NotFound($source . ' does not exist'); |
|
170 | - } |
|
171 | - // Ensure the target name is valid |
|
172 | - try { |
|
173 | - [$targetPath, $targetName] = \Sabre\Uri\split($target); |
|
174 | - $this->validator->validateFilename($targetName); |
|
175 | - } catch (InvalidPathException $e) { |
|
176 | - throw new InvalidPath($e->getMessage(), false); |
|
177 | - } |
|
178 | - // Ensure the target path is valid |
|
179 | - $segments = array_slice(explode('/', $targetPath), 2); |
|
180 | - foreach ($segments as $segment) { |
|
181 | - if ($this->validator->isFilenameValid($segment) === false) { |
|
182 | - $l = \OCP\Server::get(IFactory::class)->get('dav'); |
|
183 | - throw new InvalidPath($l->t('Invalid target path')); |
|
184 | - } |
|
185 | - } |
|
186 | - } |
|
187 | - |
|
188 | - /** |
|
189 | - * Plugin that checks if a move can actually be performed. |
|
190 | - * |
|
191 | - * @param string $source source path |
|
192 | - * @param string $target target path |
|
193 | - * @throws Forbidden If the source is not deletable |
|
194 | - * @throws NotFound If the source does not exist |
|
195 | - * @throws InvalidPath If the target name is invalid |
|
196 | - */ |
|
197 | - public function checkMove(string $source, string $target): void { |
|
198 | - $sourceNode = $this->tree->getNodeForPath($source); |
|
199 | - if (!$sourceNode instanceof Node) { |
|
200 | - return; |
|
201 | - } |
|
202 | - |
|
203 | - // First check copyable (move only needs additional delete permission) |
|
204 | - $this->checkCopy($source, $target); |
|
205 | - |
|
206 | - // The source needs to be deletable for moving |
|
207 | - $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
|
208 | - if (!$sourceNodeFileInfo->isDeletable()) { |
|
209 | - throw new Forbidden($source . ' cannot be deleted'); |
|
210 | - } |
|
211 | - |
|
212 | - // The source is not allowed to be the parent of the target |
|
213 | - if (str_starts_with($source, $target . '/')) { |
|
214 | - throw new Forbidden($source . ' cannot be moved to it\'s parent'); |
|
215 | - } |
|
216 | - } |
|
217 | - |
|
218 | - /** |
|
219 | - * This sets a cookie to be able to recognize the start of the download |
|
220 | - * the content must not be longer than 32 characters and must only contain |
|
221 | - * alphanumeric characters |
|
222 | - * |
|
223 | - * @param RequestInterface $request |
|
224 | - * @param ResponseInterface $response |
|
225 | - */ |
|
226 | - public function handleDownloadToken(RequestInterface $request, ResponseInterface $response) { |
|
227 | - $queryParams = $request->getQueryParameters(); |
|
228 | - |
|
229 | - /** |
|
230 | - * this sets a cookie to be able to recognize the start of the download |
|
231 | - * the content must not be longer than 32 characters and must only contain |
|
232 | - * alphanumeric characters |
|
233 | - */ |
|
234 | - if (isset($queryParams['downloadStartSecret'])) { |
|
235 | - $token = $queryParams['downloadStartSecret']; |
|
236 | - if (!isset($token[32]) |
|
237 | - && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) { |
|
238 | - // FIXME: use $response->setHeader() instead |
|
239 | - setcookie('ocDownloadStarted', $token, time() + 20, '/'); |
|
240 | - } |
|
241 | - } |
|
242 | - } |
|
243 | - |
|
244 | - /** |
|
245 | - * Add headers to file download |
|
246 | - * |
|
247 | - * @param RequestInterface $request |
|
248 | - * @param ResponseInterface $response |
|
249 | - */ |
|
250 | - public function httpGet(RequestInterface $request, ResponseInterface $response) { |
|
251 | - // Only handle valid files |
|
252 | - $node = $this->tree->getNodeForPath($request->getPath()); |
|
253 | - if (!($node instanceof IFile)) { |
|
254 | - return; |
|
255 | - } |
|
256 | - |
|
257 | - // adds a 'Content-Disposition: attachment' header in case no disposition |
|
258 | - // header has been set before |
|
259 | - if ($this->downloadAttachment && |
|
260 | - $response->getHeader('Content-Disposition') === null) { |
|
261 | - $filename = $node->getName(); |
|
262 | - if ($this->request->isUserAgent( |
|
263 | - [ |
|
264 | - Request::USER_AGENT_IE, |
|
265 | - Request::USER_AGENT_ANDROID_MOBILE_CHROME, |
|
266 | - Request::USER_AGENT_FREEBOX, |
|
267 | - ])) { |
|
268 | - $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); |
|
269 | - } else { |
|
270 | - $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) |
|
271 | - . '; filename="' . rawurlencode($filename) . '"'); |
|
272 | - } |
|
273 | - } |
|
274 | - |
|
275 | - if ($node instanceof File) { |
|
276 | - //Add OC-Checksum header |
|
277 | - $checksum = $node->getChecksum(); |
|
278 | - if ($checksum !== null && $checksum !== '') { |
|
279 | - $response->addHeader('OC-Checksum', $checksum); |
|
280 | - } |
|
281 | - } |
|
282 | - $response->addHeader('X-Accel-Buffering', 'no'); |
|
283 | - } |
|
284 | - |
|
285 | - /** |
|
286 | - * Adds all ownCloud-specific properties |
|
287 | - * |
|
288 | - * @param PropFind $propFind |
|
289 | - * @param \Sabre\DAV\INode $node |
|
290 | - * @return void |
|
291 | - */ |
|
292 | - public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { |
|
293 | - $httpRequest = $this->server->httpRequest; |
|
294 | - |
|
295 | - if ($node instanceof Node) { |
|
296 | - /** |
|
297 | - * This was disabled, because it made dir listing throw an exception, |
|
298 | - * so users were unable to navigate into folders where one subitem |
|
299 | - * is blocked by the files_accesscontrol app, see: |
|
300 | - * https://github.com/nextcloud/files_accesscontrol/issues/65 |
|
301 | - * if (!$node->getFileInfo()->isReadable()) { |
|
302 | - * // avoid detecting files through this means |
|
303 | - * throw new NotFound(); |
|
304 | - * } |
|
305 | - */ |
|
306 | - |
|
307 | - $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) { |
|
308 | - return $node->getFileId(); |
|
309 | - }); |
|
310 | - |
|
311 | - $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) { |
|
312 | - return $node->getInternalFileId(); |
|
313 | - }); |
|
314 | - |
|
315 | - $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) { |
|
316 | - $perms = $node->getDavPermissions(); |
|
317 | - if ($this->isPublic) { |
|
318 | - // remove mount information |
|
319 | - $perms = str_replace(['S', 'M'], '', $perms); |
|
320 | - } |
|
321 | - return $perms; |
|
322 | - }); |
|
323 | - |
|
324 | - $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
325 | - $user = $this->userSession->getUser(); |
|
326 | - if ($user === null) { |
|
327 | - return null; |
|
328 | - } |
|
329 | - return $node->getSharePermissions( |
|
330 | - $user->getUID() |
|
331 | - ); |
|
332 | - }); |
|
333 | - |
|
334 | - $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest): ?string { |
|
335 | - $user = $this->userSession->getUser(); |
|
336 | - if ($user === null) { |
|
337 | - return null; |
|
338 | - } |
|
339 | - $ncPermissions = $node->getSharePermissions( |
|
340 | - $user->getUID() |
|
341 | - ); |
|
342 | - $ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions); |
|
343 | - return json_encode($ocmPermissions, JSON_THROW_ON_ERROR); |
|
344 | - }); |
|
345 | - |
|
346 | - $propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
347 | - return json_encode($node->getShareAttributes(), JSON_THROW_ON_ERROR); |
|
348 | - }); |
|
349 | - |
|
350 | - $propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node): string { |
|
351 | - return $node->getETag(); |
|
352 | - }); |
|
353 | - |
|
354 | - $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node): ?string { |
|
355 | - $owner = $node->getOwner(); |
|
356 | - if (!$owner) { |
|
357 | - return null; |
|
358 | - } else { |
|
359 | - return $owner->getUID(); |
|
360 | - } |
|
361 | - }); |
|
362 | - $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node): ?string { |
|
363 | - $owner = $node->getOwner(); |
|
364 | - if (!$owner) { |
|
365 | - return null; |
|
366 | - } |
|
367 | - |
|
368 | - // Get current user to see if we're in a public share or not |
|
369 | - $user = $this->userSession->getUser(); |
|
370 | - |
|
371 | - // If the user is logged in, we can return the display name |
|
372 | - if ($user !== null) { |
|
373 | - return $owner->getDisplayName(); |
|
374 | - } |
|
375 | - |
|
376 | - // Check if the user published their display name |
|
377 | - $ownerAccount = $this->accountManager->getAccount($owner); |
|
378 | - $ownerNameProperty = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME); |
|
379 | - |
|
380 | - // Since we are not logged in, we need to have at least the published scope |
|
381 | - if ($ownerNameProperty->getScope() === IAccountManager::SCOPE_PUBLISHED) { |
|
382 | - return $owner->getDisplayName(); |
|
383 | - } |
|
384 | - |
|
385 | - return null; |
|
386 | - }); |
|
387 | - |
|
388 | - $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { |
|
389 | - return json_encode($this->previewManager->isAvailable($node->getFileInfo()), JSON_THROW_ON_ERROR); |
|
390 | - }); |
|
391 | - $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node): int|float { |
|
392 | - return $node->getSize(); |
|
393 | - }); |
|
394 | - $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) { |
|
395 | - return $node->getFileInfo()->getMountPoint()->getMountType(); |
|
396 | - }); |
|
397 | - |
|
398 | - /** |
|
399 | - * This is a special property which is used to determine if a node |
|
400 | - * is a mount root or not, e.g. a shared folder. |
|
401 | - * If so, then the node can only be unshared and not deleted. |
|
402 | - * @see https://github.com/nextcloud/server/blob/cc75294eb6b16b916a342e69998935f89222619d/lib/private/Files/View.php#L696-L698 |
|
403 | - */ |
|
404 | - $propFind->handle(self::MOUNT_ROOT_PROPERTYNAME, function () use ($node) { |
|
405 | - return $node->getNode()->getInternalPath() === '' ? 'true' : 'false'; |
|
406 | - }); |
|
407 | - |
|
408 | - $propFind->handle(self::SHARE_NOTE, function () use ($node): ?string { |
|
409 | - $user = $this->userSession->getUser(); |
|
410 | - return $node->getNoteFromShare( |
|
411 | - $user?->getUID() |
|
412 | - ); |
|
413 | - }); |
|
414 | - |
|
415 | - $propFind->handle(self::SHARE_HIDE_DOWNLOAD_PROPERTYNAME, function () use ($node) { |
|
416 | - $storage = $node->getNode()->getStorage(); |
|
417 | - if ($storage->instanceOfStorage(ISharedStorage::class)) { |
|
418 | - /** @var ISharedStorage $storage */ |
|
419 | - return match($storage->getShare()->getHideDownload()) { |
|
420 | - true => 'true', |
|
421 | - false => 'false', |
|
422 | - }; |
|
423 | - } else { |
|
424 | - return null; |
|
425 | - } |
|
426 | - }); |
|
427 | - |
|
428 | - $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () { |
|
429 | - return $this->config->getSystemValue('data-fingerprint', ''); |
|
430 | - }); |
|
431 | - $propFind->handle(self::CREATIONDATE_PROPERTYNAME, function () use ($node) { |
|
432 | - return (new \DateTimeImmutable()) |
|
433 | - ->setTimestamp($node->getFileInfo()->getCreationTime()) |
|
434 | - ->format(\DateTimeInterface::ATOM); |
|
435 | - }); |
|
436 | - $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) { |
|
437 | - return $node->getFileInfo()->getCreationTime(); |
|
438 | - }); |
|
439 | - |
|
440 | - foreach ($node->getFileInfo()->getMetadata() as $metadataKey => $metadataValue) { |
|
441 | - $propFind->handle(self::FILE_METADATA_PREFIX . $metadataKey, $metadataValue); |
|
442 | - } |
|
443 | - |
|
444 | - $propFind->handle(self::HIDDEN_PROPERTYNAME, function () use ($node) { |
|
445 | - $isLivePhoto = isset($node->getFileInfo()->getMetadata()['files-live-photo']); |
|
446 | - $isMovFile = $node->getFileInfo()->getMimetype() === 'video/quicktime'; |
|
447 | - return ($isLivePhoto && $isMovFile) ? 'true' : 'false'; |
|
448 | - }); |
|
449 | - |
|
450 | - /** |
|
451 | - * Return file/folder name as displayname. The primary reason to |
|
452 | - * implement it this way is to avoid costly fallback to |
|
453 | - * CustomPropertiesBackend (esp. visible when querying all files |
|
454 | - * in a folder). |
|
455 | - */ |
|
456 | - $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) { |
|
457 | - return $node->getName(); |
|
458 | - }); |
|
459 | - |
|
460 | - $propFind->handle(self::IS_FEDERATED_PROPERTYNAME, function () use ($node) { |
|
461 | - return $node->getFileInfo()->getMountPoint() |
|
462 | - instanceof SharingExternalMount; |
|
463 | - }); |
|
464 | - } |
|
465 | - |
|
466 | - if ($node instanceof File) { |
|
467 | - $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) { |
|
468 | - try { |
|
469 | - $directDownloadUrl = $node->getDirectDownload(); |
|
470 | - if (isset($directDownloadUrl['url'])) { |
|
471 | - return $directDownloadUrl['url']; |
|
472 | - } |
|
473 | - } catch (StorageNotAvailableException $e) { |
|
474 | - return false; |
|
475 | - } catch (ForbiddenException $e) { |
|
476 | - return false; |
|
477 | - } |
|
478 | - return false; |
|
479 | - }); |
|
480 | - |
|
481 | - $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) { |
|
482 | - $checksum = $node->getChecksum(); |
|
483 | - if ($checksum === null || $checksum === '') { |
|
484 | - return null; |
|
485 | - } |
|
486 | - |
|
487 | - return new ChecksumList($checksum); |
|
488 | - }); |
|
489 | - |
|
490 | - $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) { |
|
491 | - return $node->getFileInfo()->getUploadTime(); |
|
492 | - }); |
|
493 | - } |
|
494 | - |
|
495 | - if ($node instanceof Directory) { |
|
496 | - $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) { |
|
497 | - return $node->getSize(); |
|
498 | - }); |
|
499 | - |
|
500 | - $requestProperties = $propFind->getRequestedProperties(); |
|
501 | - |
|
502 | - if (in_array(self::SUBFILE_COUNT_PROPERTYNAME, $requestProperties, true) |
|
503 | - || in_array(self::SUBFOLDER_COUNT_PROPERTYNAME, $requestProperties, true)) { |
|
504 | - $nbFiles = 0; |
|
505 | - $nbFolders = 0; |
|
506 | - foreach ($node->getChildren() as $child) { |
|
507 | - if ($child instanceof File) { |
|
508 | - $nbFiles++; |
|
509 | - } elseif ($child instanceof Directory) { |
|
510 | - $nbFolders++; |
|
511 | - } |
|
512 | - } |
|
513 | - |
|
514 | - $propFind->handle(self::SUBFILE_COUNT_PROPERTYNAME, $nbFiles); |
|
515 | - $propFind->handle(self::SUBFOLDER_COUNT_PROPERTYNAME, $nbFolders); |
|
516 | - } |
|
517 | - } |
|
518 | - } |
|
519 | - |
|
520 | - /** |
|
521 | - * translate Nextcloud permissions to OCM Permissions |
|
522 | - * |
|
523 | - * @param $ncPermissions |
|
524 | - * @return array |
|
525 | - */ |
|
526 | - protected function ncPermissions2ocmPermissions($ncPermissions) { |
|
527 | - $ocmPermissions = []; |
|
528 | - |
|
529 | - if ($ncPermissions & Constants::PERMISSION_SHARE) { |
|
530 | - $ocmPermissions[] = 'share'; |
|
531 | - } |
|
532 | - |
|
533 | - if ($ncPermissions & Constants::PERMISSION_READ) { |
|
534 | - $ocmPermissions[] = 'read'; |
|
535 | - } |
|
536 | - |
|
537 | - if (($ncPermissions & Constants::PERMISSION_CREATE) || |
|
538 | - ($ncPermissions & Constants::PERMISSION_UPDATE)) { |
|
539 | - $ocmPermissions[] = 'write'; |
|
540 | - } |
|
541 | - |
|
542 | - return $ocmPermissions; |
|
543 | - } |
|
544 | - |
|
545 | - /** |
|
546 | - * Update ownCloud-specific properties |
|
547 | - * |
|
548 | - * @param string $path |
|
549 | - * @param PropPatch $propPatch |
|
550 | - * |
|
551 | - * @return void |
|
552 | - */ |
|
553 | - public function handleUpdateProperties($path, PropPatch $propPatch) { |
|
554 | - $node = $this->tree->getNodeForPath($path); |
|
555 | - if (!($node instanceof Node)) { |
|
556 | - return; |
|
557 | - } |
|
558 | - |
|
559 | - $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) { |
|
560 | - if (empty($time)) { |
|
561 | - return false; |
|
562 | - } |
|
563 | - $node->touch($time); |
|
564 | - return true; |
|
565 | - }); |
|
566 | - $propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) { |
|
567 | - if (empty($etag)) { |
|
568 | - return false; |
|
569 | - } |
|
570 | - return $node->setEtag($etag) !== -1; |
|
571 | - }); |
|
572 | - $propPatch->handle(self::CREATIONDATE_PROPERTYNAME, function ($time) use ($node) { |
|
573 | - if (empty($time)) { |
|
574 | - return false; |
|
575 | - } |
|
576 | - $dateTime = new \DateTimeImmutable($time); |
|
577 | - $node->setCreationTime($dateTime->getTimestamp()); |
|
578 | - return true; |
|
579 | - }); |
|
580 | - $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) { |
|
581 | - if (empty($time)) { |
|
582 | - return false; |
|
583 | - } |
|
584 | - $node->setCreationTime((int)$time); |
|
585 | - return true; |
|
586 | - }); |
|
587 | - |
|
588 | - $this->handleUpdatePropertiesMetadata($propPatch, $node); |
|
589 | - |
|
590 | - /** |
|
591 | - * Disable modification of the displayname property for files and |
|
592 | - * folders via PROPPATCH. See PROPFIND for more information. |
|
593 | - */ |
|
594 | - $propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function ($displayName) { |
|
595 | - return 403; |
|
596 | - }); |
|
597 | - } |
|
598 | - |
|
599 | - |
|
600 | - /** |
|
601 | - * handle the update of metadata from PROPPATCH requests |
|
602 | - * |
|
603 | - * @param PropPatch $propPatch |
|
604 | - * @param Node $node |
|
605 | - * |
|
606 | - * @throws FilesMetadataException |
|
607 | - */ |
|
608 | - private function handleUpdatePropertiesMetadata(PropPatch $propPatch, Node $node): void { |
|
609 | - $userId = $this->userSession->getUser()?->getUID(); |
|
610 | - if ($userId === null) { |
|
611 | - return; |
|
612 | - } |
|
613 | - |
|
614 | - $accessRight = $this->getMetadataFileAccessRight($node, $userId); |
|
615 | - $filesMetadataManager = $this->initFilesMetadataManager(); |
|
616 | - $knownMetadata = $filesMetadataManager->getKnownMetadata(); |
|
617 | - |
|
618 | - foreach ($propPatch->getRemainingMutations() as $mutation) { |
|
619 | - if (!str_starts_with($mutation, self::FILE_METADATA_PREFIX)) { |
|
620 | - continue; |
|
621 | - } |
|
622 | - |
|
623 | - $propPatch->handle( |
|
624 | - $mutation, |
|
625 | - function (mixed $value) use ($accessRight, $knownMetadata, $node, $mutation, $filesMetadataManager): bool { |
|
626 | - /** @var FilesMetadata $metadata */ |
|
627 | - $metadata = $filesMetadataManager->getMetadata((int)$node->getFileId(), true); |
|
628 | - $metadata->setStorageId($node->getNode()->getStorage()->getCache()->getNumericStorageId()); |
|
629 | - $metadataKey = substr($mutation, strlen(self::FILE_METADATA_PREFIX)); |
|
630 | - |
|
631 | - // confirm metadata key is editable via PROPPATCH |
|
632 | - if ($knownMetadata->getEditPermission($metadataKey) < $accessRight) { |
|
633 | - throw new FilesMetadataException('you do not have enough rights to update \'' . $metadataKey . '\' on this node'); |
|
634 | - } |
|
635 | - |
|
636 | - if ($value === null) { |
|
637 | - $metadata->unset($metadataKey); |
|
638 | - $filesMetadataManager->saveMetadata($metadata); |
|
639 | - return true; |
|
640 | - } |
|
641 | - |
|
642 | - // If the metadata is unknown, it defaults to string. |
|
643 | - try { |
|
644 | - $type = $knownMetadata->getType($metadataKey); |
|
645 | - } catch (FilesMetadataNotFoundException) { |
|
646 | - $type = IMetadataValueWrapper::TYPE_STRING; |
|
647 | - } |
|
648 | - |
|
649 | - switch ($type) { |
|
650 | - case IMetadataValueWrapper::TYPE_STRING: |
|
651 | - $metadata->setString($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
652 | - break; |
|
653 | - case IMetadataValueWrapper::TYPE_INT: |
|
654 | - $metadata->setInt($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
655 | - break; |
|
656 | - case IMetadataValueWrapper::TYPE_FLOAT: |
|
657 | - $metadata->setFloat($metadataKey, $value); |
|
658 | - break; |
|
659 | - case IMetadataValueWrapper::TYPE_BOOL: |
|
660 | - $metadata->setBool($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
661 | - break; |
|
662 | - case IMetadataValueWrapper::TYPE_ARRAY: |
|
663 | - $metadata->setArray($metadataKey, $value); |
|
664 | - break; |
|
665 | - case IMetadataValueWrapper::TYPE_STRING_LIST: |
|
666 | - $metadata->setStringList($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
667 | - break; |
|
668 | - case IMetadataValueWrapper::TYPE_INT_LIST: |
|
669 | - $metadata->setIntList($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
670 | - break; |
|
671 | - } |
|
672 | - |
|
673 | - $filesMetadataManager->saveMetadata($metadata); |
|
674 | - |
|
675 | - return true; |
|
676 | - } |
|
677 | - ); |
|
678 | - } |
|
679 | - } |
|
680 | - |
|
681 | - /** |
|
682 | - * init default internal metadata |
|
683 | - * |
|
684 | - * @return IFilesMetadataManager |
|
685 | - */ |
|
686 | - private function initFilesMetadataManager(): IFilesMetadataManager { |
|
687 | - /** @var IFilesMetadataManager $manager */ |
|
688 | - $manager = \OCP\Server::get(IFilesMetadataManager::class); |
|
689 | - $manager->initMetadata('files-live-photo', IMetadataValueWrapper::TYPE_STRING, false, IMetadataValueWrapper::EDIT_REQ_OWNERSHIP); |
|
690 | - |
|
691 | - return $manager; |
|
692 | - } |
|
693 | - |
|
694 | - /** |
|
695 | - * based on owner and shares, returns the bottom limit to update related metadata |
|
696 | - * |
|
697 | - * @param Node $node |
|
698 | - * @param string $userId |
|
699 | - * |
|
700 | - * @return int |
|
701 | - */ |
|
702 | - private function getMetadataFileAccessRight(Node $node, string $userId): int { |
|
703 | - if ($node->getOwner()?->getUID() === $userId) { |
|
704 | - return IMetadataValueWrapper::EDIT_REQ_OWNERSHIP; |
|
705 | - } else { |
|
706 | - $filePermissions = $node->getSharePermissions($userId); |
|
707 | - if ($filePermissions & Constants::PERMISSION_UPDATE) { |
|
708 | - return IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION; |
|
709 | - } |
|
710 | - } |
|
711 | - |
|
712 | - return IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION; |
|
713 | - } |
|
714 | - |
|
715 | - /** |
|
716 | - * @param string $filePath |
|
717 | - * @param ?\Sabre\DAV\INode $node |
|
718 | - * @return void |
|
719 | - * @throws \Sabre\DAV\Exception\BadRequest |
|
720 | - */ |
|
721 | - public function sendFileIdHeader($filePath, ?\Sabre\DAV\INode $node = null) { |
|
722 | - // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder |
|
723 | - if (!$this->server->tree->nodeExists($filePath)) { |
|
724 | - return; |
|
725 | - } |
|
726 | - $node = $this->server->tree->getNodeForPath($filePath); |
|
727 | - if ($node instanceof Node) { |
|
728 | - $fileId = $node->getFileId(); |
|
729 | - if (!is_null($fileId)) { |
|
730 | - $this->server->httpResponse->setHeader('OC-FileId', $fileId); |
|
731 | - } |
|
732 | - } |
|
733 | - } |
|
42 | + // namespace |
|
43 | + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|
44 | + public const NS_NEXTCLOUD = 'http://nextcloud.org/ns'; |
|
45 | + public const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id'; |
|
46 | + public const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid'; |
|
47 | + public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions'; |
|
48 | + public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions'; |
|
49 | + public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions'; |
|
50 | + public const SHARE_ATTRIBUTES_PROPERTYNAME = '{http://nextcloud.org/ns}share-attributes'; |
|
51 | + public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL'; |
|
52 | + public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size'; |
|
53 | + public const GETETAG_PROPERTYNAME = '{DAV:}getetag'; |
|
54 | + public const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; |
|
55 | + public const CREATIONDATE_PROPERTYNAME = '{DAV:}creationdate'; |
|
56 | + public const DISPLAYNAME_PROPERTYNAME = '{DAV:}displayname'; |
|
57 | + public const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id'; |
|
58 | + public const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name'; |
|
59 | + public const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums'; |
|
60 | + public const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint'; |
|
61 | + public const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview'; |
|
62 | + public const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type'; |
|
63 | + public const MOUNT_ROOT_PROPERTYNAME = '{http://nextcloud.org/ns}is-mount-root'; |
|
64 | + public const IS_FEDERATED_PROPERTYNAME = '{http://nextcloud.org/ns}is-federated'; |
|
65 | + public const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag'; |
|
66 | + public const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time'; |
|
67 | + public const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time'; |
|
68 | + public const SHARE_NOTE = '{http://nextcloud.org/ns}note'; |
|
69 | + public const SHARE_HIDE_DOWNLOAD_PROPERTYNAME = '{http://nextcloud.org/ns}hide-download'; |
|
70 | + public const SUBFOLDER_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-folder-count'; |
|
71 | + public const SUBFILE_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-file-count'; |
|
72 | + public const FILE_METADATA_PREFIX = '{http://nextcloud.org/ns}metadata-'; |
|
73 | + public const HIDDEN_PROPERTYNAME = '{http://nextcloud.org/ns}hidden'; |
|
74 | + |
|
75 | + /** Reference to main server object */ |
|
76 | + private ?Server $server = null; |
|
77 | + |
|
78 | + /** |
|
79 | + * @param Tree $tree |
|
80 | + * @param IConfig $config |
|
81 | + * @param IRequest $request |
|
82 | + * @param IPreview $previewManager |
|
83 | + * @param IUserSession $userSession |
|
84 | + * @param bool $isPublic Whether this is public WebDAV. If true, some returned information will be stripped off. |
|
85 | + * @param bool $downloadAttachment |
|
86 | + * @return void |
|
87 | + */ |
|
88 | + public function __construct( |
|
89 | + private Tree $tree, |
|
90 | + private IConfig $config, |
|
91 | + private IRequest $request, |
|
92 | + private IPreview $previewManager, |
|
93 | + private IUserSession $userSession, |
|
94 | + private IFilenameValidator $validator, |
|
95 | + private IAccountManager $accountManager, |
|
96 | + private bool $isPublic = false, |
|
97 | + private bool $downloadAttachment = true, |
|
98 | + ) { |
|
99 | + } |
|
100 | + |
|
101 | + /** |
|
102 | + * This initializes the plugin. |
|
103 | + * |
|
104 | + * This function is called by \Sabre\DAV\Server, after |
|
105 | + * addPlugin is called. |
|
106 | + * |
|
107 | + * This method should set up the required event subscriptions. |
|
108 | + * |
|
109 | + * @return void |
|
110 | + */ |
|
111 | + public function initialize(Server $server) { |
|
112 | + $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; |
|
113 | + $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc'; |
|
114 | + $server->protectedProperties[] = self::FILEID_PROPERTYNAME; |
|
115 | + $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME; |
|
116 | + $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME; |
|
117 | + $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME; |
|
118 | + $server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME; |
|
119 | + $server->protectedProperties[] = self::SHARE_ATTRIBUTES_PROPERTYNAME; |
|
120 | + $server->protectedProperties[] = self::SIZE_PROPERTYNAME; |
|
121 | + $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME; |
|
122 | + $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME; |
|
123 | + $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME; |
|
124 | + $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME; |
|
125 | + $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME; |
|
126 | + $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME; |
|
127 | + $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME; |
|
128 | + $server->protectedProperties[] = self::IS_FEDERATED_PROPERTYNAME; |
|
129 | + $server->protectedProperties[] = self::SHARE_NOTE; |
|
130 | + |
|
131 | + // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH |
|
132 | + $allowedProperties = ['{DAV:}getetag']; |
|
133 | + $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties); |
|
134 | + |
|
135 | + $this->server = $server; |
|
136 | + $this->server->on('propFind', [$this, 'handleGetProperties']); |
|
137 | + $this->server->on('propPatch', [$this, 'handleUpdateProperties']); |
|
138 | + $this->server->on('afterBind', [$this, 'sendFileIdHeader']); |
|
139 | + $this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']); |
|
140 | + $this->server->on('afterMethod:GET', [$this,'httpGet']); |
|
141 | + $this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']); |
|
142 | + $this->server->on('afterResponse', function ($request, ResponseInterface $response): void { |
|
143 | + $body = $response->getBody(); |
|
144 | + if (is_resource($body)) { |
|
145 | + fclose($body); |
|
146 | + } |
|
147 | + }); |
|
148 | + $this->server->on('beforeMove', [$this, 'checkMove']); |
|
149 | + $this->server->on('beforeCopy', [$this, 'checkCopy']); |
|
150 | + } |
|
151 | + |
|
152 | + /** |
|
153 | + * Plugin that checks if a copy can actually be performed. |
|
154 | + * |
|
155 | + * @param string $source source path |
|
156 | + * @param string $target target path |
|
157 | + * @throws NotFound If the source does not exist |
|
158 | + * @throws InvalidPath If the target is invalid |
|
159 | + */ |
|
160 | + public function checkCopy($source, $target): void { |
|
161 | + $sourceNode = $this->tree->getNodeForPath($source); |
|
162 | + if (!$sourceNode instanceof Node) { |
|
163 | + return; |
|
164 | + } |
|
165 | + |
|
166 | + // Ensure source exists |
|
167 | + $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
|
168 | + if ($sourceNodeFileInfo === null) { |
|
169 | + throw new NotFound($source . ' does not exist'); |
|
170 | + } |
|
171 | + // Ensure the target name is valid |
|
172 | + try { |
|
173 | + [$targetPath, $targetName] = \Sabre\Uri\split($target); |
|
174 | + $this->validator->validateFilename($targetName); |
|
175 | + } catch (InvalidPathException $e) { |
|
176 | + throw new InvalidPath($e->getMessage(), false); |
|
177 | + } |
|
178 | + // Ensure the target path is valid |
|
179 | + $segments = array_slice(explode('/', $targetPath), 2); |
|
180 | + foreach ($segments as $segment) { |
|
181 | + if ($this->validator->isFilenameValid($segment) === false) { |
|
182 | + $l = \OCP\Server::get(IFactory::class)->get('dav'); |
|
183 | + throw new InvalidPath($l->t('Invalid target path')); |
|
184 | + } |
|
185 | + } |
|
186 | + } |
|
187 | + |
|
188 | + /** |
|
189 | + * Plugin that checks if a move can actually be performed. |
|
190 | + * |
|
191 | + * @param string $source source path |
|
192 | + * @param string $target target path |
|
193 | + * @throws Forbidden If the source is not deletable |
|
194 | + * @throws NotFound If the source does not exist |
|
195 | + * @throws InvalidPath If the target name is invalid |
|
196 | + */ |
|
197 | + public function checkMove(string $source, string $target): void { |
|
198 | + $sourceNode = $this->tree->getNodeForPath($source); |
|
199 | + if (!$sourceNode instanceof Node) { |
|
200 | + return; |
|
201 | + } |
|
202 | + |
|
203 | + // First check copyable (move only needs additional delete permission) |
|
204 | + $this->checkCopy($source, $target); |
|
205 | + |
|
206 | + // The source needs to be deletable for moving |
|
207 | + $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
|
208 | + if (!$sourceNodeFileInfo->isDeletable()) { |
|
209 | + throw new Forbidden($source . ' cannot be deleted'); |
|
210 | + } |
|
211 | + |
|
212 | + // The source is not allowed to be the parent of the target |
|
213 | + if (str_starts_with($source, $target . '/')) { |
|
214 | + throw new Forbidden($source . ' cannot be moved to it\'s parent'); |
|
215 | + } |
|
216 | + } |
|
217 | + |
|
218 | + /** |
|
219 | + * This sets a cookie to be able to recognize the start of the download |
|
220 | + * the content must not be longer than 32 characters and must only contain |
|
221 | + * alphanumeric characters |
|
222 | + * |
|
223 | + * @param RequestInterface $request |
|
224 | + * @param ResponseInterface $response |
|
225 | + */ |
|
226 | + public function handleDownloadToken(RequestInterface $request, ResponseInterface $response) { |
|
227 | + $queryParams = $request->getQueryParameters(); |
|
228 | + |
|
229 | + /** |
|
230 | + * this sets a cookie to be able to recognize the start of the download |
|
231 | + * the content must not be longer than 32 characters and must only contain |
|
232 | + * alphanumeric characters |
|
233 | + */ |
|
234 | + if (isset($queryParams['downloadStartSecret'])) { |
|
235 | + $token = $queryParams['downloadStartSecret']; |
|
236 | + if (!isset($token[32]) |
|
237 | + && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) { |
|
238 | + // FIXME: use $response->setHeader() instead |
|
239 | + setcookie('ocDownloadStarted', $token, time() + 20, '/'); |
|
240 | + } |
|
241 | + } |
|
242 | + } |
|
243 | + |
|
244 | + /** |
|
245 | + * Add headers to file download |
|
246 | + * |
|
247 | + * @param RequestInterface $request |
|
248 | + * @param ResponseInterface $response |
|
249 | + */ |
|
250 | + public function httpGet(RequestInterface $request, ResponseInterface $response) { |
|
251 | + // Only handle valid files |
|
252 | + $node = $this->tree->getNodeForPath($request->getPath()); |
|
253 | + if (!($node instanceof IFile)) { |
|
254 | + return; |
|
255 | + } |
|
256 | + |
|
257 | + // adds a 'Content-Disposition: attachment' header in case no disposition |
|
258 | + // header has been set before |
|
259 | + if ($this->downloadAttachment && |
|
260 | + $response->getHeader('Content-Disposition') === null) { |
|
261 | + $filename = $node->getName(); |
|
262 | + if ($this->request->isUserAgent( |
|
263 | + [ |
|
264 | + Request::USER_AGENT_IE, |
|
265 | + Request::USER_AGENT_ANDROID_MOBILE_CHROME, |
|
266 | + Request::USER_AGENT_FREEBOX, |
|
267 | + ])) { |
|
268 | + $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); |
|
269 | + } else { |
|
270 | + $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) |
|
271 | + . '; filename="' . rawurlencode($filename) . '"'); |
|
272 | + } |
|
273 | + } |
|
274 | + |
|
275 | + if ($node instanceof File) { |
|
276 | + //Add OC-Checksum header |
|
277 | + $checksum = $node->getChecksum(); |
|
278 | + if ($checksum !== null && $checksum !== '') { |
|
279 | + $response->addHeader('OC-Checksum', $checksum); |
|
280 | + } |
|
281 | + } |
|
282 | + $response->addHeader('X-Accel-Buffering', 'no'); |
|
283 | + } |
|
284 | + |
|
285 | + /** |
|
286 | + * Adds all ownCloud-specific properties |
|
287 | + * |
|
288 | + * @param PropFind $propFind |
|
289 | + * @param \Sabre\DAV\INode $node |
|
290 | + * @return void |
|
291 | + */ |
|
292 | + public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) { |
|
293 | + $httpRequest = $this->server->httpRequest; |
|
294 | + |
|
295 | + if ($node instanceof Node) { |
|
296 | + /** |
|
297 | + * This was disabled, because it made dir listing throw an exception, |
|
298 | + * so users were unable to navigate into folders where one subitem |
|
299 | + * is blocked by the files_accesscontrol app, see: |
|
300 | + * https://github.com/nextcloud/files_accesscontrol/issues/65 |
|
301 | + * if (!$node->getFileInfo()->isReadable()) { |
|
302 | + * // avoid detecting files through this means |
|
303 | + * throw new NotFound(); |
|
304 | + * } |
|
305 | + */ |
|
306 | + |
|
307 | + $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) { |
|
308 | + return $node->getFileId(); |
|
309 | + }); |
|
310 | + |
|
311 | + $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) { |
|
312 | + return $node->getInternalFileId(); |
|
313 | + }); |
|
314 | + |
|
315 | + $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) { |
|
316 | + $perms = $node->getDavPermissions(); |
|
317 | + if ($this->isPublic) { |
|
318 | + // remove mount information |
|
319 | + $perms = str_replace(['S', 'M'], '', $perms); |
|
320 | + } |
|
321 | + return $perms; |
|
322 | + }); |
|
323 | + |
|
324 | + $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
325 | + $user = $this->userSession->getUser(); |
|
326 | + if ($user === null) { |
|
327 | + return null; |
|
328 | + } |
|
329 | + return $node->getSharePermissions( |
|
330 | + $user->getUID() |
|
331 | + ); |
|
332 | + }); |
|
333 | + |
|
334 | + $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest): ?string { |
|
335 | + $user = $this->userSession->getUser(); |
|
336 | + if ($user === null) { |
|
337 | + return null; |
|
338 | + } |
|
339 | + $ncPermissions = $node->getSharePermissions( |
|
340 | + $user->getUID() |
|
341 | + ); |
|
342 | + $ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions); |
|
343 | + return json_encode($ocmPermissions, JSON_THROW_ON_ERROR); |
|
344 | + }); |
|
345 | + |
|
346 | + $propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
347 | + return json_encode($node->getShareAttributes(), JSON_THROW_ON_ERROR); |
|
348 | + }); |
|
349 | + |
|
350 | + $propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node): string { |
|
351 | + return $node->getETag(); |
|
352 | + }); |
|
353 | + |
|
354 | + $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node): ?string { |
|
355 | + $owner = $node->getOwner(); |
|
356 | + if (!$owner) { |
|
357 | + return null; |
|
358 | + } else { |
|
359 | + return $owner->getUID(); |
|
360 | + } |
|
361 | + }); |
|
362 | + $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node): ?string { |
|
363 | + $owner = $node->getOwner(); |
|
364 | + if (!$owner) { |
|
365 | + return null; |
|
366 | + } |
|
367 | + |
|
368 | + // Get current user to see if we're in a public share or not |
|
369 | + $user = $this->userSession->getUser(); |
|
370 | + |
|
371 | + // If the user is logged in, we can return the display name |
|
372 | + if ($user !== null) { |
|
373 | + return $owner->getDisplayName(); |
|
374 | + } |
|
375 | + |
|
376 | + // Check if the user published their display name |
|
377 | + $ownerAccount = $this->accountManager->getAccount($owner); |
|
378 | + $ownerNameProperty = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME); |
|
379 | + |
|
380 | + // Since we are not logged in, we need to have at least the published scope |
|
381 | + if ($ownerNameProperty->getScope() === IAccountManager::SCOPE_PUBLISHED) { |
|
382 | + return $owner->getDisplayName(); |
|
383 | + } |
|
384 | + |
|
385 | + return null; |
|
386 | + }); |
|
387 | + |
|
388 | + $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { |
|
389 | + return json_encode($this->previewManager->isAvailable($node->getFileInfo()), JSON_THROW_ON_ERROR); |
|
390 | + }); |
|
391 | + $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node): int|float { |
|
392 | + return $node->getSize(); |
|
393 | + }); |
|
394 | + $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) { |
|
395 | + return $node->getFileInfo()->getMountPoint()->getMountType(); |
|
396 | + }); |
|
397 | + |
|
398 | + /** |
|
399 | + * This is a special property which is used to determine if a node |
|
400 | + * is a mount root or not, e.g. a shared folder. |
|
401 | + * If so, then the node can only be unshared and not deleted. |
|
402 | + * @see https://github.com/nextcloud/server/blob/cc75294eb6b16b916a342e69998935f89222619d/lib/private/Files/View.php#L696-L698 |
|
403 | + */ |
|
404 | + $propFind->handle(self::MOUNT_ROOT_PROPERTYNAME, function () use ($node) { |
|
405 | + return $node->getNode()->getInternalPath() === '' ? 'true' : 'false'; |
|
406 | + }); |
|
407 | + |
|
408 | + $propFind->handle(self::SHARE_NOTE, function () use ($node): ?string { |
|
409 | + $user = $this->userSession->getUser(); |
|
410 | + return $node->getNoteFromShare( |
|
411 | + $user?->getUID() |
|
412 | + ); |
|
413 | + }); |
|
414 | + |
|
415 | + $propFind->handle(self::SHARE_HIDE_DOWNLOAD_PROPERTYNAME, function () use ($node) { |
|
416 | + $storage = $node->getNode()->getStorage(); |
|
417 | + if ($storage->instanceOfStorage(ISharedStorage::class)) { |
|
418 | + /** @var ISharedStorage $storage */ |
|
419 | + return match($storage->getShare()->getHideDownload()) { |
|
420 | + true => 'true', |
|
421 | + false => 'false', |
|
422 | + }; |
|
423 | + } else { |
|
424 | + return null; |
|
425 | + } |
|
426 | + }); |
|
427 | + |
|
428 | + $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () { |
|
429 | + return $this->config->getSystemValue('data-fingerprint', ''); |
|
430 | + }); |
|
431 | + $propFind->handle(self::CREATIONDATE_PROPERTYNAME, function () use ($node) { |
|
432 | + return (new \DateTimeImmutable()) |
|
433 | + ->setTimestamp($node->getFileInfo()->getCreationTime()) |
|
434 | + ->format(\DateTimeInterface::ATOM); |
|
435 | + }); |
|
436 | + $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) { |
|
437 | + return $node->getFileInfo()->getCreationTime(); |
|
438 | + }); |
|
439 | + |
|
440 | + foreach ($node->getFileInfo()->getMetadata() as $metadataKey => $metadataValue) { |
|
441 | + $propFind->handle(self::FILE_METADATA_PREFIX . $metadataKey, $metadataValue); |
|
442 | + } |
|
443 | + |
|
444 | + $propFind->handle(self::HIDDEN_PROPERTYNAME, function () use ($node) { |
|
445 | + $isLivePhoto = isset($node->getFileInfo()->getMetadata()['files-live-photo']); |
|
446 | + $isMovFile = $node->getFileInfo()->getMimetype() === 'video/quicktime'; |
|
447 | + return ($isLivePhoto && $isMovFile) ? 'true' : 'false'; |
|
448 | + }); |
|
449 | + |
|
450 | + /** |
|
451 | + * Return file/folder name as displayname. The primary reason to |
|
452 | + * implement it this way is to avoid costly fallback to |
|
453 | + * CustomPropertiesBackend (esp. visible when querying all files |
|
454 | + * in a folder). |
|
455 | + */ |
|
456 | + $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) { |
|
457 | + return $node->getName(); |
|
458 | + }); |
|
459 | + |
|
460 | + $propFind->handle(self::IS_FEDERATED_PROPERTYNAME, function () use ($node) { |
|
461 | + return $node->getFileInfo()->getMountPoint() |
|
462 | + instanceof SharingExternalMount; |
|
463 | + }); |
|
464 | + } |
|
465 | + |
|
466 | + if ($node instanceof File) { |
|
467 | + $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) { |
|
468 | + try { |
|
469 | + $directDownloadUrl = $node->getDirectDownload(); |
|
470 | + if (isset($directDownloadUrl['url'])) { |
|
471 | + return $directDownloadUrl['url']; |
|
472 | + } |
|
473 | + } catch (StorageNotAvailableException $e) { |
|
474 | + return false; |
|
475 | + } catch (ForbiddenException $e) { |
|
476 | + return false; |
|
477 | + } |
|
478 | + return false; |
|
479 | + }); |
|
480 | + |
|
481 | + $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) { |
|
482 | + $checksum = $node->getChecksum(); |
|
483 | + if ($checksum === null || $checksum === '') { |
|
484 | + return null; |
|
485 | + } |
|
486 | + |
|
487 | + return new ChecksumList($checksum); |
|
488 | + }); |
|
489 | + |
|
490 | + $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) { |
|
491 | + return $node->getFileInfo()->getUploadTime(); |
|
492 | + }); |
|
493 | + } |
|
494 | + |
|
495 | + if ($node instanceof Directory) { |
|
496 | + $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) { |
|
497 | + return $node->getSize(); |
|
498 | + }); |
|
499 | + |
|
500 | + $requestProperties = $propFind->getRequestedProperties(); |
|
501 | + |
|
502 | + if (in_array(self::SUBFILE_COUNT_PROPERTYNAME, $requestProperties, true) |
|
503 | + || in_array(self::SUBFOLDER_COUNT_PROPERTYNAME, $requestProperties, true)) { |
|
504 | + $nbFiles = 0; |
|
505 | + $nbFolders = 0; |
|
506 | + foreach ($node->getChildren() as $child) { |
|
507 | + if ($child instanceof File) { |
|
508 | + $nbFiles++; |
|
509 | + } elseif ($child instanceof Directory) { |
|
510 | + $nbFolders++; |
|
511 | + } |
|
512 | + } |
|
513 | + |
|
514 | + $propFind->handle(self::SUBFILE_COUNT_PROPERTYNAME, $nbFiles); |
|
515 | + $propFind->handle(self::SUBFOLDER_COUNT_PROPERTYNAME, $nbFolders); |
|
516 | + } |
|
517 | + } |
|
518 | + } |
|
519 | + |
|
520 | + /** |
|
521 | + * translate Nextcloud permissions to OCM Permissions |
|
522 | + * |
|
523 | + * @param $ncPermissions |
|
524 | + * @return array |
|
525 | + */ |
|
526 | + protected function ncPermissions2ocmPermissions($ncPermissions) { |
|
527 | + $ocmPermissions = []; |
|
528 | + |
|
529 | + if ($ncPermissions & Constants::PERMISSION_SHARE) { |
|
530 | + $ocmPermissions[] = 'share'; |
|
531 | + } |
|
532 | + |
|
533 | + if ($ncPermissions & Constants::PERMISSION_READ) { |
|
534 | + $ocmPermissions[] = 'read'; |
|
535 | + } |
|
536 | + |
|
537 | + if (($ncPermissions & Constants::PERMISSION_CREATE) || |
|
538 | + ($ncPermissions & Constants::PERMISSION_UPDATE)) { |
|
539 | + $ocmPermissions[] = 'write'; |
|
540 | + } |
|
541 | + |
|
542 | + return $ocmPermissions; |
|
543 | + } |
|
544 | + |
|
545 | + /** |
|
546 | + * Update ownCloud-specific properties |
|
547 | + * |
|
548 | + * @param string $path |
|
549 | + * @param PropPatch $propPatch |
|
550 | + * |
|
551 | + * @return void |
|
552 | + */ |
|
553 | + public function handleUpdateProperties($path, PropPatch $propPatch) { |
|
554 | + $node = $this->tree->getNodeForPath($path); |
|
555 | + if (!($node instanceof Node)) { |
|
556 | + return; |
|
557 | + } |
|
558 | + |
|
559 | + $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) { |
|
560 | + if (empty($time)) { |
|
561 | + return false; |
|
562 | + } |
|
563 | + $node->touch($time); |
|
564 | + return true; |
|
565 | + }); |
|
566 | + $propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) { |
|
567 | + if (empty($etag)) { |
|
568 | + return false; |
|
569 | + } |
|
570 | + return $node->setEtag($etag) !== -1; |
|
571 | + }); |
|
572 | + $propPatch->handle(self::CREATIONDATE_PROPERTYNAME, function ($time) use ($node) { |
|
573 | + if (empty($time)) { |
|
574 | + return false; |
|
575 | + } |
|
576 | + $dateTime = new \DateTimeImmutable($time); |
|
577 | + $node->setCreationTime($dateTime->getTimestamp()); |
|
578 | + return true; |
|
579 | + }); |
|
580 | + $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) { |
|
581 | + if (empty($time)) { |
|
582 | + return false; |
|
583 | + } |
|
584 | + $node->setCreationTime((int)$time); |
|
585 | + return true; |
|
586 | + }); |
|
587 | + |
|
588 | + $this->handleUpdatePropertiesMetadata($propPatch, $node); |
|
589 | + |
|
590 | + /** |
|
591 | + * Disable modification of the displayname property for files and |
|
592 | + * folders via PROPPATCH. See PROPFIND for more information. |
|
593 | + */ |
|
594 | + $propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function ($displayName) { |
|
595 | + return 403; |
|
596 | + }); |
|
597 | + } |
|
598 | + |
|
599 | + |
|
600 | + /** |
|
601 | + * handle the update of metadata from PROPPATCH requests |
|
602 | + * |
|
603 | + * @param PropPatch $propPatch |
|
604 | + * @param Node $node |
|
605 | + * |
|
606 | + * @throws FilesMetadataException |
|
607 | + */ |
|
608 | + private function handleUpdatePropertiesMetadata(PropPatch $propPatch, Node $node): void { |
|
609 | + $userId = $this->userSession->getUser()?->getUID(); |
|
610 | + if ($userId === null) { |
|
611 | + return; |
|
612 | + } |
|
613 | + |
|
614 | + $accessRight = $this->getMetadataFileAccessRight($node, $userId); |
|
615 | + $filesMetadataManager = $this->initFilesMetadataManager(); |
|
616 | + $knownMetadata = $filesMetadataManager->getKnownMetadata(); |
|
617 | + |
|
618 | + foreach ($propPatch->getRemainingMutations() as $mutation) { |
|
619 | + if (!str_starts_with($mutation, self::FILE_METADATA_PREFIX)) { |
|
620 | + continue; |
|
621 | + } |
|
622 | + |
|
623 | + $propPatch->handle( |
|
624 | + $mutation, |
|
625 | + function (mixed $value) use ($accessRight, $knownMetadata, $node, $mutation, $filesMetadataManager): bool { |
|
626 | + /** @var FilesMetadata $metadata */ |
|
627 | + $metadata = $filesMetadataManager->getMetadata((int)$node->getFileId(), true); |
|
628 | + $metadata->setStorageId($node->getNode()->getStorage()->getCache()->getNumericStorageId()); |
|
629 | + $metadataKey = substr($mutation, strlen(self::FILE_METADATA_PREFIX)); |
|
630 | + |
|
631 | + // confirm metadata key is editable via PROPPATCH |
|
632 | + if ($knownMetadata->getEditPermission($metadataKey) < $accessRight) { |
|
633 | + throw new FilesMetadataException('you do not have enough rights to update \'' . $metadataKey . '\' on this node'); |
|
634 | + } |
|
635 | + |
|
636 | + if ($value === null) { |
|
637 | + $metadata->unset($metadataKey); |
|
638 | + $filesMetadataManager->saveMetadata($metadata); |
|
639 | + return true; |
|
640 | + } |
|
641 | + |
|
642 | + // If the metadata is unknown, it defaults to string. |
|
643 | + try { |
|
644 | + $type = $knownMetadata->getType($metadataKey); |
|
645 | + } catch (FilesMetadataNotFoundException) { |
|
646 | + $type = IMetadataValueWrapper::TYPE_STRING; |
|
647 | + } |
|
648 | + |
|
649 | + switch ($type) { |
|
650 | + case IMetadataValueWrapper::TYPE_STRING: |
|
651 | + $metadata->setString($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
652 | + break; |
|
653 | + case IMetadataValueWrapper::TYPE_INT: |
|
654 | + $metadata->setInt($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
655 | + break; |
|
656 | + case IMetadataValueWrapper::TYPE_FLOAT: |
|
657 | + $metadata->setFloat($metadataKey, $value); |
|
658 | + break; |
|
659 | + case IMetadataValueWrapper::TYPE_BOOL: |
|
660 | + $metadata->setBool($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
661 | + break; |
|
662 | + case IMetadataValueWrapper::TYPE_ARRAY: |
|
663 | + $metadata->setArray($metadataKey, $value); |
|
664 | + break; |
|
665 | + case IMetadataValueWrapper::TYPE_STRING_LIST: |
|
666 | + $metadata->setStringList($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
667 | + break; |
|
668 | + case IMetadataValueWrapper::TYPE_INT_LIST: |
|
669 | + $metadata->setIntList($metadataKey, $value, $knownMetadata->isIndex($metadataKey)); |
|
670 | + break; |
|
671 | + } |
|
672 | + |
|
673 | + $filesMetadataManager->saveMetadata($metadata); |
|
674 | + |
|
675 | + return true; |
|
676 | + } |
|
677 | + ); |
|
678 | + } |
|
679 | + } |
|
680 | + |
|
681 | + /** |
|
682 | + * init default internal metadata |
|
683 | + * |
|
684 | + * @return IFilesMetadataManager |
|
685 | + */ |
|
686 | + private function initFilesMetadataManager(): IFilesMetadataManager { |
|
687 | + /** @var IFilesMetadataManager $manager */ |
|
688 | + $manager = \OCP\Server::get(IFilesMetadataManager::class); |
|
689 | + $manager->initMetadata('files-live-photo', IMetadataValueWrapper::TYPE_STRING, false, IMetadataValueWrapper::EDIT_REQ_OWNERSHIP); |
|
690 | + |
|
691 | + return $manager; |
|
692 | + } |
|
693 | + |
|
694 | + /** |
|
695 | + * based on owner and shares, returns the bottom limit to update related metadata |
|
696 | + * |
|
697 | + * @param Node $node |
|
698 | + * @param string $userId |
|
699 | + * |
|
700 | + * @return int |
|
701 | + */ |
|
702 | + private function getMetadataFileAccessRight(Node $node, string $userId): int { |
|
703 | + if ($node->getOwner()?->getUID() === $userId) { |
|
704 | + return IMetadataValueWrapper::EDIT_REQ_OWNERSHIP; |
|
705 | + } else { |
|
706 | + $filePermissions = $node->getSharePermissions($userId); |
|
707 | + if ($filePermissions & Constants::PERMISSION_UPDATE) { |
|
708 | + return IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION; |
|
709 | + } |
|
710 | + } |
|
711 | + |
|
712 | + return IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION; |
|
713 | + } |
|
714 | + |
|
715 | + /** |
|
716 | + * @param string $filePath |
|
717 | + * @param ?\Sabre\DAV\INode $node |
|
718 | + * @return void |
|
719 | + * @throws \Sabre\DAV\Exception\BadRequest |
|
720 | + */ |
|
721 | + public function sendFileIdHeader($filePath, ?\Sabre\DAV\INode $node = null) { |
|
722 | + // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder |
|
723 | + if (!$this->server->tree->nodeExists($filePath)) { |
|
724 | + return; |
|
725 | + } |
|
726 | + $node = $this->server->tree->getNodeForPath($filePath); |
|
727 | + if ($node instanceof Node) { |
|
728 | + $fileId = $node->getFileId(); |
|
729 | + if (!is_null($fileId)) { |
|
730 | + $this->server->httpResponse->setHeader('OC-FileId', $fileId); |
|
731 | + } |
|
732 | + } |
|
733 | + } |
|
734 | 734 | } |
@@ -137,9 +137,9 @@ discard block |
||
137 | 137 | $this->server->on('propPatch', [$this, 'handleUpdateProperties']); |
138 | 138 | $this->server->on('afterBind', [$this, 'sendFileIdHeader']); |
139 | 139 | $this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']); |
140 | - $this->server->on('afterMethod:GET', [$this,'httpGet']); |
|
140 | + $this->server->on('afterMethod:GET', [$this, 'httpGet']); |
|
141 | 141 | $this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']); |
142 | - $this->server->on('afterResponse', function ($request, ResponseInterface $response): void { |
|
142 | + $this->server->on('afterResponse', function($request, ResponseInterface $response): void { |
|
143 | 143 | $body = $response->getBody(); |
144 | 144 | if (is_resource($body)) { |
145 | 145 | fclose($body); |
@@ -166,7 +166,7 @@ discard block |
||
166 | 166 | // Ensure source exists |
167 | 167 | $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
168 | 168 | if ($sourceNodeFileInfo === null) { |
169 | - throw new NotFound($source . ' does not exist'); |
|
169 | + throw new NotFound($source.' does not exist'); |
|
170 | 170 | } |
171 | 171 | // Ensure the target name is valid |
172 | 172 | try { |
@@ -206,12 +206,12 @@ discard block |
||
206 | 206 | // The source needs to be deletable for moving |
207 | 207 | $sourceNodeFileInfo = $sourceNode->getFileInfo(); |
208 | 208 | if (!$sourceNodeFileInfo->isDeletable()) { |
209 | - throw new Forbidden($source . ' cannot be deleted'); |
|
209 | + throw new Forbidden($source.' cannot be deleted'); |
|
210 | 210 | } |
211 | 211 | |
212 | 212 | // The source is not allowed to be the parent of the target |
213 | - if (str_starts_with($source, $target . '/')) { |
|
214 | - throw new Forbidden($source . ' cannot be moved to it\'s parent'); |
|
213 | + if (str_starts_with($source, $target.'/')) { |
|
214 | + throw new Forbidden($source.' cannot be moved to it\'s parent'); |
|
215 | 215 | } |
216 | 216 | } |
217 | 217 | |
@@ -265,10 +265,10 @@ discard block |
||
265 | 265 | Request::USER_AGENT_ANDROID_MOBILE_CHROME, |
266 | 266 | Request::USER_AGENT_FREEBOX, |
267 | 267 | ])) { |
268 | - $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); |
|
268 | + $response->addHeader('Content-Disposition', 'attachment; filename="'.rawurlencode($filename).'"'); |
|
269 | 269 | } else { |
270 | - $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) |
|
271 | - . '; filename="' . rawurlencode($filename) . '"'); |
|
270 | + $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\''.rawurlencode($filename) |
|
271 | + . '; filename="'.rawurlencode($filename).'"'); |
|
272 | 272 | } |
273 | 273 | } |
274 | 274 | |
@@ -304,15 +304,15 @@ discard block |
||
304 | 304 | * } |
305 | 305 | */ |
306 | 306 | |
307 | - $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) { |
|
307 | + $propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) { |
|
308 | 308 | return $node->getFileId(); |
309 | 309 | }); |
310 | 310 | |
311 | - $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) { |
|
311 | + $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) { |
|
312 | 312 | return $node->getInternalFileId(); |
313 | 313 | }); |
314 | 314 | |
315 | - $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) { |
|
315 | + $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) { |
|
316 | 316 | $perms = $node->getDavPermissions(); |
317 | 317 | if ($this->isPublic) { |
318 | 318 | // remove mount information |
@@ -321,7 +321,7 @@ discard block |
||
321 | 321 | return $perms; |
322 | 322 | }); |
323 | 323 | |
324 | - $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
324 | + $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) { |
|
325 | 325 | $user = $this->userSession->getUser(); |
326 | 326 | if ($user === null) { |
327 | 327 | return null; |
@@ -331,7 +331,7 @@ discard block |
||
331 | 331 | ); |
332 | 332 | }); |
333 | 333 | |
334 | - $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest): ?string { |
|
334 | + $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest): ?string { |
|
335 | 335 | $user = $this->userSession->getUser(); |
336 | 336 | if ($user === null) { |
337 | 337 | return null; |
@@ -343,15 +343,15 @@ discard block |
||
343 | 343 | return json_encode($ocmPermissions, JSON_THROW_ON_ERROR); |
344 | 344 | }); |
345 | 345 | |
346 | - $propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function () use ($node, $httpRequest) { |
|
346 | + $propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function() use ($node, $httpRequest) { |
|
347 | 347 | return json_encode($node->getShareAttributes(), JSON_THROW_ON_ERROR); |
348 | 348 | }); |
349 | 349 | |
350 | - $propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node): string { |
|
350 | + $propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node): string { |
|
351 | 351 | return $node->getETag(); |
352 | 352 | }); |
353 | 353 | |
354 | - $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node): ?string { |
|
354 | + $propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node): ?string { |
|
355 | 355 | $owner = $node->getOwner(); |
356 | 356 | if (!$owner) { |
357 | 357 | return null; |
@@ -359,7 +359,7 @@ discard block |
||
359 | 359 | return $owner->getUID(); |
360 | 360 | } |
361 | 361 | }); |
362 | - $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node): ?string { |
|
362 | + $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node): ?string { |
|
363 | 363 | $owner = $node->getOwner(); |
364 | 364 | if (!$owner) { |
365 | 365 | return null; |
@@ -385,13 +385,13 @@ discard block |
||
385 | 385 | return null; |
386 | 386 | }); |
387 | 387 | |
388 | - $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { |
|
388 | + $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function() use ($node) { |
|
389 | 389 | return json_encode($this->previewManager->isAvailable($node->getFileInfo()), JSON_THROW_ON_ERROR); |
390 | 390 | }); |
391 | - $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node): int|float { |
|
391 | + $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node): int | float { |
|
392 | 392 | return $node->getSize(); |
393 | 393 | }); |
394 | - $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) { |
|
394 | + $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function() use ($node) { |
|
395 | 395 | return $node->getFileInfo()->getMountPoint()->getMountType(); |
396 | 396 | }); |
397 | 397 | |
@@ -401,18 +401,18 @@ discard block |
||
401 | 401 | * If so, then the node can only be unshared and not deleted. |
402 | 402 | * @see https://github.com/nextcloud/server/blob/cc75294eb6b16b916a342e69998935f89222619d/lib/private/Files/View.php#L696-L698 |
403 | 403 | */ |
404 | - $propFind->handle(self::MOUNT_ROOT_PROPERTYNAME, function () use ($node) { |
|
404 | + $propFind->handle(self::MOUNT_ROOT_PROPERTYNAME, function() use ($node) { |
|
405 | 405 | return $node->getNode()->getInternalPath() === '' ? 'true' : 'false'; |
406 | 406 | }); |
407 | 407 | |
408 | - $propFind->handle(self::SHARE_NOTE, function () use ($node): ?string { |
|
408 | + $propFind->handle(self::SHARE_NOTE, function() use ($node): ?string { |
|
409 | 409 | $user = $this->userSession->getUser(); |
410 | 410 | return $node->getNoteFromShare( |
411 | 411 | $user?->getUID() |
412 | 412 | ); |
413 | 413 | }); |
414 | 414 | |
415 | - $propFind->handle(self::SHARE_HIDE_DOWNLOAD_PROPERTYNAME, function () use ($node) { |
|
415 | + $propFind->handle(self::SHARE_HIDE_DOWNLOAD_PROPERTYNAME, function() use ($node) { |
|
416 | 416 | $storage = $node->getNode()->getStorage(); |
417 | 417 | if ($storage->instanceOfStorage(ISharedStorage::class)) { |
418 | 418 | /** @var ISharedStorage $storage */ |
@@ -425,23 +425,23 @@ discard block |
||
425 | 425 | } |
426 | 426 | }); |
427 | 427 | |
428 | - $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () { |
|
428 | + $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() { |
|
429 | 429 | return $this->config->getSystemValue('data-fingerprint', ''); |
430 | 430 | }); |
431 | - $propFind->handle(self::CREATIONDATE_PROPERTYNAME, function () use ($node) { |
|
431 | + $propFind->handle(self::CREATIONDATE_PROPERTYNAME, function() use ($node) { |
|
432 | 432 | return (new \DateTimeImmutable()) |
433 | 433 | ->setTimestamp($node->getFileInfo()->getCreationTime()) |
434 | 434 | ->format(\DateTimeInterface::ATOM); |
435 | 435 | }); |
436 | - $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) { |
|
436 | + $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function() use ($node) { |
|
437 | 437 | return $node->getFileInfo()->getCreationTime(); |
438 | 438 | }); |
439 | 439 | |
440 | 440 | foreach ($node->getFileInfo()->getMetadata() as $metadataKey => $metadataValue) { |
441 | - $propFind->handle(self::FILE_METADATA_PREFIX . $metadataKey, $metadataValue); |
|
441 | + $propFind->handle(self::FILE_METADATA_PREFIX.$metadataKey, $metadataValue); |
|
442 | 442 | } |
443 | 443 | |
444 | - $propFind->handle(self::HIDDEN_PROPERTYNAME, function () use ($node) { |
|
444 | + $propFind->handle(self::HIDDEN_PROPERTYNAME, function() use ($node) { |
|
445 | 445 | $isLivePhoto = isset($node->getFileInfo()->getMetadata()['files-live-photo']); |
446 | 446 | $isMovFile = $node->getFileInfo()->getMimetype() === 'video/quicktime'; |
447 | 447 | return ($isLivePhoto && $isMovFile) ? 'true' : 'false'; |
@@ -453,18 +453,18 @@ discard block |
||
453 | 453 | * CustomPropertiesBackend (esp. visible when querying all files |
454 | 454 | * in a folder). |
455 | 455 | */ |
456 | - $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) { |
|
456 | + $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function() use ($node) { |
|
457 | 457 | return $node->getName(); |
458 | 458 | }); |
459 | 459 | |
460 | - $propFind->handle(self::IS_FEDERATED_PROPERTYNAME, function () use ($node) { |
|
460 | + $propFind->handle(self::IS_FEDERATED_PROPERTYNAME, function() use ($node) { |
|
461 | 461 | return $node->getFileInfo()->getMountPoint() |
462 | 462 | instanceof SharingExternalMount; |
463 | 463 | }); |
464 | 464 | } |
465 | 465 | |
466 | 466 | if ($node instanceof File) { |
467 | - $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) { |
|
467 | + $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) { |
|
468 | 468 | try { |
469 | 469 | $directDownloadUrl = $node->getDirectDownload(); |
470 | 470 | if (isset($directDownloadUrl['url'])) { |
@@ -478,7 +478,7 @@ discard block |
||
478 | 478 | return false; |
479 | 479 | }); |
480 | 480 | |
481 | - $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) { |
|
481 | + $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) { |
|
482 | 482 | $checksum = $node->getChecksum(); |
483 | 483 | if ($checksum === null || $checksum === '') { |
484 | 484 | return null; |
@@ -487,13 +487,13 @@ discard block |
||
487 | 487 | return new ChecksumList($checksum); |
488 | 488 | }); |
489 | 489 | |
490 | - $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) { |
|
490 | + $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function() use ($node) { |
|
491 | 491 | return $node->getFileInfo()->getUploadTime(); |
492 | 492 | }); |
493 | 493 | } |
494 | 494 | |
495 | 495 | if ($node instanceof Directory) { |
496 | - $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) { |
|
496 | + $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) { |
|
497 | 497 | return $node->getSize(); |
498 | 498 | }); |
499 | 499 | |
@@ -556,20 +556,20 @@ discard block |
||
556 | 556 | return; |
557 | 557 | } |
558 | 558 | |
559 | - $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) { |
|
559 | + $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($node) { |
|
560 | 560 | if (empty($time)) { |
561 | 561 | return false; |
562 | 562 | } |
563 | 563 | $node->touch($time); |
564 | 564 | return true; |
565 | 565 | }); |
566 | - $propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) { |
|
566 | + $propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($node) { |
|
567 | 567 | if (empty($etag)) { |
568 | 568 | return false; |
569 | 569 | } |
570 | 570 | return $node->setEtag($etag) !== -1; |
571 | 571 | }); |
572 | - $propPatch->handle(self::CREATIONDATE_PROPERTYNAME, function ($time) use ($node) { |
|
572 | + $propPatch->handle(self::CREATIONDATE_PROPERTYNAME, function($time) use ($node) { |
|
573 | 573 | if (empty($time)) { |
574 | 574 | return false; |
575 | 575 | } |
@@ -577,11 +577,11 @@ discard block |
||
577 | 577 | $node->setCreationTime($dateTime->getTimestamp()); |
578 | 578 | return true; |
579 | 579 | }); |
580 | - $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) { |
|
580 | + $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function($time) use ($node) { |
|
581 | 581 | if (empty($time)) { |
582 | 582 | return false; |
583 | 583 | } |
584 | - $node->setCreationTime((int)$time); |
|
584 | + $node->setCreationTime((int) $time); |
|
585 | 585 | return true; |
586 | 586 | }); |
587 | 587 | |
@@ -591,7 +591,7 @@ discard block |
||
591 | 591 | * Disable modification of the displayname property for files and |
592 | 592 | * folders via PROPPATCH. See PROPFIND for more information. |
593 | 593 | */ |
594 | - $propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function ($displayName) { |
|
594 | + $propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function($displayName) { |
|
595 | 595 | return 403; |
596 | 596 | }); |
597 | 597 | } |
@@ -622,15 +622,15 @@ discard block |
||
622 | 622 | |
623 | 623 | $propPatch->handle( |
624 | 624 | $mutation, |
625 | - function (mixed $value) use ($accessRight, $knownMetadata, $node, $mutation, $filesMetadataManager): bool { |
|
625 | + function(mixed $value) use ($accessRight, $knownMetadata, $node, $mutation, $filesMetadataManager): bool { |
|
626 | 626 | /** @var FilesMetadata $metadata */ |
627 | - $metadata = $filesMetadataManager->getMetadata((int)$node->getFileId(), true); |
|
627 | + $metadata = $filesMetadataManager->getMetadata((int) $node->getFileId(), true); |
|
628 | 628 | $metadata->setStorageId($node->getNode()->getStorage()->getCache()->getNumericStorageId()); |
629 | 629 | $metadataKey = substr($mutation, strlen(self::FILE_METADATA_PREFIX)); |
630 | 630 | |
631 | 631 | // confirm metadata key is editable via PROPPATCH |
632 | 632 | if ($knownMetadata->getEditPermission($metadataKey) < $accessRight) { |
633 | - throw new FilesMetadataException('you do not have enough rights to update \'' . $metadataKey . '\' on this node'); |
|
633 | + throw new FilesMetadataException('you do not have enough rights to update \''.$metadataKey.'\' on this node'); |
|
634 | 634 | } |
635 | 635 | |
636 | 636 | if ($value === null) { |