Skip to content

Commit 470f23c

Browse files
authored
Merge pull request #54 from nextcloud/backport-upload-only-shares
[stable9] Backport files drop feature
2 parents 4fcc8c9 + 7f5048e commit 470f23c

15 files changed

Lines changed: 429 additions & 149 deletions

File tree

apps/dav/appinfo/v1/publicwebdav.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,17 @@
5959
$rootShare = \OCP\Share::resolveReShare($share);
6060
$owner = $rootShare['uid_owner'];
6161
$isWritable = $share['permissions'] & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE);
62+
$isReadable = $share['permissions'] & \OCP\Constants::PERMISSION_READ;
6263
$fileId = $share['file_source'];
6364

6465
if (!$isWritable) {
6566
\OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) {
6667
return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_READ + \OCP\Constants::PERMISSION_SHARE));
6768
});
6869
}
70+
if (!$isReadable) {
71+
return false;
72+
}
6973

7074
OC_Util::setupFS($owner);
7175
$ownerView = \OC\Files\Filesystem::getView();

apps/files/ajax/upload.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
// If no token is sent along, rely on login only
4444

4545
$errorCode = null;
46+
$errorFileName = null;
4647

4748
$l = \OC::$server->getL10N('files');
4849
if (empty($_POST['dirToken'])) {
@@ -161,6 +162,15 @@
161162
$resolution = null;
162163
}
163164

165+
if(isset($_POST['dirToken'])) {
166+
// If it is a read only share the resolution will always be autorename
167+
$shareManager = \OC::$server->getShareManager();
168+
$share = $shareManager->getShareByToken((string)$_POST['dirToken']);
169+
if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
170+
$resolution = 'autorename';
171+
}
172+
}
173+
164174
// target directory for when uploading folders
165175
$relativePath = '';
166176
if(!empty($_POST['file_directory'])) {
@@ -216,6 +226,7 @@
216226

217227
} else {
218228
$error = $l->t('Upload failed. Could not find uploaded file');
229+
$errorFileName = $files['name'][$i];
219230
}
220231
} catch(Exception $ex) {
221232
$error = $ex->getMessage();
@@ -247,7 +258,26 @@
247258
}
248259

249260
if ($error === false) {
261+
// Do not leak file information if it is a read-only share
262+
if(isset($_POST['dirToken'])) {
263+
$shareManager = \OC::$server->getShareManager();
264+
$share = $shareManager->getShareByToken((string)$_POST['dirToken']);
265+
if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
266+
$newResults = [];
267+
foreach($result as $singleResult) {
268+
$fileName = $singleResult['originalname'];
269+
$newResults['filename'] = $fileName;
270+
$newResults['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($fileName);
271+
}
272+
$result = $newResults;
273+
}
274+
}
275+
250276
OCP\JSON::encodedPrint($result);
251277
} else {
252-
OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats))));
278+
OCP\JSON::error(array(array('data' => array_merge(array(
279+
'message' => $error,
280+
'code' => $errorCode,
281+
'filename' => $errorFileName
282+
), $storageStats))));
253283
}

apps/files_sharing/ajax/publicpreview.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
}
4343

4444
$linkedItem = \OCP\Share::getShareByToken($token);
45+
$shareManager = \OC::$server->getShareManager();
46+
$share = $shareManager->getShareByToken($token);
47+
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
48+
OCP\JSON::error(array('data' => 'Share is not readable.'));
49+
exit();
50+
}
4551
if($linkedItem === false || ($linkedItem['item_type'] !== 'file' && $linkedItem['item_type'] !== 'folder')) {
4652
\OC_Response::setStatus(\OC_Response::STATUS_NOT_FOUND);
4753
\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);

apps/files_sharing/ajax/shareinfo.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@
6262
$rootInfo = \OC\Files\Filesystem::getFileInfo($path);
6363
$rootView = new \OC\Files\View('');
6464

