Merge pull request #19 from nextcloud/files-drop

add "hide file list" option
This commit is contained in:
Lukas Reschke 2016-06-10 18:29:09 +02:00 committed by GitHub
commit 842cc2a788
17 changed files with 340 additions and 28 deletions

View file

@ -67,8 +67,13 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func
$share = $authBackend->getShare(); $share = $authBackend->getShare();
$owner = $share->getShareOwner(); $owner = $share->getShareOwner();
$isWritable = $share->getPermissions() & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE); $isWritable = $share->getPermissions() & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE);
$isReadable = $share->getPermissions() & \OCP\Constants::PERMISSION_READ;
$fileId = $share->getNodeId(); $fileId = $share->getNodeId();
if (!$isReadable) {
return false;
}
if (!$isWritable) { if (!$isWritable) {
\OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) { \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) {
return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_READ + \OCP\Constants::PERMISSION_SHARE)); return new \OC\Files\Storage\Wrapper\PermissionsMask(array('storage' => $storage, 'mask' => \OCP\Constants::PERMISSION_READ + \OCP\Constants::PERMISSION_SHARE));

View file

@ -161,6 +161,15 @@ if (\OC\Files\Filesystem::isValidPath($dir) === true) {
$resolution = null; $resolution = null;
} }
if(isset($_POST['dirToken'])) {
// If it is a read only share the resolution will always be autorename
$shareManager = \OC::$server->getShareManager();
$share = $shareManager->getShareByToken((string)$_POST['dirToken']);
if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
$resolution = 'autorename';
}
}
// target directory for when uploading folders // target directory for when uploading folders
$relativePath = ''; $relativePath = '';
if(!empty($_POST['file_directory'])) { if(!empty($_POST['file_directory'])) {
@ -247,6 +256,20 @@ if (\OC\Files\Filesystem::isValidPath($dir) === true) {
} }
if ($error === false) { if ($error === false) {
// Do not leak file information if it is a read-only share
if(isset($_POST['dirToken'])) {
$shareManager = \OC::$server->getShareManager();
$share = $shareManager->getShareByToken((string)$_POST['dirToken']);
if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
$newResults = [];
foreach($result as $singleResult) {
$fileName = $singleResult['originalname'];
$newResults['filename'] = $fileName;
$newResults['mimetype'] = \OC::$server->getMimeTypeDetector()->detectPath($fileName);
}
$result = $newResults;
}
}
OCP\JSON::encodedPrint($result); OCP\JSON::encodedPrint($result);
} else { } else {
OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats)))); OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats))));

View file

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

View file

