1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* ownCloud - Richdocuments App |
4
|
|
|
* |
5
|
|
|
* @author Victor Dubiniuk |
6
|
|
|
* @copyright 2014 Victor Dubiniuk [email protected] |
7
|
|
|
* |
8
|
|
|
* This file is licensed under the Affero General Public License version 3 or |
9
|
|
|
* later. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace OCA\Richdocuments\Controller; |
13
|
|
|
|
14
|
|
|
use \OCP\AppFramework\Controller; |
15
|
|
|
use \OCP\IRequest; |
16
|
|
|
use \OCP\IConfig; |
17
|
|
|
use \OCP\IL10N; |
18
|
|
|
use \OCP\AppFramework\Http\ContentSecurityPolicy; |
19
|
|
|
use \OCP\AppFramework\Http\JSONResponse; |
20
|
|
|
use \OCP\AppFramework\Http\TemplateResponse; |
21
|
|
|
|
22
|
|
|
use \OCA\Richdocuments\AppConfig; |
23
|
|
|
use \OCA\Richdocuments\Db; |
24
|
|
|
use \OCA\Richdocuments\Helper; |
25
|
|
|
use \OCA\Richdocuments\Storage; |
26
|
|
|
use \OCA\Richdocuments\Download; |
27
|
|
|
use \OCA\Richdocuments\DownloadResponse; |
28
|
|
|
use \OCA\Richdocuments\File; |
29
|
|
|
use \OCA\Richdocuments\Genesis; |
30
|
|
|
use \OC\Files\View; |
31
|
|
|
use \OCP\ICacheFactory; |
32
|
|
|
use \OCP\ILogger; |
33
|
|
|
|
34
|
|
|
class ResponseException extends \Exception { |
35
|
|
|
private $hint; |
36
|
|
|
|
37
|
|
|
public function __construct($description, $hint = '') { |
38
|
|
|
parent::__construct($description); |
39
|
|
|
$this->hint = $hint; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
public function getHint() { |
43
|
|
|
return $this->hint; |
44
|
|
|
} |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
class DocumentController extends Controller { |
48
|
|
|
|
49
|
|
|
private $uid; |
50
|
|
|
private $l10n; |
51
|
|
|
private $settings; |
52
|
|
|
private $appConfig; |
53
|
|
|
private $cache; |
54
|
|
|
private $logger; |
55
|
|
|
const ODT_TEMPLATE_PATH = '/assets/odttemplate.odt'; |
56
|
|
|
|
57
|
1 |
|
public function __construct($appName, IRequest $request, IConfig $settings, AppConfig $appConfig, IL10N $l10n, $uid, ICacheFactory $cache, ILogger $logger){ |
58
|
1 |
|
parent::__construct($appName, $request); |
59
|
1 |
|
$this->uid = $uid; |
60
|
1 |
|
$this->l10n = $l10n; |
61
|
1 |
|
$this->settings = $settings; |
62
|
1 |
|
$this->appConfig = $appConfig; |
63
|
1 |
|
$this->cache = $cache->create($appName); |
64
|
1 |
|
$this->logger = $logger; |
65
|
1 |
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @param \SimpleXMLElement $discovery |
|
|
|
|
69
|
|
|
* @param string $mimetype |
70
|
|
|
*/ |
71
|
|
|
private function getWopiSrcUrl($discovery_parsed, $mimetype) { |
72
|
|
|
if(is_null($discovery_parsed) || $discovery_parsed == false) { |
73
|
|
|
return null; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
$result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype)); |
77
|
|
|
if ($result && count($result) > 0) { |
78
|
|
|
return array( |
79
|
|
|
'urlsrc' => (string)$result[0]['urlsrc'], |
80
|
|
|
'action' => (string)$result[0]['name'] |
81
|
|
|
); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
return null; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Log the user with given $userid. |
89
|
|
|
* This function should only be used from public controller methods where no |
90
|
|
|
* existing session exists, for example, when loolwsd is directly calling a |
91
|
|
|
* public method with its own access token. After validating the access |
92
|
|
|
* token, and retrieving the correct user with help of access token, it can |
93
|
|
|
* be set as current user with help of this method. |
94
|
|
|
* |
95
|
|
|
* @param string $userid |
96
|
|
|
*/ |
97
|
|
|
private function loginUser($userid) { |
98
|
|
|
\OC_Util::tearDownFS(); |
99
|
|
|
|
100
|
|
|
$users = \OC::$server->getUserManager()->search($userid, 1, 0); |
101
|
|
|
if (count($users) > 0) { |
102
|
|
|
$user = array_shift($users); |
103
|
|
|
if (strcasecmp($user->getUID(), $userid) === 0) { |
104
|
|
|
// clear the existing sessions, if any |
105
|
|
|
\OC::$server->getSession()->close(); |
106
|
|
|
|
107
|
|
|
// initialize a dummy memory session |
108
|
|
|
$session = new \OC\Session\Memory(''); |
109
|
|
|
// wrap it |
110
|
|
|
$cryptoWrapper = \OC::$server->getSessionCryptoWrapper(); |
111
|
|
|
$session = $cryptoWrapper->wrapSession($session); |
112
|
|
|
// set our session |
113
|
|
|
\OC::$server->setSession($session); |
114
|
|
|
|
115
|
|
|
\OC::$server->getUserSession()->setUser($user); |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
\OC_Util::setupFS(); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Log out the current user |
124
|
|
|
* This is helpful when we are artifically logged in as someone |
125
|
|
|
*/ |
126
|
|
|
private function logoutUser() { |
127
|
|
|
\OC_Util::tearDownFS(); |
128
|
|
|
|
129
|
|
|
\OC::$server->getSession()->close(); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
private function responseError($message, $hint = ''){ |
133
|
|
|
$errors = array('errors' => array(array('error' => $message, 'hint' => $hint))); |
134
|
|
|
$response = new TemplateResponse('', 'error', $errors, 'error'); |
135
|
|
|
return $response; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Return the original wopi url or test wopi url |
140
|
|
|
*/ |
141
|
|
|
private function getWopiUrl($tester) { |
142
|
|
|
$wopiurl = ''; |
|
|
|
|
143
|
|
|
if ($tester) { |
144
|
|
|
$wopiurl = $this->appConfig->getAppValue('test_wopi_url'); |
145
|
|
|
} else { |
146
|
|
|
$wopiurl = $this->appConfig->getAppValue('wopi_url'); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
return $wopiurl; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Return true if the currently logged in user is a tester. |
154
|
|
|
* This depends on whether current user is the member of one of the groups |
155
|
|
|
* mentioned in settings (test_server_groups) |
156
|
|
|
*/ |
157
|
|
|
private function isTester() { |
158
|
|
|
$tester = false; |
159
|
|
|
|
160
|
|
|
$user = \OC::$server->getUserSession()->getUser()->getUID(); |
161
|
|
|
$testgroups = array_filter(explode('|', $this->appConfig->getAppValue('test_server_groups'))); |
162
|
|
|
\OC::$server->getLogger()->debug('Testgroups are {testgroups}', [ |
163
|
|
|
'app' => $this->appName, |
164
|
|
|
'testgroups' => $testgroups |
165
|
|
|
]); |
166
|
|
View Code Duplication |
foreach ($testgroups as $testgroup) { |
|
|
|
|
167
|
|
|
$test = \OC::$server->getGroupManager()->get($testgroup); |
168
|
|
|
if ($test !== null && sizeof($test->searchUsers($user)) > 0) { |
169
|
|
|
\OC::$server->getLogger()->debug('User {user} found in {group}', [ |
170
|
|
|
'app' => $this->appName, |
171
|
|
|
'user' => $user, |
172
|
|
|
'group' => $testgroup |
173
|
|
|
]); |
174
|
|
|
|
175
|
|
|
$tester = true; |
176
|
|
|
break; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return $tester; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** Return the content of discovery.xml - either from cache, or download it. |
184
|
|
|
*/ |
185
|
|
|
private function getDiscovery(){ |
186
|
|
|
\OC::$server->getLogger()->debug('getDiscovery(): Getting discovery.xml from the cache.'); |
187
|
|
|
|
188
|
|
|
$tester = $this->isTester(); |
189
|
|
|
$wopiRemote = $this->getWopiUrl($tester); |
190
|
|
|
$discoveryKey = 'discovery.xml'; |
191
|
|
|
if ($tester) { |
192
|
|
|
$discoveryKey = 'discovery.xml_test'; |
193
|
|
|
} |
194
|
|
|
// Provides access to information about the capabilities of a WOPI client |
195
|
|
|
// and the mechanisms for invoking those abilities through URIs. |
196
|
|
|
$wopiDiscovery = $wopiRemote . '/hosting/discovery'; |
197
|
|
|
|
198
|
|
|
// Read the memcached value (if the memcache is installed) |
199
|
|
|
$discovery = $this->cache->get($discoveryKey); |
200
|
|
|
|
201
|
|
|
if (is_null($discovery)) { |
202
|
|
|
$contact_admin = $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote)); |
203
|
|
|
|
204
|
|
|
try { |
205
|
|
|
$wopiClient = \OC::$server->getHTTPClientService()->newClient(); |
206
|
|
|
$discovery = $wopiClient->get($wopiDiscovery)->getBody(); |
207
|
|
|
} |
208
|
|
|
catch (\Exception $e) { |
209
|
|
|
$error_message = $e->getMessage(); |
210
|
|
|
if (preg_match('/^cURL error ([0-9]*):/', $error_message, $matches)) { |
211
|
|
|
$admin_check = $this->l10n->t('Please ask your administrator to check the Collabora Online server setting. The exact error message was: ') . $error_message; |
212
|
|
|
|
213
|
|
|
$curl_error = $matches[1]; |
214
|
|
|
switch ($curl_error) { |
215
|
|
|
case '1': |
216
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: The protocol specified in "%s" is not allowed.', array($wopiRemote)), $admin_check); |
217
|
|
|
case '3': |
218
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: Malformed URL "%s".', array($wopiRemote)), $admin_check); |
219
|
|
|
case '6': |
220
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: Cannot resolve the host "%s".', array($wopiRemote)), $admin_check); |
221
|
|
|
case '7': |
222
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: Cannot connect to the host "%s".', array($wopiRemote)), $admin_check); |
223
|
|
|
case '60': |
224
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: SSL certificate is not installed.'), $this->l10n->t('Please ask your administrator to add ca-chain.cert.pem to the ca-bundle.crt, for example "cat /etc/loolwsd/ca-chain.cert.pem >> <server-installation>/resources/config/ca-bundle.crt" . The exact error message was: ') . $error_message); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online unknown error: ') . $error_message, $contact_admin); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if (!$discovery) { |
231
|
|
|
throw new ResponseException($this->l10n->t('Collabora Online: Unable to read discovery.xml from "%s".', array($wopiRemote)), $contact_admin); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
\OC::$server->getLogger()->debug('Storing the discovery.xml under key ' . $discoveryKey . ' to the cache.'); |
235
|
|
|
$this->cache->set($discoveryKey, $discovery, 3600); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
return $discovery; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** Prepare document(s) structure |
242
|
|
|
*/ |
243
|
|
|
private function prepareDocuments($rawDocuments){ |
244
|
|
|
$discovery_parsed = null; |
245
|
|
|
try { |
246
|
|
|
$discovery = $this->getDiscovery(); |
247
|
|
|
|
248
|
|
|
$loadEntities = libxml_disable_entity_loader(true); |
249
|
|
|
$discovery_parsed = simplexml_load_string($discovery); |
250
|
|
|
libxml_disable_entity_loader($loadEntities); |
251
|
|
|
|
252
|
|
View Code Duplication |
if ($discovery_parsed === false) { |
|
|
|
|
253
|
|
|
$this->cache->remove('discovery.xml'); |
254
|
|
|
$wopiRemote = $this->getWopiUrl($this->isTester()); |
255
|
|
|
|
256
|
|
|
return array( |
257
|
|
|
'status' => 'error', |
258
|
|
|
'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)), |
259
|
|
|
'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote)) |
260
|
|
|
); |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
catch (ResponseException $e) { |
264
|
|
|
return array( |
265
|
|
|
'status' => 'error', |
266
|
|
|
'message' => $e->getMessage(), |
267
|
|
|
'hint' => $e->getHint() |
268
|
|
|
); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$fileIds = array(); |
272
|
|
|
$documents = array(); |
273
|
|
|
$lolang = strtolower(str_replace('_', '-', $this->settings->getUserValue($this->uid, 'core', 'lang', 'en'))); |
274
|
|
|
foreach ($rawDocuments as $key=>$document) { |
275
|
|
|
if (is_object($document)){ |
276
|
|
|
$documents[] = $document->getData(); |
277
|
|
|
} else { |
278
|
|
|
$documents[$key] = $document; |
279
|
|
|
} |
280
|
|
|
$documents[$key]['icon'] = preg_replace('/\.png$/', '.svg', \OCP\Template::mimetype_icon($document['mimetype'])); |
281
|
|
|
$documents[$key]['hasPreview'] = \OC::$server->getPreviewManager()->isMimeSupported($document['mimetype']); |
282
|
|
|
$ret = $this->getWopiSrcUrl($discovery_parsed, $document['mimetype']); |
283
|
|
|
$documents[$key]['urlsrc'] = $ret['urlsrc']; |
284
|
|
|
$documents[$key]['action'] = $ret['action']; |
285
|
|
|
$documents[$key]['lolang'] = $lolang; |
286
|
|
|
$fileIds[] = $document['fileid']; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
usort($documents, function($a, $b){ |
290
|
|
|
return @$b['mtime']-@$a['mtime']; |
291
|
|
|
}); |
292
|
|
|
|
293
|
|
|
$session = new Db\Session(); |
294
|
|
|
$sessions = $session->getCollectionBy('file_id', $fileIds); |
295
|
|
|
|
296
|
|
|
$members = array(); |
297
|
|
|
$member = new Db\Member(); |
298
|
|
|
foreach ($sessions as $session) { |
299
|
|
|
$members[$session['es_id']] = $member->getActiveCollection($session['es_id']); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
return array( |
303
|
|
|
'status' => 'success', 'documents' => $documents,'sessions' => $sessions,'members' => $members |
304
|
|
|
); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* @NoAdminRequired |
309
|
|
|
* @NoCSRFRequired |
310
|
|
|
*/ |
311
|
|
|
public function index(){ |
312
|
|
|
$wopiRemote = $this->getWopiUrl($this->isTester()); |
313
|
|
|
if (($parts = parse_url($wopiRemote)) && isset($parts['scheme']) && isset($parts['host'])) { |
314
|
|
|
$webSocketProtocol = "ws://"; |
315
|
|
|
if ($parts['scheme'] == "https") { |
316
|
|
|
$webSocketProtocol = "wss://"; |
317
|
|
|
} |
318
|
|
|
$webSocket = sprintf( |
319
|
|
|
"%s%s%s", |
320
|
|
|
$webSocketProtocol, |
321
|
|
|
$parts['host'], |
322
|
|
|
isset($parts['port']) ? ":" . $parts['port'] : ""); |
323
|
|
|
} |
324
|
|
|
else { |
325
|
|
|
return $this->responseError($this->l10n->t('Collabora Online: Invalid URL "%s".', array($wopiRemote)), $this->l10n->t('Please ask your administrator to check the Collabora Online server setting.')); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
$user = \OC::$server->getUserSession()->getUser(); |
329
|
|
|
$usergroups = array_filter(\OC::$server->getGroupManager()->getUserGroupIds($user)); |
330
|
|
|
$usergroups = join('|', $usergroups); |
331
|
|
|
\OC::$server->getLogger()->debug('User is in groups: {groups}', [ 'app' => $this->appName, 'groups' => $usergroups ]); |
332
|
|
|
|
333
|
|
|
\OC::$server->getNavigationManager()->setActiveEntry( 'richdocuments_index' ); |
334
|
|
|
$maxUploadFilesize = \OCP\Util::maxUploadFilesize("/"); |
335
|
|
|
$response = new TemplateResponse('richdocuments', 'documents', [ |
336
|
|
|
'enable_previews' => $this->settings->getSystemValue('enable_previews', true), |
337
|
|
|
'uploadMaxFilesize' => $maxUploadFilesize, |
338
|
|
|
'uploadMaxHumanFilesize' => \OCP\Util::humanFileSize($maxUploadFilesize), |
339
|
|
|
'allowShareWithLink' => $this->settings->getAppValue('core', 'shareapi_allow_links', 'yes'), |
340
|
|
|
'wopi_url' => $webSocket, |
341
|
|
|
'doc_format' => $this->appConfig->getAppValue('doc_format') |
342
|
|
|
]); |
343
|
|
|
|
344
|
|
|
$policy = new ContentSecurityPolicy(); |
345
|
|
|
$policy->addAllowedScriptDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote); |
346
|
|
|
/* frame-src is deprecated on Firefox, but Safari wants it! */ |
347
|
|
|
$policy->addAllowedFrameDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote . ' blob:'); |
348
|
|
|
$policy->addAllowedChildSrcDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote); |
349
|
|
|
$policy->addAllowedConnectDomain($webSocket); |
350
|
|
|
$policy->addAllowedImageDomain('*'); |
351
|
|
|
$policy->allowInlineScript(true); |
352
|
|
|
$policy->addAllowedFontDomain('data:'); |
353
|
|
|
$response->setContentSecurityPolicy($policy); |
354
|
|
|
|
355
|
|
|
return $response; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* @NoAdminRequired |
360
|
|
|
*/ |
361
|
|
|
public function create(){ |
362
|
|
|
$mimetype = $this->request->post['mimetype']; |
363
|
|
|
$filename = $this->request->post['filename']; |
364
|
|
|
$dir = $this->request->post['dir']; |
365
|
|
|
|
366
|
|
|
$view = new View('/' . $this->uid . '/files'); |
367
|
|
|
if (!$dir){ |
368
|
|
|
$dir = '/'; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
$basename = $this->l10n->t('New Document.odt'); |
372
|
|
|
switch ($mimetype) { |
373
|
|
|
case 'application/vnd.oasis.opendocument.spreadsheet': |
374
|
|
|
$basename = $this->l10n->t('New Spreadsheet.ods'); |
375
|
|
|
break; |
376
|
|
|
case 'application/vnd.oasis.opendocument.presentation': |
377
|
|
|
$basename = $this->l10n->t('New Presentation.odp'); |
378
|
|
|
break; |
379
|
|
|
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': |
380
|
|
|
$basename = $this->l10n->t('New Document.docx'); |
381
|
|
|
break; |
382
|
|
|
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': |
383
|
|
|
$basename = $this->l10n->t('New Spreadsheet.xlsx'); |
384
|
|
|
break; |
385
|
|
|
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': |
386
|
|
|
$basename = $this->l10n->t('New Presentation.pptx'); |
387
|
|
|
break; |
388
|
|
|
default: |
389
|
|
|
// to be safe |
390
|
|
|
$mimetype = 'application/vnd.oasis.opendocument.text'; |
391
|
|
|
break; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
if (!$filename){ |
395
|
|
|
$path = Helper::getNewFileName($view, $dir . '/' . $basename); |
396
|
|
|
} else { |
397
|
|
|
$path = $dir . '/' . $filename; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
$content = ''; |
401
|
|
|
if (class_exists('\OC\Files\Type\TemplateManager')){ |
402
|
|
|
$manager = \OC_Helper::getFileTemplateManager(); |
403
|
|
|
$content = $manager->getTemplate($mimetype); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
if (!$content){ |
407
|
|
|
$content = file_get_contents(dirname(__DIR__) . self::ODT_TEMPLATE_PATH); |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
$discovery_parsed = null; |
411
|
|
|
try { |
412
|
|
|
$discovery = $this->getDiscovery(); |
413
|
|
|
|
414
|
|
|
$loadEntities = libxml_disable_entity_loader(true); |
415
|
|
|
$discovery_parsed = simplexml_load_string($discovery); |
416
|
|
|
libxml_disable_entity_loader($loadEntities); |
417
|
|
|
|
418
|
|
View Code Duplication |
if ($discovery_parsed === false) { |
|
|
|
|
419
|
|
|
$this->cache->remove('discovery.xml'); |
420
|
|
|
$wopiRemote = $this->getWopiUrl($this->isTester()); |
421
|
|
|
|
422
|
|
|
return array( |
423
|
|
|
'status' => 'error', |
424
|
|
|
'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)), |
425
|
|
|
'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote)) |
426
|
|
|
); |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
catch (ResponseException $e) { |
430
|
|
|
return array( |
431
|
|
|
'status' => 'error', |
432
|
|
|
'message' => $e->getMessage(), |
433
|
|
|
'hint' => $e->getHint() |
434
|
|
|
); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
if ($content && $view->file_put_contents($path, $content)){ |
438
|
|
|
$info = $view->getFileInfo($path); |
439
|
|
|
$ret = $this->getWopiSrcUrl($discovery_parsed, $mimetype); |
440
|
|
|
$response = array( |
441
|
|
|
'status' => 'success', |
442
|
|
|
'fileid' => $info['fileid'], |
443
|
|
|
'urlsrc' => $ret['urlsrc'], |
444
|
|
|
'action' => $ret['action'], |
445
|
|
|
'lolang' => $this->settings->getUserValue($this->uid, 'core', 'lang', 'en'), |
446
|
|
|
'data' => \OCA\Files\Helper::formatFileInfo($info) |
447
|
|
|
); |
448
|
|
|
} else { |
449
|
|
|
$response = array( |
450
|
|
|
'status' => 'error', |
451
|
|
|
'message' => (string) $this->l10n->t('Can\'t create document') |
452
|
|
|
); |
453
|
|
|
} |
454
|
|
|
return $response; |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
/** |
458
|
|
|
* @NoAdminRequired |
459
|
|
|
* Generates and returns an access token for a given fileId. |
460
|
|
|
* Only for authenticated users! |
461
|
|
|
*/ |
462
|
|
|
public function wopiGetToken($fileId){ |
463
|
|
|
$arr = explode('_', $fileId, 2); |
464
|
|
|
$version = '0'; |
465
|
|
View Code Duplication |
if (count($arr) == 2) { |
|
|
|
|
466
|
|
|
$fileId = $arr[0]; |
467
|
|
|
$version = $arr[1]; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
\OC::$server->getLogger()->debug('Generating WOPI Token for file {fileId}, version {version}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version ]); |
471
|
|
|
|
472
|
|
|
$view = \OC\Files\Filesystem::getView(); |
473
|
|
|
$path = $view->getPath($fileId); |
474
|
|
|
$updatable = (bool)$view->isUpdatable($path); |
475
|
|
|
|
476
|
|
|
// Check if the editor (user who is accessing) is in editable group |
477
|
|
|
// UserCanWrite only if |
478
|
|
|
// 1. No edit groups are set or |
479
|
|
|
// 2. if they are set, it is in one of the edit groups |
480
|
|
|
$editorUid = \OC::$server->getUserSession()->getUser()->getUID(); |
481
|
|
|
$editGroups = array_filter(explode('|', $this->appConfig->getAppValue('edit_groups'))); |
482
|
|
|
if ($updatable && count($editGroups) > 0) { |
483
|
|
|
$updatable = false; |
484
|
|
View Code Duplication |
foreach($editGroups as $editGroup) { |
|
|
|
|
485
|
|
|
$editorGroup = \OC::$server->getGroupManager()->get($editGroup); |
486
|
|
|
if ($editorGroup !== null && sizeof($editorGroup->searchUsers($editorUid)) > 0) { |
487
|
|
|
\OC::$server->getLogger()->debug("Editor {editor} is in edit group {group}", [ |
488
|
|
|
'app' => $this->appName, |
489
|
|
|
'editor' => $editorUid, |
490
|
|
|
'group' => $editGroup |
491
|
|
|
]); |
492
|
|
|
$updatable = true; |
493
|
|
|
break; |
494
|
|
|
} |
495
|
|
|
} |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
// If token is for some versioned file |
499
|
|
|
if ($version !== '0') { |
500
|
|
|
\OC::$server->getLogger()->debug('setting updatable to false'); |
501
|
|
|
$updatable = false; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
\OC::$server->getLogger()->debug('File with {fileid} has updatable set to {updatable}', [ 'app' => $this->appName, 'fileid' => $fileId, 'updatable' => $updatable ]); |
505
|
|
|
|
506
|
|
|
$row = new Db\Wopi(); |
507
|
|
|
$serverHost = $this->request->getServerProtocol() . '://' . $this->request->getServerHost(); |
508
|
|
|
$token = $row->generateFileToken($fileId, $version, $updatable, $serverHost); |
509
|
|
|
|
510
|
|
|
// Return the token. |
511
|
|
|
return array( |
512
|
|
|
'status' => 'success', |
513
|
|
|
'token' => $token |
514
|
|
|
); |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
/** |
518
|
|
|
* @NoAdminRequired |
519
|
|
|
* @NoCSRFRequired |
520
|
|
|
* @PublicPage |
521
|
|
|
* Returns general info about a file. |
522
|
|
|
*/ |
523
|
|
|
public function wopiCheckFileInfo($fileId){ |
524
|
|
|
$token = $this->request->getParam('access_token'); |
525
|
|
|
|
526
|
|
|
$arr = explode('_', $fileId, 2); |
527
|
|
|
$version = '0'; |
528
|
|
View Code Duplication |
if (count($arr) == 2) { |
|
|
|
|
529
|
|
|
$fileId = $arr[0]; |
530
|
|
|
$version = $arr[1]; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
\OC::$server->getLogger()->debug('Getting info about file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]); |
534
|
|
|
|
535
|
|
|
$row = new Db\Wopi(); |
536
|
|
|
$row->loadBy('token', $token); |
537
|
|
|
|
538
|
|
|
$res = $row->getPathForToken($fileId, $version, $token); |
539
|
|
|
if ($res == false || http_response_code() != 200) |
540
|
|
|
{ |
541
|
|
|
return false; |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
// Login the user to see his mount locations |
545
|
|
|
$this->loginUser($res['owner']); |
546
|
|
|
$view = new \OC\Files\View('/' . $res['owner'] . '/files'); |
547
|
|
|
$info = $view->getFileInfo($res['path']); |
548
|
|
|
$this->logoutUser(); |
549
|
|
|
|
550
|
|
|
if (!$info) { |
551
|
|
|
http_response_code(404); |
552
|
|
|
return false; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
$editorName = \OC::$server->getUserManager()->get($res['editor'])->getDisplayName(); |
556
|
|
|
return array( |
557
|
|
|
'BaseFileName' => $info['name'], |
558
|
|
|
'Size' => $info['size'], |
559
|
|
|
'Version' => $version, |
560
|
|
|
'UserId' => $res['editor'], |
561
|
|
|
'UserFriendlyName' => $editorName, |
562
|
|
|
'UserCanWrite' => $res['canwrite'] ? true : false, |
563
|
|
|
'PostMessageOrigin' => $res['server_host'] |
564
|
|
|
); |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* @NoAdminRequired |
569
|
|
|
* @NoCSRFRequired |
570
|
|
|
* @PublicPage |
571
|
|
|
* Given an access token and a fileId, returns the contents of the file. |
572
|
|
|
* Expects a valid token in access_token parameter. |
573
|
|
|
*/ |
574
|
|
|
public function wopiGetFile($fileId){ |
575
|
|
|
$token = $this->request->getParam('access_token'); |
576
|
|
|
|
577
|
|
|
$arr = explode('_', $fileId, 2); |
578
|
|
|
$version = '0'; |
579
|
|
View Code Duplication |
if (count($arr) == 2) { |
|
|
|
|
580
|
|
|
$fileId = $arr[0]; |
581
|
|
|
$version = $arr[1]; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
\OC::$server->getLogger()->debug('Getting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]); |
585
|
|
|
|
586
|
|
|
$row = new Db\Wopi(); |
587
|
|
|
$row->loadBy('token', $token); |
588
|
|
|
|
589
|
|
|
//TODO: Support X-WOPIMaxExpectedSize header. |
590
|
|
|
$res = $row->getPathForToken($fileId, $version, $token); |
591
|
|
|
$ownerid = $res['owner']; |
592
|
|
|
|
593
|
|
|
// Login the user to see his mount locations |
594
|
|
|
$this->loginUser($ownerid); |
595
|
|
|
$view = new \OC\Files\View('/' . $res['owner'] . '/files'); |
596
|
|
|
$info = $view->getFileInfo($res['path']); |
597
|
|
|
|
598
|
|
|
if (!$info) { |
599
|
|
|
http_response_code(404); |
600
|
|
|
return false; |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
$filename = ''; |
|
|
|
|
604
|
|
|
// If some previous version is requested, fetch it from Files_Version app |
605
|
|
|
if ($version !== '0') { |
606
|
|
|
\OCP\JSON::checkAppEnabled('files_versions'); |
607
|
|
|
|
608
|
|
|
$filename = '/files_versions/' . $info['name'] . '.v' . $version; |
609
|
|
|
} else { |
610
|
|
|
$filename = '/files' . $res['path']; |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
$this->logoutUser(); |
614
|
|
|
|
615
|
|
|
return new DownloadResponse($this->request, $ownerid, $filename); |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/** |
619
|
|
|
* @NoAdminRequired |
620
|
|
|
* @NoCSRFRequired |
621
|
|
|
* @PublicPage |
622
|
|
|
* Given an access token and a fileId, replaces the files with the request body. |
623
|
|
|
* Expects a valid token in access_token parameter. |
624
|
|
|
*/ |
625
|
|
|
public function wopiPutFile($fileId){ |
626
|
|
|
$token = $this->request->getParam('access_token'); |
627
|
|
|
|
628
|
|
|
$arr = explode('_', $fileId, 2); |
629
|
|
|
$version = '0'; |
630
|
|
View Code Duplication |
if (count($arr) == 2) { |
|
|
|
|
631
|
|
|
$fileId = $arr[0]; |
632
|
|
|
$version = $arr[1]; |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
\OC::$server->getLogger()->debug('Putting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]); |
636
|
|
|
|
637
|
|
|
$row = new Db\Wopi(); |
638
|
|
|
$row->loadBy('token', $token); |
639
|
|
|
|
640
|
|
|
$res = $row->getPathForToken($fileId, $version, $token); |
641
|
|
|
if (!$res['canwrite']) { |
642
|
|
|
return array( |
643
|
|
|
'status' => 'error', |
644
|
|
|
'message' => 'Permission denied' |
645
|
|
|
); |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
// Log-in as the user to regiser the change under her name. |
649
|
|
|
$editorid = $res['editor']; |
650
|
|
|
// This call is made from loolwsd, so we need to initialize the |
651
|
|
|
// session before we can make the user who opened the document |
652
|
|
|
// login. This is necessary to make activity app register the |
653
|
|
|
// change made to this file under this user's (editorid) name. |
654
|
|
|
$this->loginUser($editorid); |
655
|
|
|
|
656
|
|
|
// Set up the filesystem view for the owner (where the file actually is). |
657
|
|
|
$userid = $res['owner']; |
658
|
|
|
$root = '/' . $userid . '/files'; |
659
|
|
|
$view = new \OC\Files\View($root); |
660
|
|
|
|
661
|
|
|
// Read the contents of the file from the POST body and store. |
662
|
|
|
$content = fopen('php://input', 'r'); |
663
|
|
|
\OC::$server->getLogger()->debug('Storing file {fileId} by {editor} owned by {owner}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'editor' => $editorid, 'owner' => $userid ]); |
664
|
|
|
|
665
|
|
|
// Setup the FS which is needed to emit hooks (versioning). |
666
|
|
|
\OC_Util::tearDownFS(); |
667
|
|
|
\OC_Util::setupFS($userid); |
668
|
|
|
|
669
|
|
|
$view->file_put_contents($res['path'], $content); |
670
|
|
|
|
671
|
|
|
$this->logoutUser(); |
672
|
|
|
|
673
|
|
|
return array( |
674
|
|
|
'status' => 'success' |
675
|
|
|
); |
676
|
|
|
} |
677
|
|
|
|
678
|
|
|
/** |
679
|
|
|
* @NoAdminRequired |
680
|
|
|
* @PublicPage |
681
|
|
|
* Process partial/complete file download |
682
|
|
|
*/ |
683
|
|
|
public function serve($esId){ |
684
|
|
|
$session = new Db\Session(); |
685
|
|
|
$session->load($esId); |
686
|
|
|
|
687
|
|
|
$filename = $session->getGenesisUrl() ? $session->getGenesisUrl() : ''; |
688
|
|
|
return new DownloadResponse($this->request, $session->getOwner(), $filename); |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
/** |
692
|
|
|
* @NoAdminRequired |
693
|
|
|
*/ |
694
|
|
|
public function download($path){ |
695
|
|
|
if (!$path){ |
696
|
|
|
$response = new JSONResponse(); |
697
|
|
|
$response->setStatus(Http::STATUS_BAD_REQUEST); |
698
|
|
|
return $response; |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
$fullPath = '/files' . $path; |
702
|
|
|
$fileInfo = \OC\Files\Filesystem::getFileInfo($path); |
703
|
|
|
if ($fileInfo){ |
704
|
|
|
$file = new File($fileInfo->getId()); |
705
|
|
|
$genesis = new Genesis($file); |
706
|
|
|
$fullPath = $genesis->getPath(); |
707
|
|
|
} |
708
|
|
|
return new DownloadResponse($this->request, $this->uid, $fullPath); |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* @NoAdminRequired |
714
|
|
|
*/ |
715
|
1 |
|
public function rename($fileId){ |
716
|
1 |
|
$name = $this->request->post['name']; |
717
|
|
|
|
718
|
1 |
|
$view = \OC\Files\Filesystem::getView(); |
719
|
1 |
|
$path = $view->getPath($fileId); |
720
|
|
|
|
721
|
|
|
if ($name && $view->is_file($path) && $view->isUpdatable($path)) { |
722
|
|
|
$newPath = dirname($path) . '/' . $name; |
723
|
|
|
if ($view->rename($path, $newPath)) { |
724
|
|
|
return array('status' => 'success'); |
725
|
|
|
} |
726
|
|
|
} |
727
|
|
|
return array( |
728
|
|
|
'status' => 'error', |
729
|
|
|
'message' => (string) $this->l10n->t('You don\'t have permission to rename this document') |
730
|
|
|
); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
/** |
734
|
|
|
* @NoAdminRequired |
735
|
|
|
* Get file information about single document with fileId |
736
|
|
|
*/ |
737
|
|
|
public function get($fileId){ |
738
|
|
|
$documents = array(); |
739
|
|
|
$documents[0] = Storage::getDocumentById($fileId); |
740
|
|
|
|
741
|
|
|
return $this->prepareDocuments($documents); |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
|
745
|
|
|
/** |
746
|
|
|
* @NoAdminRequired |
747
|
|
|
* lists the documents the user has access to (including shared files, once the code in core has been fixed) |
748
|
|
|
* also adds session and member info for these files |
749
|
|
|
*/ |
750
|
|
|
public function listAll(){ |
751
|
|
|
return $this->prepareDocuments(Storage::getDocuments()); |
752
|
|
|
} |
753
|
|
|
} |
754
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.