65+
66+
$shareManager = \OC::$server->getShareManager();
67+
$share = $shareManager->getShareByToken($token);
68+
$sharePermissions= (int)$share->getPermissions();
69+
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
70+
OCP\JSON::error(array('data' => 'Share is not readable.'));
71+
exit();
72+
}
73+
6574
/**
6675
* @param \OCP\Files\FileInfo $dir
6776
* @param \OC\Files\View $view

apps/files_sharing/api/share20ocs.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ public function updateShare($id) {
584584

585585
if ($newPermissions !== null &&
586586
$newPermissions !== \OCP\Constants::PERMISSION_READ &&
587+
$newPermissions !== (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
587588
$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)) {
588589
return new \OC_OCS_Result(null, 400, 'can\'t change permission for public link share');
589590
}

apps/files_sharing/css/public.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,62 @@ thead {
158158
opacity: 1;
159159
cursor: pointer;
160160
}
161+
162+
#public-upload .avatardiv {
163+
margin: 0 auto;
164+
}
165+
166+
#public-upload #emptycontent h2 {
167+
margin: 10px 0 5px 0;
168+
}
169+
170+
#public-upload #emptycontent h2+p {
171+
margin-bottom: 30px;
172+
}
173+
174+
#public-upload #emptycontent .icon-folder {
175+
height: 16px;
176+
width: 16px;
177+
background-size: 16px;
178+
display: inline-block;
179+
vertical-align: text-top;
180+
margin-bottom: 0;
181+
margin-right: 5px;
182+
opacity: 1;
183+
}
184+
185+
#public-upload #emptycontent .button {
186+
background-size: 16px;
187+
height: 16px;
188+
width: 16px;
189+
background-position: 16px;
190+
opacity: .7;
191+
font-size: 20px;
192+
margin: 20px;
193+
padding: 10px 20px;
194+
padding-left: 42px;
195+
font-weight: normal;
196+
}
197+
198+
#public-upload #emptycontent ul {
199+
width: 160px;
200+
margin: 5px auto;
201+
text-align: left;
202+
}
203+
204+
#public-upload #emptycontent li {
205+
overflow: hidden;
206+
text-overflow: ellipsis;
207+
white-space: nowrap;
208+
padding: 7px 0;
209+
}
210+
211+
#public-upload #emptycontent li img {
212+
vertical-align: text-bottom;
213+
margin-right: 5px;
214+
}
215+
216+
#public-upload li span.icon-loading-small {
217+
padding-left: 18px;
218+
margin-right: 7px;
219+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
3+
*
4+
* This file is licensed under the Affero General Public License version 3
5+
* or later.
6+
*
7+
* See the COPYING-README file.
8+
*
9+
*/
10+
11+
(function ($) {
12+
var TEMPLATE =
13+
'<li data-toggle="tooltip" title="{{name}}" data-name="{{name}}">' +
14+
'{{#if isUploading}}' +
15+
'<span class="icon-loading-small"></span> {{name}}' +
16+
'{{else}}' +
17+
'<img src="' + OC.imagePath('core', 'actions/error.svg') + '"/> {{name}}' +
18+
'{{/if}}' +
19+
'</li>';
20+
var Drop = {
21+
/** @type {Function} **/
22+
_template: undefined,
23+
24+
initialize: function () {
25+
$(document).bind('drop dragover', function (e) {
26+
// Prevent the default browser drop action:
27+
e.preventDefault();
28+
});
29+
var output = this.template();
30+
$('#public-upload').fileupload({
31+
url: OC.linkTo('files', 'ajax/upload.php'),
32+
dataType: 'json',
33+
dropZone: $('#public-upload'),
34+
formData: {
35+
dirToken: $('#sharingToken').val()
36+
},
37+
add: function(e, data) {
38+
var errors = [];
39+
if(data.files[0]['size'] && data.files[0]['size'] > $('#maxFilesizeUpload').val()) {
40+
errors.push('File is too big');
41+
}
42+
43+
$('#drop-upload-done-indicator').addClass('hidden');
44+
$('#drop-upload-progress-indicator').removeClass('hidden');
45+
_.each(data['files'], function(file) {
46+
if(errors.length === 0) {
47+
$('#public-upload ul').append(output({isUploading: true, name: escapeHTML(file.name)}));
48+
$('[data-toggle="tooltip"]').tooltip();
49+
data.submit();
50+
} else {
51+
OC.Notification.showTemporary(OC.L10N.translate('files_sharing', 'Could not upload "{filename}"', {filename: file.name}));
52+
$('#public-upload ul').append(output({isUploading: false, name: escapeHTML(file.name)}));
53+
$('[data-toggle="tooltip"]').tooltip();
54+
}
55+
});
56+
},
57+
success: function (response) {
58+
if(response.status !== 'error') {
59+
var mimeTypeUrl = OC.MimeType.getIconUrl(response['mimetype']);
60+
$('#public-upload ul li[data-name="' + escapeHTML(response['filename']) + '"]').html('<img src="' + escapeHTML(mimeTypeUrl) + '"/> ' + escapeHTML(response['filename']));
61+
$('[data-toggle="tooltip"]').tooltip();
62+
} else {
63+
var name = response[0]['data']['filename'];
64+
OC.Notification.showTemporary(OC.L10N.translate('files_sharing', 'Could not upload "{filename}"', {filename: name}));
65+
$('#public-upload ul li[data-name="' + escapeHTML(name) + '"]').html(output({isUploading: false, name: escapeHTML(name)}));
66+
$('[data-toggle="tooltip"]').tooltip();
67+
}
68+
69+
},
70+
progressall: function (e, data) {
71+
var progress = parseInt(data.loaded / data.total * 100, 10);
72+
if(progress === 100) {
73+
$('#drop-upload-done-indicator').removeClass('hidden');
74+
$('#drop-upload-progress-indicator').addClass('hidden');
75+
} else {
76+
$('#drop-upload-done-indicator').addClass('hidden');
77+
$('#drop-upload-progress-indicator').removeClass('hidden');
78+
}
79+
}
80+
});
81+
$('#public-upload .button.icon-upload').click(function(e) {
82+
e.preventDefault();
83+
$('#public-upload #emptycontent input').focus().trigger('click');
84+
});
85+
},
86+
87+
/**
88+
* @returns {Function} from Handlebars
89+
* @private
90+
*/
91+
template: function () {
92+
if (!this._template) {
93+
this._template = Handlebars.compile(TEMPLATE);
94+
}
95+
return this._template;
96+
}
97+
};
98+
99+
$(document).ready(function() {
100+
if($('#upload-only-interface').val() === "1") {
101+
$('.avatardiv').avatar($('#sharingUserId').val(), 128, true);
102+
}
103+
104+
OCA.Files_Sharing_Drop = Drop;
105+
OCA.Files_Sharing_Drop.initialize();
106+
});
107+
108+
109+
})(jQuery);