@ -71,6 +71,11 @@ $shareManager = \OC::$server->getShareManager();
$share = $shareManager->getShareByToken($token); $share = $shareManager->getShareByToken($token);
$sharePermissions= (int)$share->getPermissions(); $sharePermissions= (int)$share->getPermissions();
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
OCP\JSON::error(array('data' => 'Share is not readable.'));
exit();
}
/** /**
* @param \OCP\Files\FileInfo $dir * @param \OCP\Files\FileInfo $dir
* @param \OC\Files\View $view * @param \OC\Files\View $view

View file

@ -158,3 +158,62 @@ thead {
opacity: 1; opacity: 1;
cursor: pointer; cursor: pointer;
} }
#public-upload .avatardiv {
margin: 0 auto;
}
#public-upload #emptycontent h2 {
margin: 10px 0 5px 0;
}
#public-upload #emptycontent h2+p {
margin-bottom: 30px;
}
#public-upload #emptycontent .icon-folder {
height: 16px;
width: 16px;
background-size: 16px;
display: inline-block;
vertical-align: text-top;
margin-bottom: 0;
margin-right: 5px;
opacity: 1;
}
#public-upload #emptycontent .button {
background-size: 16px;
height: 16px;
width: 16px;
background-position: 16px;
opacity: .7;
font-size: 20px;
margin: 20px;
padding: 10px 20px;
padding-left: 42px;
font-weight: normal;
}
#public-upload #emptycontent ul {
width: 160px;
margin: 5px auto;
text-align: left;
}
#public-upload #emptycontent li {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 7px 0;
}
#public-upload #emptycontent li img {
vertical-align: text-bottom;
margin-right: 5px;
}
#public-upload li span.icon-loading-small {
padding-left: 18px;
margin-right: 7px;
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
(function ($) {
var Drop = {
initialize: function () {
$(document).bind('drop dragover', function (e) {
// Prevent the default browser drop action:
e.preventDefault();
});
$('#public-upload').fileupload({
url: OC.linkTo('files', 'ajax/upload.php'),
dataType: 'json',
dropZone: $('#public-upload'),
formData: {
dirToken: $('#sharingToken').val()
},
add: function(e, data) {
var errors = [];
if(data.files[0]['size'] && data.files[0]['size'] > $('#maxFilesizeUpload').val()) {
errors.push('File is too big');
}
$('#drop-upload-done-indicator').addClass('hidden');
$('#drop-upload-progress-indicator').removeClass('hidden');
_.each(data['files'], function(file) {
if(errors.length === 0) {
$('#public-upload ul').append('<li data-toggle="tooltip" title="'+escapeHTML(file.name)+'" data-name="'+escapeHTML(file.name)+'"><span class="icon-loading-small"></span> '+escapeHTML(file.name)+'</li>');
$('[data-toggle="tooltip"]').tooltip();
data.submit();
} else {
OC.Notification.showTemporary(OC.L10N.translate('files_sharing', 'Could not upload "{filename}"', {filename: file.name}));
$('#public-upload ul').append('<li data-toggle="tooltip" title="'+escapeHTML(file.name)+'" data-name="'+escapeHTML(file.name)+'"><img src="'+OC.imagePath('core', 'actions/error.svg')+'"/> '+escapeHTML(file.name)+'</li>');
$('[data-toggle="tooltip"]').tooltip();
}
});
},
success: function (response) {
if(response.status !== 'error') {
var mimeTypeUrl = OC.MimeType.getIconUrl(response['mimetype']);
$('#public-upload ul li[data-name="' + escapeHTML(response['filename']) + '"]').html('<img src="' + escapeHTML(mimeTypeUrl) + '"/> ' + escapeHTML(response['filename']));
$('[data-toggle="tooltip"]').tooltip();
}
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
if(progress === 100) {
$('#drop-upload-done-indicator').removeClass('hidden');
$('#drop-upload-progress-indicator').addClass('hidden');
} else {
$('#drop-upload-done-indicator').addClass('hidden');
$('#drop-upload-progress-indicator').removeClass('hidden');
}
}
});
$('#public-upload .button.icon-upload').click(function(e) {
e.preventDefault();
$('#public-upload #emptycontent input').focus().trigger('click');
});
}
};
$(document).ready(function() {
if($('#upload-only-interface').val() === "1") {
$('.avatardiv').avatar($('#sharingUserId').val(), 128, true);
}
OCA.Files_Sharing_Drop = Drop;
OCA.Files_Sharing_Drop.initialize();
});
})(jQuery);

View file

@ -602,6 +602,7 @@ class Share20OCS {
if ($newPermissions !== null && if ($newPermissions !== null &&
$newPermissions !== \OCP\Constants::PERMISSION_READ && $newPermissions !== \OCP\Constants::PERMISSION_READ &&
$newPermissions !== (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) &&
$newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)) { $newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)) {
$share->getNode()->unlock(ILockingProvider::LOCK_SHARED); $share->getNode()->unlock(ILockingProvider::LOCK_SHARED);
return new \OC_OCS_Result(null, 400, $this->l->t('Can\'t change permissions for public share links')); return new \OC_OCS_Result(null, 400, $this->l->t('Can\'t change permissions for public share links'));

View file

@ -49,7 +49,6 @@ use OCP\ILogger;
use OCP\IUserManager; use OCP\IUserManager;
use OCP\ISession; use OCP\ISession;
use OCP\IPreview; use OCP\IPreview;
use OCA\Files_Sharing\Helper;
use OCP\Util; use OCP\Util;
use OCA\Files_Sharing\Activity; use OCA\Files_Sharing\Activity;
use \OCP\Files\NotFoundException; use \OCP\Files\NotFoundException;
@ -253,6 +252,7 @@ class ShareController extends Controller {
* @param string $path * @param string $path
* @return TemplateResponse|RedirectResponse * @return TemplateResponse|RedirectResponse
* @throws NotFoundException * @throws NotFoundException
* @throws \Exception
*/ */
public function showShare($token, $path = '') { public function showShare($token, $path = '') {
\OC_User::setIncognitoMode(true); \OC_User::setIncognitoMode(true);
@ -314,6 +314,7 @@ class ShareController extends Controller {
$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize()); $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
// Show file list // Show file list
$hideFileList = false;
if ($share->getNode() instanceof \OCP\Files\Folder) { if ($share->getNode() instanceof \OCP\Files\Folder) {
$shareTmpl['dir'] = $rootFolder->getRelativePath($path->getPath()); $shareTmpl['dir'] = $rootFolder->getRelativePath($path->getPath());
@ -329,12 +330,14 @@ class ShareController extends Controller {
$uploadLimit = Util::uploadLimit(); $uploadLimit = Util::uploadLimit();
$maxUploadFilesize = min($freeSpace, $uploadLimit); $maxUploadFilesize = min($freeSpace, $uploadLimit);
$hideFileList = $share->getPermissions() & \OCP\Constants::PERMISSION_READ ? false : true;
$folder = new Template('files', 'list', ''); $folder = new Template('files', 'list', '');
$folder->assign('dir', $rootFolder->getRelativePath($path->getPath())); $folder->assign('dir', $rootFolder->getRelativePath($path->getPath()));
$folder->assign('dirToken', $token); $folder->assign('dirToken', $token);
$folder->assign('permissions', \OCP\Constants::PERMISSION_READ); $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
$folder->assign('isPublic', true); $folder->assign('isPublic', true);
$folder->assign('hideFileList', $hideFileList);
$folder->assign('publicUploadEnabled', 'no'); $folder->assign('publicUploadEnabled', 'no');
$folder->assign('uploadMaxFilesize', $maxUploadFilesize); $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
$folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); $folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
@ -345,6 +348,8 @@ class ShareController extends Controller {
$shareTmpl['folder'] = $folder->fetchPage(); $shareTmpl['folder'] = $folder->fetchPage();
} }
$shareTmpl['hideFileList'] = $hideFileList;
$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', array('token' => $token)); $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', array('token' => $token));
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10); $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true); $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
@ -369,13 +374,18 @@ class ShareController extends Controller {
* @param string $files * @param string $files
* @param string $path * @param string $path
* @param string $downloadStartSecret * @param string $downloadStartSecret
* @return void|RedirectResponse * @return void|OCP\AppFramework\Http\Response
* @throws NotFoundException
*/ */
public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') { public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
\OC_User::setIncognitoMode(true); \OC_User::setIncognitoMode(true);
$share = $this->shareManager->getShareByToken($token); $share = $this->shareManager->getShareByToken($token);
if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
return new OCP\AppFramework\Http\DataResponse('Share is read-only');
}
// Share is password protected - check whether the user is permitted to access the share // Share is password protected - check whether the user is permitted to access the share
if ($share->getPassword() !== null && !$this->linkShareAuth($share)) { if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate', return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',

View file

@ -9,6 +9,7 @@ OCP\Util::addScript('files_sharing', 'public');
OCP\Util::addScript('files', 'fileactions'); OCP\Util::addScript('files', 'fileactions');
OCP\Util::addScript('files', 'fileactionsmenu'); OCP\Util::addScript('files', 'fileactionsmenu');
OCP\Util::addScript('files', 'jquery.fileupload'); OCP\Util::addScript('files', 'jquery.fileupload');
OCP\Util::addScript('files_sharing', 'files_drop');
// JS required for folders // JS required for folders
OCP\Util::addStyle('files', 'files'); OCP\Util::addStyle('files', 'files');
@ -30,6 +31,7 @@ OCP\Util::addscript('files', 'keyboardshortcuts');
<div id="notification" style="display: none;"></div> <div id="notification" style="display: none;"></div>
</div> </div>
<input type="hidden" id="sharingUserId" value="<?php p($_['owner']) ?>">
<input type="hidden" id="filesApp" name="filesApp" value="1"> <input type="hidden" id="filesApp" name="filesApp" value="1">
<input type="hidden" id="isPublic" name="isPublic" value="1"> <input type="hidden" id="isPublic" name="isPublic" value="1">
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir"> <input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
@ -39,7 +41,16 @@ OCP\Util::addscript('files', 'keyboardshortcuts');
<input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype"> <input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype">
<input type="hidden" name="previewSupported" value="<?php p($_['previewSupported'] ? 'true' : 'false'); ?>" id="previewSupported"> <input type="hidden" name="previewSupported" value="<?php p($_['previewSupported'] ? 'true' : 'false'); ?>" id="previewSupported">
<input type="hidden" name="mimetypeIcon" value="<?php p(\OC::$server->getMimeTypeDetector()->mimeTypeIcon($_['mimetype'])); ?>" id="mimetypeIcon"> <input type="hidden" name="mimetypeIcon" value="<?php p(\OC::$server->getMimeTypeDetector()->mimeTypeIcon($_['mimetype'])); ?>" id="mimetypeIcon">
<input type="hidden" name="filesize" value="<?php p($_['nonHumanFileSize']); ?>" id="filesize"> <?php
$upload_max_filesize = OC::$server->getIniWrapper()->getBytes('upload_max_filesize');
$post_max_size = OC::$server->getIniWrapper()->getBytes('post_max_size');
$maxUploadFilesize = min($upload_max_filesize, $post_max_size);
?>
<input type="hidden" name="maxFilesizeUpload" value="<?php p($maxUploadFilesize); ?>" id="maxFilesizeUpload">
<?php if (!isset($_['hideFileList']) || (isset($_['hideFileList']) && $_['hideFileList'] === false)): ?>
<input type="hidden" name="filesize" value="<?php p($_['nonHumanFileSize']); ?>" id="filesize">
<?php endif; ?>
<input type="hidden" name="maxSizeAnimateGif" value="<?php p($_['maxSizeAnimateGif']); ?>" id="maxSizeAnimateGif"> <input type="hidden" name="maxSizeAnimateGif" value="<?php p($_['maxSizeAnimateGif']); ?>" id="maxSizeAnimateGif">
@ -66,27 +77,30 @@ OCP\Util::addscript('files', 'keyboardshortcuts');
<div class="header-right"> <div class="header-right">
<span id="details"> <span id="details">
<?php <?php
if ($_['server2serversharing']) { if (!isset($_['hideFileList']) || (isset($_['hideFileList']) && $_['hideFileList'] === false)) {
?> if ($_['server2serversharing']) {
<span id="save" data-protected="<?php p($_['protected']) ?>" ?>
data-owner-display-name="<?php p($_['displayName']) ?>" data-owner="<?php p($_['owner']) ?>" data-name="<?php p($_['filename']) ?>"> <span id="save" data-protected="<?php p($_['protected']) ?>"
<button id="save-button"><?php p($l->t('Add to your ownCloud')) ?></button> data-owner-display-name="<?php p($_['displayName']) ?>" data-owner="<?php p($_['owner']) ?>" data-name="<?php p($_['filename']) ?>">
<form class="save-form hidden" action="#"> <button id="save-button"><?php p($l->t('Add to your ownCloud')) ?></button>
<input type="text" id="remote_address" placeholder="example.com/owncloud"/> <form class="save-form hidden" action="#">
<button id="save-button-confirm" class="icon-confirm svg" disabled></button> <input type="text" id="remote_address" placeholder="example.com/owncloud"/>
</form> <button id="save-button-confirm" class="icon-confirm svg" disabled></button>
</span> </form>
</span>
<?php } ?>
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
<img class="svg" alt="" src="<?php print_unescaped(image_path("core", "actions/download.svg")); ?>"/>
<span id="download-text"><?php p($l->t('Download'))?></span>
</a>
<?php } ?> <?php } ?>
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
<img class="svg" alt="" src="<?php print_unescaped(image_path("core", "actions/download.svg")); ?>"/>
<span id="download-text"><?php p($l->t('Download'))?></span>
</a>
</span> </span>
</div> </div>
</div></header> </div></header>
<div id="content-wrapper"> <div id="content-wrapper">
<?php if (!isset($_['hideFileList']) || (isset($_['hideFileList']) && $_['hideFileList'] === false)) { ?>
<div id="content"> <div id="content">
<div id="preview"> <div id="preview">
<?php if (isset($_['folder'])): ?> <?php if (isset($_['folder'])): ?>
<?php print_unescaped($_['folder']); ?> <?php print_unescaped($_['folder']); ?>
<?php else: ?> <?php else: ?>
@ -112,7 +126,31 @@ OCP\Util::addscript('files', 'keyboardshortcuts');
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php } else { ?>
<input type="hidden" id="upload-only-interface" value="1"/>
<div id="public-upload">
<div id="emptycontent" class="">
<div id="displayavatar"><div class="avatardiv"></div></div>
<h2><?php p($l->t('Upload files to %s', [$_['shareOwner']])) ?></h2>
<p><span class="icon-folder"></span> <?php p($_['filename']) ?></p>
<input type="file" name="files[]" class="hidden" multiple>
<a href="#" class="button icon-upload"><?php p($l->t('Select or drop files')) ?></a>
<div id="drop-upload-progress-indicator" style="padding-top: 25px;" class="hidden"><?php p($l->t('Uploading files…')) ?></div>
<div id="drop-upload-done-indicator" style="padding-top: 25px;" class="hidden"><?php p($l->t('Uploaded files:')) ?></div>
<ul>
</ul>
</div>
</div>
<?php } ?>
</div>
<?php if (!isset($_['hideFileList']) || (isset($_['hideFileList']) && $_['hideFileList'] !== true)): ?>
<input type="hidden" name="dir" id="dir" value="" />
<div class="hiddenuploadfield">
<input type="file" id="file_upload_start" class="hiddenuploadfield" name="files[]"
data-url="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>" />
</div> </div>
<?php endif; ?>
<footer> <footer>
<p class="info"> <p class="info">
<?php print_unescaped($theme->getLongFooter()); ?> <?php print_unescaped($theme->getLongFooter()); ?>

View file

@ -32,6 +32,7 @@ namespace OCA\Files_Sharing\Tests\Controllers;
use OC\Files\Filesystem; use OC\Files\Filesystem;
use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Files_Sharing\Controllers\ShareController; use OCA\Files_Sharing\Controllers\ShareController;
use OCP\AppFramework\Http\DataResponse;
use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\Exceptions\ShareNotFound;
use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\RedirectResponse;
@ -372,6 +373,8 @@ class ShareControllerTest extends \Test\TestCase {
'previewEnabled' => true, 'previewEnabled' => true,
'previewMaxX' => 1024, 'previewMaxX' => 1024,
'previewMaxY' => 1024, 'previewMaxY' => 1024,
'hideFileList' => false,
'shareOwner' => 'ownerDisplay'
); );
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
@ -430,10 +433,13 @@ class ShareControllerTest extends \Test\TestCase {
$this->shareController->showShare('token'); $this->shareController->showShare('token');
} }
public function testDownloadShare() { public function testDownloadShare() {
$share = $this->getMock('\OCP\Share\IShare'); $share = $this->getMock('\OCP\Share\IShare');
$share->method('getPassword')->willReturn('password'); $share->method('getPassword')->willReturn('password');
$share
->expects($this->once())
->method('getPermissions')
->willReturn(\OCP\Constants::PERMISSION_READ);
$this->shareManager $this->shareManager
->expects($this->once()) ->expects($this->once())
@ -452,4 +458,24 @@ class ShareControllerTest extends \Test\TestCase {
$this->assertEquals($expectedResponse, $response); $this->assertEquals($expectedResponse, $response);
} }
public function testDownloadShareWithCreateOnlyShare() {
$share = $this->getMock('\OCP\Share\IShare');
$share->method('getPassword')->willReturn('password');
$share
->expects($this->once())
->method('getPermissions')
->willReturn(\OCP\Constants::PERMISSION_CREATE);
$this->shareManager
->expects($this->once())
->method('getShareByToken')
->with('validtoken')
->willReturn($share);
// Test with a password protected share and no authentication
$response = $this->shareController->downloadShare('validtoken');
$expectedResponse = new DataResponse('Share is read-only');
$this->assertEquals($expectedResponse, $response);
}
} }

View file

@ -85,7 +85,7 @@ class AvatarController extends Controller {
IL10N $l10n, IL10N $l10n,
IUserManager $userManager, IUserManager $userManager,
IUserSession $userSession, IUserSession $userSession,
Folder $userFolder, Folder $userFolder = null,
ILogger $logger) { ILogger $logger) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
@ -101,6 +101,7 @@ class AvatarController extends Controller {
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
* @PublicPage
* *
* @param string $userId * @param string $userId
* @param int $size * @param int $size

View file

@ -442,7 +442,6 @@ OC.Share = _.extend(OC.Share || {}, {
}); });
$(document).ready(function() { $(document).ready(function() {
if(typeof monthNames != 'undefined'){ if(typeof monthNames != 'undefined'){
// min date should always be the next day // min date should always be the next day
var minDate = new Date(); var minDate = new Date();

View file

@ -30,6 +30,13 @@
' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload-{{cid}}" class="checkbox publicUploadCheckbox" {{{publicUploadChecked}}} />' + ' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload-{{cid}}" class="checkbox publicUploadCheckbox" {{{publicUploadChecked}}} />' +
'<label for="sharingDialogAllowPublicUpload-{{cid}}">{{publicUploadLabel}}</label>' + '<label for="sharingDialogAllowPublicUpload-{{cid}}">{{publicUploadLabel}}</label>' +
'</div>' + '</div>' +
'{{#if hideFileList}}' +
'<div id="hideFileListWrapper">' +
' <span class="icon-loading-small hidden"></span>' +
' <input type="checkbox" value="1" name="hideFileList" id="sharingDialogHideFileList-{{cid}}" class="checkbox hideFileListCheckbox" {{{hideFileListChecked}}} />' +
'<label for="sharingDialogHideFileList-{{cid}}">{{hideFileListLabel}}</label>' +
'</div>' +
'{{/if}}' +
' {{/if}}' + ' {{/if}}' +
' {{#if showPasswordCheckBox}}' + ' {{#if showPasswordCheckBox}}' +
'<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' + '<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
@ -75,6 +82,7 @@
'click .linkCheckbox': 'onLinkCheckBoxChange', 'click .linkCheckbox': 'onLinkCheckBoxChange',
'click .linkText': 'onLinkTextClick', 'click .linkText': 'onLinkTextClick',
'change .publicUploadCheckbox': 'onAllowPublicUploadChange', 'change .publicUploadCheckbox': 'onAllowPublicUploadChange',
'change .hideFileListCheckbox': 'onHideFileListChange',
'click .showPasswordCheckbox': 'onShowPasswordClick' 'click .showPasswordCheckbox': 'onShowPasswordClick'
}, },
@ -93,6 +101,10 @@
view.render(); view.render();
}); });
this.model.on('change:hideFileListStatus', function() {
view.render();
});
this.model.on('change:linkShare', function() { this.model.on('change:linkShare', function() {
view.render(); view.render();
}); });
@ -110,6 +122,7 @@
'onPasswordKeyUp', 'onPasswordKeyUp',
'onLinkTextClick', 'onLinkTextClick',
'onShowPasswordClick', 'onShowPasswordClick',
'onHideFileListChange',
'onAllowPublicUploadChange' 'onAllowPublicUploadChange'
); );
}, },
@ -210,6 +223,20 @@
}); });
}, },
onHideFileListChange: function () {
var $checkbox = this.$('.hideFileListCheckbox');
$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
var permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ;
if ($checkbox.is(':checked')) {
permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE;
}
this.model.saveLinkShare({
permissions: permissions
});
},
render: function() { render: function() {
var linkShareTemplate = this.template(); var linkShareTemplate = this.template();
var resharingAllowed = this.model.sharePermissionPossible(); var resharingAllowed = this.model.sharePermissionPossible();
@ -237,6 +264,13 @@
publicUploadChecked = 'checked="checked"'; publicUploadChecked = 'checked="checked"';
} }
var hideFileList = publicUploadChecked;
var hideFileListChecked = '';
if(this.model.isHideFileListSet()) {
hideFileListChecked = 'checked="checked"';
}
var isLinkShare = this.model.get('linkShare').isLinkShare; var isLinkShare = this.model.get('linkShare').isLinkShare;
var isPasswordSet = !!this.model.get('linkShare').password; var isPasswordSet = !!this.model.get('linkShare').password;
var showPasswordCheckBox = isLinkShare var showPasswordCheckBox = isLinkShare
@ -246,6 +280,7 @@
this.$el.html(linkShareTemplate({ this.$el.html(linkShareTemplate({
cid: this.cid, cid: this.cid,
shareAllowed: true, shareAllowed: true,
hideFileList: hideFileList,
isLinkShare: isLinkShare, isLinkShare: isLinkShare,
shareLinkURL: this.model.get('linkShare').link, shareLinkURL: this.model.get('linkShare').link,
linkShareLabel: t('core', 'Share link'), linkShareLabel: t('core', 'Share link'),
@ -257,7 +292,9 @@
showPasswordCheckBox: showPasswordCheckBox, showPasswordCheckBox: showPasswordCheckBox,
publicUpload: publicUpload && isLinkShare, publicUpload: publicUpload && isLinkShare,
publicUploadChecked: publicUploadChecked, publicUploadChecked: publicUploadChecked,
hideFileListChecked: hideFileListChecked,
publicUploadLabel: t('core', 'Allow editing'), publicUploadLabel: t('core', 'Allow editing'),
hideFileListLabel: t('core', 'Hide file listing'),
mailPublicNotificationEnabled: isLinkShare && this.configModel.isMailPublicNotificationEnabled(), mailPublicNotificationEnabled: isLinkShare && this.configModel.isMailPublicNotificationEnabled(),
mailPrivatePlaceholder: t('core', 'Email link to person'), mailPrivatePlaceholder: t('core', 'Email link to person'),
mailButtonText: t('core', 'Send') mailButtonText: t('core', 'Send')

View file

@ -272,6 +272,13 @@
return this.get('allowPublicUploadStatus'); return this.get('allowPublicUploadStatus');
}, },
/**
* @returns {boolean}
*/
isHideFileListSet: function() {
return this.get('hideFileListStatus');
},
/** /**
* @returns {boolean} * @returns {boolean}
*/ */
@ -685,6 +692,16 @@
}); });
} }
var hideFileListStatus = false;
if(!_.isUndefined(data.shares)) {
$.each(data.shares, function (key, value) {
if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
hideFileListStatus = (value.permissions & OC.PERMISSION_READ) ? false : true;
return true;
}
});
}
/** @type {OC.Share.Types.ShareInfo[]} **/ /** @type {OC.Share.Types.ShareInfo[]} **/
var shares = _.map(data.shares, function(share) { var shares = _.map(data.shares, function(share) {
// properly parse some values because sometimes the server // properly parse some values because sometimes the server
@ -757,7 +774,8 @@
shares: shares, shares: shares,
linkShare: linkShare, linkShare: linkShare,
permissions: permissions, permissions: permissions,
allowPublicUploadStatus: allowPublicUploadStatus allowPublicUploadStatus: allowPublicUploadStatus,
hideFileListStatus: hideFileListStatus
}; };
}, },

View file

@ -239,8 +239,11 @@ class Manager implements IManager {
throw new GenericShareException($message_t, $message_t, 404); throw new GenericShareException($message_t, $message_t, 404);
} }
// Check that read permissions are always set // Check that read permissions are always set
if (($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) { // Link shares are allowed to have no read permissions to allow upload to hidden folders
if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK &&
($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
throw new \InvalidArgumentException('Shares need at least read permissions'); throw new \InvalidArgumentException('Shares need at least read permissions');
} }

View file

@ -670,7 +670,6 @@ class ManagerTest extends \Test\TestCase {
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null), 'Shares need at least read permissions', true]; $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null), 'Shares need at least read permissions', true];
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null), 'Shares need at least read permissions', true]; $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null), 'Shares need at least read permissions', true];
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_LINK, $allPermssions, null, $user0, $user0, 16, null, null), 'Shares need at least read permissions', true];
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 31, null, null), null, false]; $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 31, null, null), null, false];
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 3, null, null), null, false]; $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 3, null, null), null, false];