apps/files_sharing/lib/controllers/sharecontroller.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ public function showShare($token, $path = '') {
307307
$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
308308

309309
// Show file list
310+
$hideFileList = false;
310311
if ($share->getNode() instanceof \OCP\Files\Folder) {
311312
$shareTmpl['dir'] = $rootFolder->getRelativePath($path->getPath());
312313

@@ -322,12 +323,14 @@ public function showShare($token, $path = '') {
322323

323324
$uploadLimit = Util::uploadLimit();
324325
$maxUploadFilesize = min($freeSpace, $uploadLimit);
326+
$hideFileList = $share->getPermissions() & \OCP\Constants::PERMISSION_READ ? false : true;
325327

326328
$folder = new Template('files', 'list', '');
327329
$folder->assign('dir', $rootFolder->getRelativePath($path->getPath()));
328330
$folder->assign('dirToken', $token);
329331
$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
330332
$folder->assign('isPublic', true);
333+
$folder->assign('hideFileList', $hideFileList);
331334
$folder->assign('publicUploadEnabled', 'no');
332335
$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
333336
$folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
@@ -338,6 +341,8 @@ public function showShare($token, $path = '') {
338341
$shareTmpl['folder'] = $folder->fetchPage();
339342
}
340343

344+
$shareTmpl['hideFileList'] = $hideFileList;
345+
$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
341346
$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', array('token' => $token));
342347
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
343348
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
@@ -360,12 +365,15 @@ public function showShare($token, $path = '') {
360365
* @param string $files
361366
* @param string $path
362367
* @param string $downloadStartSecret
363-
* @return void|RedirectResponse
368+
* @return void|OCP\AppFramework\Http\Response
364369
*/
365370
public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
366371
\OC_User::setIncognitoMode(true);
367372

368373
$share = $this->shareManager->getShareByToken($token);
374+
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
375+
return new OCP\AppFramework\Http\DataResponse('Share is read-only');
376+
}
369377

370378
// Share is password protected - check whether the user is permitted to access the share
371379
if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {

0 commit comments

Comments
 (0)