mirror of
https://github.com/nextcloud/server.git
synced 2026-04-21 06:08:46 -04:00
feat(files_sharing): Migrate public share to use Vue files list
Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de> Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
e4fa996701
commit
96c8275586
15 changed files with 692 additions and 620 deletions
|
|
@ -30,8 +30,10 @@ window.OCA.Files = window.OCA.Files ?? {}
|
|||
window.OCP.Files = window.OCP.Files ?? {}
|
||||
|
||||
// Expose router
|
||||
const Router = new RouterService(router)
|
||||
Object.assign(window.OCP.Files, { Router })
|
||||
if (!window.OCP.Files.Router) {
|
||||
const Router = new RouterService(router)
|
||||
Object.assign(window.OCP.Files, { Router })
|
||||
}
|
||||
|
||||
// Init Pinia store
|
||||
Vue.use(PiniaVuePlugin)
|
||||
|
|
@ -48,6 +50,6 @@ Object.assign(window.OCA.Files.Settings, { Setting: SettingsModel })
|
|||
|
||||
const FilesAppVue = Vue.extend(FilesApp)
|
||||
new FilesAppVue({
|
||||
router,
|
||||
router: window.OCP.Files.Router.router,
|
||||
pinia,
|
||||
}).$mount('#content')
|
||||
|
|
|
|||
|
|
@ -5,25 +5,26 @@
|
|||
import type { ContentsWithRoot, File, Folder } from '@nextcloud/files'
|
||||
import type { FileStat, ResponseDataDetailed } from 'webdav'
|
||||
|
||||
import { CancelablePromise } from 'cancelable-promise'
|
||||
import { davGetDefaultPropfind, davResultToNode, davRootPath } from '@nextcloud/files'
|
||||
import { CancelablePromise } from 'cancelable-promise'
|
||||
import { join } from 'path'
|
||||
import { client } from './WebdavClient.ts'
|
||||
import logger from '../logger.ts'
|
||||
|
||||
/**
|
||||
* Slim wrapper over `@nextcloud/files` `davResultToNode` to allow using the function with `Array.map`
|
||||
* @param node The node returned by the webdav library
|
||||
* @param stat The result returned by the webdav library
|
||||
*/
|
||||
export const resultToNode = (node: FileStat): File | Folder => davResultToNode(node)
|
||||
export const resultToNode = (stat: FileStat): File | Folder => davResultToNode(stat)
|
||||
|
||||
export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> => {
|
||||
path = join(davRootPath, path)
|
||||
const controller = new AbortController()
|
||||
const propfindPayload = davGetDefaultPropfind()
|
||||
|
||||
path = `${davRootPath}${path}`
|
||||
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
onCancel(() => controller.abort())
|
||||
|
||||
try {
|
||||
const contentsResponse = await client.getDirectoryContents(path, {
|
||||
details: true,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ export default class RouterService {
|
|||
return this._router.currentRoute.params || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a protected getter only for internal use
|
||||
* @private
|
||||
*/
|
||||
get router() {
|
||||
return this._router
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a route change on the files app
|
||||
*
|
||||
|
|
|
|||
|
|
@ -91,9 +91,9 @@ import { getCurrentUser } from '@nextcloud/auth'
|
|||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
|
||||
import { File, Folder, formatFileSize } from '@nextcloud/files'
|
||||
import { File, Folder, davRemoteURL, davRootPath, formatFileSize } from '@nextcloud/files'
|
||||
import { encodePath } from '@nextcloud/paths'
|
||||
import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { ShareType } from '@nextcloud/sharing'
|
||||
import { mdiStar, mdiStarOutline } from '@mdi/js'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
|
@ -187,8 +187,7 @@ export default {
|
|||
* @return {string}
|
||||
*/
|
||||
davPath() {
|
||||
const user = this.currentUser.uid
|
||||
return generateRemoteUrl(`dav/files/${user}${encodePath(this.file)}`)
|
||||
return `${davRemoteURL}/${davRootPath}${encodePath(this.file)}`
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,11 +20,10 @@ use OCP\AppFramework\Http\Template\PublicTemplateResponse;
|
|||
use OCP\AppFramework\Http\Template\SimpleMenuAction;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\Constants;
|
||||
use OCP\Defaults;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\File;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IPreview;
|
||||
|
|
@ -34,7 +33,6 @@ use OCP\IUser;
|
|||
use OCP\IUserManager;
|
||||
use OCP\Share\IPublicShareTemplateProvider;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\Template;
|
||||
use OCP\Util;
|
||||
|
||||
class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider {
|
||||
|
|
@ -51,6 +49,7 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
|
|||
private IConfig $config,
|
||||
private IRequest $request,
|
||||
private IInitialState $initialState,
|
||||
private IAppConfig $appConfig,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -60,113 +59,142 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
|
|||
|
||||
public function renderPage(IShare $share, string $token, string $path): TemplateResponse {
|
||||
$shareNode = $share->getNode();
|
||||
$ownerName = '';
|
||||
$ownerId = '';
|
||||
|
||||
$shareTmpl = [];
|
||||
$shareTmpl['owner'] = '';
|
||||
$shareTmpl['shareOwner'] = '';
|
||||
|
||||
// Only make the share owner public if they allowed to show their name
|
||||
$owner = $this->userManager->get($share->getShareOwner());
|
||||
if ($owner instanceof IUser) {
|
||||
$ownerAccount = $this->accountManager->getAccount($owner);
|
||||
|
||||
$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
|
||||
if ($ownerName->getScope() === IAccountManager::SCOPE_PUBLISHED) {
|
||||
$shareTmpl['owner'] = $owner->getUID();
|
||||
$shareTmpl['shareOwner'] = $owner->getDisplayName();
|
||||
$this->initialState->provideInitialState('owner', $shareTmpl['owner']);
|
||||
$this->initialState->provideInitialState('ownerDisplayName', $shareTmpl['shareOwner']);
|
||||
$ownerNameProperty = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
|
||||
if ($ownerNameProperty->getScope() === IAccountManager::SCOPE_PUBLISHED) {
|
||||
$ownerName = $owner->getDisplayName();
|
||||
$ownerId = $owner->getUID();
|
||||
}
|
||||
}
|
||||
|
||||
// Provide initial state
|
||||
$this->initialState->provideInitialState('label', $share->getLabel());
|
||||
$this->initialState->provideInitialState('note', $share->getNote());
|
||||
$view = 'public-share';
|
||||
if ($shareNode instanceof File) {
|
||||
$view = 'public-file-share';
|
||||
} elseif (($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE)
|
||||
&& !($share->getPermissions() & \OCP\Constants::PERMISSION_READ)
|
||||
) {
|
||||
// share is a folder with create but no read permissions -> file drop only
|
||||
$view = 'public-file-drop';
|
||||
// Only needed for file drops
|
||||
$this->initialState->provideInitialState(
|
||||
'disclaimer',
|
||||
$this->appConfig->getValueString('core', 'shareapi_public_link_disclaimertext'),
|
||||
);
|
||||
}
|
||||
// Set up initial state
|
||||
$this->initialState->provideInitialState('isPublic', true);
|
||||
$this->initialState->provideInitialState('sharingToken', $token);
|
||||
$this->initialState->provideInitialState('filename', $shareNode->getName());
|
||||
$this->initialState->provideInitialState('view', $view);
|
||||
|
||||
$shareTmpl['filename'] = $shareNode->getName();
|
||||
$shareTmpl['directory_path'] = $share->getTarget();
|
||||
$shareTmpl['label'] = $share->getLabel();
|
||||
$shareTmpl['note'] = $share->getNote();
|
||||
$shareTmpl['mimetype'] = $shareNode->getMimetype();
|
||||
$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
|
||||
$shareTmpl['dirToken'] = $token;
|
||||
$shareTmpl['sharingToken'] = $token;
|
||||
$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
|
||||
$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
|
||||
$shareTmpl['dir'] = '';
|
||||
$shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
|
||||
$shareTmpl['fileSize'] = Util::humanFileSize($shareNode->getSize());
|
||||
$shareTmpl['hideDownload'] = $share->getHideDownload();
|
||||
// Load scripts and styles for UI
|
||||
\OCP\Util::addInitScript('files', 'init');
|
||||
\OCP\Util::addInitScript(Application::APP_ID, 'init');
|
||||
\OCP\Util::addInitScript(Application::APP_ID, 'init-public');
|
||||
\OCP\Util::addScript('files', 'main');
|
||||
\OCP\Util::addStyle('files', 'merged');
|
||||
|
||||
$hideFileList = false;
|
||||
|
||||
if ($shareNode instanceof Folder) {
|
||||
$shareIsFolder = true;
|
||||
|
||||
$folderNode = $shareNode->get($path);
|
||||
$shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
|
||||
|
||||
/*
|
||||
* The OC_Util methods require a view. This just uses the node API
|
||||
*/
|
||||
$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
|
||||
if ($freeSpace < FileInfo::SPACE_UNLIMITED) {
|
||||
$freeSpace = (int)max($freeSpace, 0);
|
||||
} else {
|
||||
$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
|
||||
}
|
||||
|
||||
$hideFileList = !($share->getPermissions() & Constants::PERMISSION_READ);
|
||||
$maxUploadFilesize = $freeSpace;
|
||||
|
||||
$folder = new Template('files', 'list', '');
|
||||
|
||||
$folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
|
||||
$folder->assign('dirToken', $token);
|
||||
$folder->assign('permissions', Constants::PERMISSION_READ);
|
||||
$folder->assign('isPublic', true);
|
||||
$folder->assign('hideFileList', $hideFileList);
|
||||
$folder->assign('publicUploadEnabled', 'no');
|
||||
// default to list view
|
||||
$folder->assign('showgridview', false);
|
||||
$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
|
||||
$folder->assign('uploadMaxHumanFilesize', Util::humanFileSize($maxUploadFilesize));
|
||||
$folder->assign('freeSpace', $freeSpace);
|
||||
$folder->assign('usedSpacePercent', 0);
|
||||
$folder->assign('trash', false);
|
||||
$shareTmpl['folder'] = $folder->fetchPage();
|
||||
} else {
|
||||
$shareIsFolder = false;
|
||||
// Add file-request script if needed
|
||||
$attributes = $share->getAttributes();
|
||||
$isFileRequest = $attributes?->getAttribute('fileRequest', 'enabled') === true;
|
||||
if ($isFileRequest) {
|
||||
Util::addScript(Application::APP_ID, 'public-file-request');
|
||||
}
|
||||
|
||||
// default to list view
|
||||
$shareTmpl['showgridview'] = false;
|
||||
// Load Viewer scripts
|
||||
if (class_exists(LoadViewer::class)) {
|
||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||
}
|
||||
|
||||
$shareTmpl['hideFileList'] = $hideFileList;
|
||||
$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
|
||||
'token' => $token,
|
||||
'filename' => $shareIsFolder ? null : $shareNode->getName()
|
||||
]);
|
||||
$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
|
||||
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
|
||||
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
|
||||
$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
|
||||
$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
|
||||
$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', '');
|
||||
$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
|
||||
// Allow external apps to register their scripts
|
||||
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
|
||||
|
||||
if ($shareTmpl['previewSupported']) {
|
||||
$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
|
||||
['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
|
||||
$ogPreview = $shareTmpl['previewImage'];
|
||||
// OpenGraph Support: http://ogp.me/
|
||||
$this->addOpenGraphHeaders($share);
|
||||
|
||||
// We just have direct previews for image files
|
||||
// CSP to allow office
|
||||
$csp = new ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
|
||||
$response = new PublicTemplateResponse(
|
||||
'files',
|
||||
'index',
|
||||
);
|
||||
$response->setContentSecurityPolicy($csp);
|
||||
// If the share has a label, use it as the title
|
||||
if ($share->getLabel() !== '') {
|
||||
$response->setHeaderTitle($share->getLabel());
|
||||
} else {
|
||||
$response->setHeaderTitle($shareNode->getName());
|
||||
}
|
||||
if ($ownerName !== '') {
|
||||
$response->setHeaderDetails($this->l10n->t('shared by %s', [$ownerName]));
|
||||
}
|
||||
|
||||
// Create the header action menu
|
||||
$headerActions = [];
|
||||
if ($view !== 'public-file-drop') {
|
||||
// The download URL is used for the "download" header action as well as in some cases for the direct link
|
||||
$downloadUrl = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
|
||||
'token' => $token,
|
||||
'filename' => ($shareNode instanceof File) ? $shareNode->getName() : null,
|
||||
]);
|
||||
|
||||
// If not a file drop, then add the download header action
|
||||
$headerActions[] = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $downloadUrl, 0, (string)$shareNode->getSize());
|
||||
|
||||
// If remote sharing is enabled also add the remote share action to the menu
|
||||
if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled() && !$share->getHideDownload()) {
|
||||
$headerActions[] = new ExternalShareMenuAction(
|
||||
// TRANSLATORS The placeholder refers to the software product name as in 'Add to your Nextcloud'
|
||||
$this->l10n->t('Add to your %s', [$this->defaults->getProductName()]),
|
||||
'icon-external',
|
||||
$ownerId,
|
||||
$ownerName,
|
||||
$shareNode->getName(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$shareUrl = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
|
||||
// By default use the share link as the direct link
|
||||
$directLink = $shareUrl;
|
||||
// Add the direct link header actions
|
||||
if ($shareNode->getMimePart() === 'image') {
|
||||
// If this is a file and especially an image directly point to the image preview
|
||||
$directLink = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
|
||||
} elseif (($share->getPermissions() & \OCP\Constants::PERMISSION_READ) && !$share->getHideDownload()) {
|
||||
// Can read and no download restriction, so just download it
|
||||
$directLink = $downloadUrl ?? $shareUrl;
|
||||
}
|
||||
$headerActions[] = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $directLink);
|
||||
$response->setHeaderActions($headerActions);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add OpenGraph headers to response for preview
|
||||
* @param IShare $share The share for which to add the headers
|
||||
*/
|
||||
protected function addOpenGraphHeaders(IShare $share): void {
|
||||
$shareNode = $share->getNode();
|
||||
$token = $share->getToken();
|
||||
$shareUrl = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
|
||||
|
||||
// Handle preview generation for OpenGraph
|
||||
if ($this->previewManager->isMimeSupported($shareNode->getMimetype())) {
|
||||
// For images we can use direct links
|
||||
if ($shareNode->getMimePart() === 'image') {
|
||||
$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
|
||||
|
||||
$ogPreview = $shareTmpl['previewURL'];
|
||||
|
||||
//Whatapp is kind of picky about their size requirements
|
||||
$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
|
||||
// Whatsapp is kind of picky about their size requirements
|
||||
if ($this->request->isUserAgent(['/^WhatsApp/'])) {
|
||||
$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
|
||||
'token' => $token,
|
||||
|
|
@ -175,93 +203,28 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
|
|||
'a' => true,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// For normal files use preview API
|
||||
$ogPreview = $this->urlGenerator->linkToRouteAbsolute(
|
||||
'files_sharing.PublicPreview.getPreview',
|
||||
[
|
||||
'x' => 256,
|
||||
'y' => 256,
|
||||
'file' => $share->getTarget(),
|
||||
'token' => $token,
|
||||
],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
|
||||
$ogPreview = $shareTmpl['previewImage'];
|
||||
// No preview supported, so we just add the favicon
|
||||
$ogPreview = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
|
||||
}
|
||||
|
||||
// Load files we need
|
||||
Util::addScript('files', 'semaphore');
|
||||
Util::addScript('files', 'file-upload');
|
||||
Util::addStyle('files_sharing', 'publicView');
|
||||
Util::addScript('files_sharing', 'public');
|
||||
Util::addScript('files_sharing', 'templates');
|
||||
Util::addScript('files', 'fileactions');
|
||||
Util::addScript('files', 'fileactionsmenu');
|
||||
Util::addScript('files', 'jquery.fileupload');
|
||||
Util::addScript('files_sharing', 'files_drop');
|
||||
|
||||
if (isset($shareTmpl['folder'])) {
|
||||
// JS required for folders
|
||||
Util::addStyle('files', 'merged');
|
||||
Util::addScript('files', 'filesummary');
|
||||
Util::addScript('files', 'templates');
|
||||
Util::addScript('files', 'breadcrumb');
|
||||
Util::addScript('files', 'fileinfomodel');
|
||||
Util::addScript('files', 'newfilemenu');
|
||||
Util::addScript('files', 'files');
|
||||
Util::addScript('files', 'filemultiselectmenu');
|
||||
Util::addScript('files', 'filelist');
|
||||
Util::addScript('files', 'keyboardshortcuts');
|
||||
Util::addScript('files', 'operationprogressbar');
|
||||
}
|
||||
|
||||
// Load Viewer scripts
|
||||
if (class_exists(LoadViewer::class)) {
|
||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||
}
|
||||
// OpenGraph Support: http://ogp.me/
|
||||
Util::addHeader('meta', ['property' => 'og:title', 'content' => $shareTmpl['filename']]);
|
||||
Util::addHeader('meta', ['property' => 'og:title', 'content' => $shareNode->getName()]);
|
||||
Util::addHeader('meta', ['property' => 'og:description', 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
|
||||
Util::addHeader('meta', ['property' => 'og:site_name', 'content' => $this->defaults->getName()]);
|
||||
Util::addHeader('meta', ['property' => 'og:url', 'content' => $shareTmpl['shareUrl']]);
|
||||
Util::addHeader('meta', ['property' => 'og:url', 'content' => $shareUrl]);
|
||||
Util::addHeader('meta', ['property' => 'og:type', 'content' => 'object']);
|
||||
Util::addHeader('meta', ['property' => 'og:image', 'content' => $ogPreview]);
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
|
||||
|
||||
$csp = new ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
|
||||
$response = new PublicTemplateResponse(Application::APP_ID, 'public', $shareTmpl);
|
||||
$response->setHeaderTitle($shareTmpl['filename']);
|
||||
if ($shareTmpl['shareOwner'] !== '') {
|
||||
$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
|
||||
}
|
||||
|
||||
// If the share has a label, use it as the title
|
||||
if ($shareTmpl['label'] !== '') {
|
||||
$response->setHeaderTitle($shareTmpl['label']);
|
||||
}
|
||||
|
||||
$isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== Constants::PERMISSION_CREATE;
|
||||
|
||||
if ($isNoneFileDropFolder && !$share->getHideDownload()) {
|
||||
Util::addScript('files_sharing', 'public_note');
|
||||
|
||||
$download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 0, $shareTmpl['fileSize']);
|
||||
$downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 0, $shareTmpl['fileSize']);
|
||||
$directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
|
||||
// TRANSLATORS The placeholder refers to the software product name as in 'Add to your Nextcloud'
|
||||
$externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your %s', [$this->defaults->getProductName()]), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
|
||||
|
||||
$responseComposer = [];
|
||||
|
||||
if ($shareIsFolder) {
|
||||
$responseComposer[] = $downloadAll;
|
||||
} else {
|
||||
$responseComposer[] = $download;
|
||||
}
|
||||
$responseComposer[] = $directLink;
|
||||
if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
|
||||
$responseComposer[] = $externalShare;
|
||||
}
|
||||
|
||||
$response->setHeaderActions($responseComposer);
|
||||
}
|
||||
|
||||
$response->setContentSecurityPolicy($csp);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
apps/files_sharing/src/init-public.ts
Normal file
28
apps/files_sharing/src/init-public.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { getNavigation, registerDavProperty } from '@nextcloud/files'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import registerFileDropView from './views/publicFileDrop.ts'
|
||||
import registerPublicShareView from './views/publicShare.ts'
|
||||
import registerPublicFileShareView from './views/publicFileShare.ts'
|
||||
import RouterService from '../../files/src/services/RouterService'
|
||||
import router from './router'
|
||||
|
||||
registerFileDropView()
|
||||
registerPublicShareView()
|
||||
registerPublicFileShareView()
|
||||
|
||||
registerDavProperty('nc:share-attributes', { nc: 'http://nextcloud.org/ns' })
|
||||
registerDavProperty('oc:share-types', { oc: 'http://owncloud.org/ns' })
|
||||
registerDavProperty('ocs:share-permissions', { ocs: 'http://open-collaboration-services.org/ns' })
|
||||
|
||||
// Get the current view from state and set it active
|
||||
const view = loadState<string>('files_sharing', 'view')
|
||||
const navigation = getNavigation()
|
||||
navigation.setActive(navigation.views.find(({ id }) => id === view) ?? null)
|
||||
|
||||
// Force our own router
|
||||
window.OCP.Files = window.OCP.Files ?? {}
|
||||
window.OCP.Files.Router = new RouterService(router)
|
||||
54
apps/files_sharing/src/router/index.ts
Normal file
54
apps/files_sharing/src/router/index.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { RawLocation, Route } from 'vue-router'
|
||||
import type { ErrorHandler } from 'vue-router/types/router.d.ts'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import queryString from 'query-string'
|
||||
import Router from 'vue-router'
|
||||
import Vue from 'vue'
|
||||
|
||||
const view = loadState<string>('files_sharing', 'view')
|
||||
const sharingToken = loadState<string>('files_sharing', 'sharingToken')
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
// Prevent router from throwing errors when we're already on the page we're trying to go to
|
||||
const originalPush = Router.prototype.push as (to, onComplete?, onAbort?) => Promise<Route>
|
||||
Router.prototype.push = function push(to: RawLocation, onComplete?: ((route: Route) => void) | undefined, onAbort?: ErrorHandler | undefined): Promise<Route> {
|
||||
if (onComplete || onAbort) return originalPush.call(this, to, onComplete, onAbort)
|
||||
return originalPush.call(this, to).catch(err => err)
|
||||
}
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
|
||||
// if index.php is in the url AND we got this far, then it's working:
|
||||
// let's keep using index.php in the url
|
||||
base: generateUrl('/s'),
|
||||
linkActiveClass: 'active',
|
||||
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
// Pretending we're using the default view
|
||||
redirect: { name: 'filelist', params: { view, token: sharingToken } },
|
||||
},
|
||||
{
|
||||
path: '/:token',
|
||||
name: 'filelist',
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
|
||||
// Custom stringifyQuery to prevent encoding of slashes in the url
|
||||
stringifyQuery(query) {
|
||||
const result = queryString.stringify(query).replace(/%2F/gmi, '/')
|
||||
return result ? ('?' + result) : ''
|
||||
},
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
@ -18,14 +18,12 @@ const axios = vi.hoisted(() => ({ get: vi.fn() }))
|
|||
vi.mock('@nextcloud/auth')
|
||||
vi.mock('@nextcloud/axios', () => ({ default: axios }))
|
||||
|
||||
// Mock web root variable
|
||||
// Mock TAG
|
||||
beforeAll(() => {
|
||||
window.OC = {
|
||||
...window.OC,
|
||||
TAG_FAVORITE,
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
;(window as any)._oc_webroot = ''
|
||||
})
|
||||
|
||||
describe('SharingService methods definitions', () => {
|
||||
|
|
|
|||
|
|
@ -6,18 +6,17 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import type { AxiosPromise } from '@nextcloud/axios'
|
||||
import type { ContentsWithRoot } from '@nextcloud/files'
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
import type { ShareAttribute } from '../sharing'
|
||||
|
||||
import { Folder, File, type ContentsWithRoot, Permission } from '@nextcloud/files'
|
||||
import { generateOcsUrl, generateRemoteUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { Folder, File, Permission, davRemoteURL, davRootPath } from '@nextcloud/files'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import logger from './logger'
|
||||
|
||||
export const rootPath = `/files/${getCurrentUser()?.uid}`
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
|
@ -57,7 +56,7 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
|
|||
|
||||
// Generate path and strip double slashes
|
||||
const path = ocsEntry.path || ocsEntry.file_target || ocsEntry.name
|
||||
const source = generateRemoteUrl(`dav/${rootPath}/${path}`.replaceAll(/\/\//gm, '/'))
|
||||
const source = `${davRemoteURL}${davRootPath}/${path.replace(/^\/+/, '')}`
|
||||
|
||||
let mtime = ocsEntry.item_mtime ? new Date((ocsEntry.item_mtime) * 1000) : undefined
|
||||
// Prefer share time if more recent than item mtime
|
||||
|
|
@ -73,7 +72,7 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
|
|||
mtime,
|
||||
size: ocsEntry?.item_size,
|
||||
permissions: ocsEntry?.item_permissions || ocsEntry?.permissions,
|
||||
root: rootPath,
|
||||
root: davRootPath,
|
||||
attributes: {
|
||||
...ocsEntry,
|
||||
'has-preview': hasPreview,
|
||||
|
|
@ -217,7 +216,7 @@ export const getContents = async (sharedWithYou = true, sharedWithOthers = true,
|
|||
return {
|
||||
folder: new Folder({
|
||||
id: 0,
|
||||
source: generateRemoteUrl('dav' + rootPath),
|
||||
source: `${davRemoteURL}${davRootPath}`,
|
||||
owner: getCurrentUser()?.uid || null,
|
||||
}),
|
||||
contents,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<NcEmptyContent class="file-drop-empty-content"
|
||||
data-cy-files-sharing-file-drop
|
||||
:name="t('files_sharing', 'File drop')">
|
||||
<template #icon>
|
||||
<NcIconSvgWrapper :svg="svgCloudUpload" />
|
||||
</template>
|
||||
<template #description>
|
||||
{{ t('files_sharing', 'Upload files to {foldername}.', { foldername }) }}
|
||||
{{ disclaimer === '' ? '' : t('files_sharing', 'By uploading files, you agree to the terms of service.') }}
|
||||
</template>
|
||||
<template #action>
|
||||
<template v-if="disclaimer">
|
||||
<!-- Terms of service if enabled -->
|
||||
<NcButton type="primary" @click="showDialog = true">
|
||||
{{ t('files_sharing', 'View terms of service') }}
|
||||
</NcButton>
|
||||
<NcDialog close-on-click-outside
|
||||
content-classes="terms-of-service-dialog"
|
||||
:open.sync="showDialog"
|
||||
:name="t('files_sharing', 'Terms of service')"
|
||||
:message="disclaimer" />
|
||||
</template>
|
||||
<UploadPicker allow-folders
|
||||
:content="() => []"
|
||||
no-menu
|
||||
:destination="uploadDestination"
|
||||
multiple />
|
||||
</template>
|
||||
</NcEmptyContent>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { getUploader, UploadPicker } from '@nextcloud/upload'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
|
||||
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
|
||||
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
|
||||
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
|
||||
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
|
||||
|
||||
defineProps<{
|
||||
foldername: string
|
||||
}>()
|
||||
|
||||
const disclaimer = loadState<string>('files_sharing', 'disclaimer', '')
|
||||
const showDialog = ref(false)
|
||||
const uploadDestination = getUploader().destination
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.terms-of-service-dialog) {
|
||||
min-height: min(100px, 20vh);
|
||||
}
|
||||
/* TODO fix in library */
|
||||
.file-drop-empty-content :deep(.empty-content__action) {
|
||||
display: flex;
|
||||
gap: var(--default-grid-baseline);
|
||||
}
|
||||
</style>
|
||||
59
apps/files_sharing/src/views/publicFileDrop.ts
Normal file
59
apps/files_sharing/src/views/publicFileDrop.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { VueConstructor } from 'vue'
|
||||
|
||||
import { Folder, Permission, View, davRemoteURL, davRootPath, getNavigation } from '@nextcloud/files'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
|
||||
import Vue from 'vue'
|
||||
|
||||
export default () => {
|
||||
const foldername = loadState<string>('files_sharing', 'filename')
|
||||
|
||||
let FilesViewFileDropEmptyContent: VueConstructor
|
||||
let fileDropEmptyContentInstance: Vue
|
||||
|
||||
const view = new View({
|
||||
id: 'public-file-drop',
|
||||
name: t('files_sharing', 'File drop'),
|
||||
caption: t('files_sharing', 'Upload files to {foldername}', { foldername }),
|
||||
icon: svgCloudUpload,
|
||||
order: 1,
|
||||
|
||||
emptyView: async (div: HTMLDivElement) => {
|
||||
if (FilesViewFileDropEmptyContent === undefined) {
|
||||
const { default: component } = await import('../views/FilesViewFileDropEmptyContent.vue')
|
||||
FilesViewFileDropEmptyContent = Vue.extend(component)
|
||||
}
|
||||
if (fileDropEmptyContentInstance) {
|
||||
fileDropEmptyContentInstance.$destroy()
|
||||
}
|
||||
fileDropEmptyContentInstance = new FilesViewFileDropEmptyContent({
|
||||
propsData: {
|
||||
foldername,
|
||||
},
|
||||
})
|
||||
fileDropEmptyContentInstance.$mount(div)
|
||||
},
|
||||
|
||||
getContents: async () => {
|
||||
return {
|
||||
contents: [],
|
||||
// Fake a writeonly folder as root
|
||||
folder: new Folder({
|
||||
id: 0,
|
||||
source: `${davRemoteURL}${davRootPath}`,
|
||||
root: davRootPath,
|
||||
owner: null,
|
||||
permissions: Permission.CREATE,
|
||||
}),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const Navigation = getNavigation()
|
||||
Navigation.register(view)
|
||||
}
|
||||
67
apps/files_sharing/src/views/publicFileShare.ts
Normal file
67
apps/files_sharing/src/views/publicFileShare.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { FileStat, ResponseDataDetailed } from 'webdav'
|
||||
import { Folder, Permission, View, davGetDefaultPropfind, davRemoteURL, davRootPath, getNavigation } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { CancelablePromise } from 'cancelable-promise'
|
||||
import LinkSvg from '@mdi/svg/svg/link.svg?raw'
|
||||
|
||||
import { resultToNode } from '../../../files/src/services/Files'
|
||||
import { client } from '../../../files/src/services/WebdavClient'
|
||||
import logger from '../services/logger'
|
||||
|
||||
export default () => {
|
||||
const view = new View({
|
||||
id: 'public-file-share',
|
||||
name: t('files_sharing', 'Public file share'),
|
||||
caption: t('files_sharing', 'Public shared file.'),
|
||||
|
||||
emptyTitle: t('files_sharing', 'No file'),
|
||||
emptyCaption: t('files_sharing', 'The file shared with you will show up here'),
|
||||
|
||||
icon: LinkSvg,
|
||||
order: 1,
|
||||
|
||||
getContents: () => {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
const abort = new AbortController()
|
||||
onCancel(() => abort.abort())
|
||||
try {
|
||||
const node = await client.stat(
|
||||
davRootPath,
|
||||
{
|
||||
data: davGetDefaultPropfind(),
|
||||
details: true,
|
||||
signal: abort.signal,
|
||||
},
|
||||
) as ResponseDataDetailed<FileStat>
|
||||
|
||||
resolve({
|
||||
// We only have one file as the content
|
||||
contents: [resultToNode(node.data)],
|
||||
// Fake a readonly folder as root
|
||||
folder: new Folder({
|
||||
id: 0,
|
||||
source: `${davRemoteURL}${davRootPath}`,
|
||||
root: davRootPath,
|
||||
owner: null,
|
||||
permissions: Permission.READ,
|
||||
attributes: {
|
||||
// Ensure the share note is set on the root
|
||||
note: node.data.props?.note,
|
||||
},
|
||||
}),
|
||||
})
|
||||
} catch (e) {
|
||||
logger.error(e as Error)
|
||||
reject(e as Error)
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const Navigation = getNavigation()
|
||||
Navigation.register(view)
|
||||
}
|
||||
28
apps/files_sharing/src/views/publicShare.ts
Normal file
28
apps/files_sharing/src/views/publicShare.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { View, getNavigation } from '@nextcloud/files'
|
||||
import LinkSvg from '@mdi/svg/svg/link.svg?raw'
|
||||
|
||||
import { getContents } from '../../../files/src/services/Files'
|
||||
|
||||
export default () => {
|
||||
const view = new View({
|
||||
id: 'public-share',
|
||||
name: t('files_sharing', 'Public share'),
|
||||
caption: t('files_sharing', 'Public shared files.'),
|
||||
|
||||
emptyTitle: t('files_sharing', 'No files'),
|
||||
emptyCaption: t('files_sharing', 'Files and folders shared with you will show up here'),
|
||||
|
||||
icon: LinkSvg,
|
||||
order: 1,
|
||||
|
||||
getContents,
|
||||
})
|
||||
|
||||
const Navigation = getNavigation()
|
||||
Navigation.register(view)
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ use OCP\EventDispatcher\IEventDispatcher;
|
|||
use OCP\Files\File;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\Storage;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IPreview;
|
||||
|
|
@ -50,41 +50,27 @@ use PHPUnit\Framework\MockObject\MockObject;
|
|||
* @package OCA\Files_Sharing\Controllers
|
||||
*/
|
||||
class ShareControllerTest extends \Test\TestCase {
|
||||
/** @var string */
|
||||
private $user;
|
||||
/** @var string */
|
||||
private $oldUser;
|
||||
|
||||
/** @var string */
|
||||
private $appName = 'files_sharing';
|
||||
/** @var ShareController */
|
||||
private $shareController;
|
||||
/** @var IURLGenerator|MockObject */
|
||||
private $urlGenerator;
|
||||
/** @var ISession|MockObject */
|
||||
private $session;
|
||||
/** @var \OCP\IPreview|MockObject */
|
||||
private $previewManager;
|
||||
/** @var \OCP\IConfig|MockObject */
|
||||
private $config;
|
||||
/** @var \OC\Share20\Manager|MockObject */
|
||||
private $shareManager;
|
||||
/** @var IUserManager|MockObject */
|
||||
private $userManager;
|
||||
/** @var FederatedShareProvider|MockObject */
|
||||
private $federatedShareProvider;
|
||||
/** @var IAccountManager|MockObject */
|
||||
private $accountManager;
|
||||
/** @var IEventDispatcher|MockObject */
|
||||
private $eventDispatcher;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var ISecureRandom */
|
||||
private $secureRandom;
|
||||
/** @var Defaults|MockObject */
|
||||
private $defaults;
|
||||
/** @var IPublicShareTemplateFactory|MockObject */
|
||||
private $publicShareTemplateFactory;
|
||||
private string $user;
|
||||
private string $oldUser;
|
||||
private string $appName = 'files_sharing';
|
||||
private ShareController $shareController;
|
||||
|
||||
private IL10N&MockObject $l10n;
|
||||
private IConfig&MockObject $config;
|
||||
private ISession&MockObject $session;
|
||||
private Defaults&MockObject $defaults;
|
||||
private IAppConfig&MockObject $appConfig;
|
||||
private Manager&MockObject $shareManager;
|
||||
private IPreview&MockObject $previewManager;
|
||||
private IUserManager&MockObject $userManager;
|
||||
private IInitialState&MockObject $initialState;
|
||||
private IURLGenerator&MockObject $urlGenerator;
|
||||
private ISecureRandom&MockObject $secureRandom;
|
||||
private IAccountManager&MockObject $accountManager;
|
||||
private IEventDispatcher&MockObject $eventDispatcher;
|
||||
private FederatedShareProvider&MockObject $federatedShareProvider;
|
||||
private IPublicShareTemplateFactory&MockObject $publicShareTemplateFactory;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
|
@ -95,7 +81,9 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
$this->session = $this->createMock(ISession::class);
|
||||
$this->previewManager = $this->createMock(IPreview::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->initialState = $this->createMock(IInitialState::class);
|
||||
$this->federatedShareProvider = $this->createMock(FederatedShareProvider::class);
|
||||
$this->federatedShareProvider->expects($this->any())
|
||||
->method('isOutgoingServer2serverShareEnabled')->willReturn(true);
|
||||
|
|
@ -122,7 +110,8 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
$this->defaults,
|
||||
$this->config,
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IInitialState::class)
|
||||
$this->initialState,
|
||||
$this->appConfig,
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -246,29 +235,32 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
->with($owner)
|
||||
->willReturn($account);
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
$share->setId(42);
|
||||
$share->setPassword('password')
|
||||
/** @var Manager */
|
||||
$manager = \OCP\Server::get(Manager::class);
|
||||
$share = $manager->newShare();
|
||||
$share->setId(42)
|
||||
->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE)
|
||||
->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setSharedBy('initiatorUID')
|
||||
->setNode($file)
|
||||
->setNote($note)
|
||||
->setTarget("/$filename");
|
||||
->setTarget("/$filename")
|
||||
->setToken('token');
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
|
||||
$this->urlGenerator->expects($this->exactly(3))
|
||||
$this->urlGenerator->expects(self::atLeast(2))
|
||||
->method('linkToRouteAbsolute')
|
||||
->withConsecutive(
|
||||
['files_sharing.sharecontroller.downloadShare', ['token' => 'token', 'filename' => $filename]],
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token']],
|
||||
['files_sharing.PublicPreview.getPreview', ['token' => 'token', 'x' => 200, 'y' => 200, 'file' => '/'.$filename]],
|
||||
)->willReturnOnConsecutiveCalls(
|
||||
'downloadURL',
|
||||
'shareUrl',
|
||||
'previewImage',
|
||||
);
|
||||
->willReturnMap([
|
||||
// every file has the show show share url in the opengraph url prop
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'],
|
||||
// this share is not an image to the default preview is used
|
||||
['files_sharing.PublicPreview.getPreview', ['x' => 256, 'y' => 256, 'file' => $share->getTarget(), 'token' => 'token'], 'previewUrl'],
|
||||
// for the direct link
|
||||
['files_sharing.sharecontroller.downloadShare', ['token' => 'token', 'filename' => $filename ], 'downloadUrl'],
|
||||
]);
|
||||
|
||||
$this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true);
|
||||
|
||||
|
|
@ -281,19 +273,12 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
['preview_max_y', 1024, 1024],
|
||||
]
|
||||
);
|
||||
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
|
||||
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('My disclaimer text');
|
||||
|
||||
$this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) {
|
||||
if ($uid === 'ownerUID') {
|
||||
|
|
@ -325,47 +310,175 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
->method('getProductName')
|
||||
->willReturn('Nextcloud');
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
$sharedTmplParams = [
|
||||
'owner' => 'ownerUID',
|
||||
'filename' => $filename,
|
||||
'directory_path' => "/$filename",
|
||||
'mimetype' => 'text/plain',
|
||||
'dirToken' => 'token',
|
||||
// Ensure the correct initial state is setup
|
||||
// Shared node is a file so this is a single file share:
|
||||
$view = 'public-file-share';
|
||||
// Set up initial state
|
||||
$initialState = [];
|
||||
$this->initialState->expects(self::any())
|
||||
->method('provideInitialState')
|
||||
->willReturnCallback(function ($key, $value) use (&$initialState) {
|
||||
$initialState[$key] = $value;
|
||||
});
|
||||
$expectedInitialState = [
|
||||
'isPublic' => true,
|
||||
'sharingToken' => 'token',
|
||||
'server2serversharing' => true,
|
||||
'protected' => 'true',
|
||||
'dir' => '',
|
||||
'downloadURL' => 'downloadURL',
|
||||
'fileSize' => '33 B',
|
||||
'nonHumanFileSize' => 33,
|
||||
'maxSizeAnimateGif' => 10,
|
||||
'previewSupported' => true,
|
||||
'previewEnabled' => true,
|
||||
'previewMaxX' => 1024,
|
||||
'previewMaxY' => 1024,
|
||||
'hideFileList' => false,
|
||||
'shareOwner' => 'ownerDisplay',
|
||||
'disclaimer' => 'My disclaimer text',
|
||||
'shareUrl' => 'shareUrl',
|
||||
'previewImage' => 'previewImage',
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note,
|
||||
'hideDownload' => false,
|
||||
'showgridview' => false,
|
||||
'label' => ''
|
||||
'sharePermissions' => (Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE),
|
||||
'filename' => $filename,
|
||||
'view' => $view,
|
||||
];
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
|
||||
$this->assertEquals($expectedInitialState, $initialState);
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse($this->appName, 'public', $sharedTmplParams);
|
||||
$expectedResponse = new PublicTemplateResponse('files', 'index');
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($sharedTmplParams['filename']);
|
||||
$expectedResponse->setHeaderDetails('shared by ' . $sharedTmplParams['shareOwner']);
|
||||
$expectedResponse->setHeaderTitle($filename);
|
||||
$expectedResponse->setHeaderDetails('shared by ownerDisplay');
|
||||
$expectedResponse->setHeaderActions([
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $sharedTmplParams['downloadURL'], 0, $sharedTmplParams['fileSize']),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $sharedTmplParams['previewURL']),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $sharedTmplParams['owner'], $sharedTmplParams['shareOwner'], $sharedTmplParams['filename']),
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', 'downloadUrl', 0, '33'),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', 'owner', 'ownerDisplay', $filename),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'downloadUrl'),
|
||||
]);
|
||||
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testShowFileDropShare() {
|
||||
$filename = 'folder1';
|
||||
|
||||
$this->shareController->setToken('token');
|
||||
|
||||
$owner = $this->createMock(IUser::class);
|
||||
$owner->method('getDisplayName')->willReturn('ownerDisplay');
|
||||
$owner->method('getUID')->willReturn('ownerUID');
|
||||
$owner->method('isEnabled')->willReturn(true);
|
||||
|
||||
$initiator = $this->createMock(IUser::class);
|
||||
$initiator->method('getDisplayName')->willReturn('initiatorDisplay');
|
||||
$initiator->method('getUID')->willReturn('initiatorUID');
|
||||
$initiator->method('isEnabled')->willReturn(true);
|
||||
|
||||
$file = $this->createMock(Folder::class);
|
||||
$file->method('isReadable')->willReturn(true);
|
||||
$file->method('isShareable')->willReturn(true);
|
||||
$file->method('getId')->willReturn(1234);
|
||||
$file->method('getName')->willReturn($filename);
|
||||
|
||||
$accountName = $this->createMock(IAccountProperty::class);
|
||||
$accountName->method('getScope')
|
||||
->willReturn(IAccountManager::SCOPE_PUBLISHED);
|
||||
$account = $this->createMock(IAccount::class);
|
||||
$account->method('getProperty')
|
||||
->with(IAccountManager::PROPERTY_DISPLAYNAME)
|
||||
->willReturn($accountName);
|
||||
$this->accountManager->expects($this->once())
|
||||
->method('getAccount')
|
||||
->with($owner)
|
||||
->willReturn($account);
|
||||
|
||||
/** @var Manager */
|
||||
$manager = \OCP\Server::get(Manager::class);
|
||||
$share = $manager->newShare();
|
||||
$share->setId(42)
|
||||
->setPermissions(Constants::PERMISSION_CREATE)
|
||||
->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setSharedBy('initiatorUID')
|
||||
->setNode($file)
|
||||
->setTarget("/$filename")
|
||||
->setToken('token');
|
||||
|
||||
$this->appConfig
|
||||
->expects($this->once())
|
||||
->method('getValueString')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', '')
|
||||
->willReturn('My disclaimer text');
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
|
||||
$this->urlGenerator->expects(self::atLeastOnce())
|
||||
->method('linkToRouteAbsolute')
|
||||
->willReturnMap([
|
||||
// every file has the show show share url in the opengraph url prop
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'],
|
||||
// there is no preview or folders so no other link for opengraph
|
||||
]);
|
||||
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturnMap(
|
||||
[
|
||||
['max_filesize_animated_gifs_public_sharing', 10, 10],
|
||||
['enable_previews', true, true],
|
||||
['preview_max_x', 1024, 1024],
|
||||
['preview_max_y', 1024, 1024],
|
||||
]
|
||||
);
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
|
||||
$this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) {
|
||||
if ($uid === 'ownerUID') {
|
||||
return $owner;
|
||||
}
|
||||
if ($uid === 'initiatorUID') {
|
||||
return $initiator;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->eventDispatcher->method('dispatchTyped')->with(
|
||||
$this->callback(function ($event) use ($share) {
|
||||
if ($event instanceof BeforeTemplateRenderedEvent) {
|
||||
return $event->getShare() === $share;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
$this->l10n->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
// Set up initial state
|
||||
$initialState = [];
|
||||
$this->initialState->expects(self::any())
|
||||
->method('provideInitialState')
|
||||
->willReturnCallback(function ($key, $value) use (&$initialState) {
|
||||
$initialState[$key] = $value;
|
||||
});
|
||||
$expectedInitialState = [
|
||||
'isPublic' => true,
|
||||
'sharingToken' => 'token',
|
||||
'sharePermissions' => Constants::PERMISSION_CREATE,
|
||||
'filename' => $filename,
|
||||
'view' => 'public-file-drop',
|
||||
'disclaimer' => 'My disclaimer text',
|
||||
];
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
|
||||
$this->assertEquals($expectedInitialState, $initialState);
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse('files', 'index');
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($filename);
|
||||
$expectedResponse->setHeaderDetails('shared by ownerDisplay');
|
||||
$expectedResponse->setHeaderActions([
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'shareUrl'),
|
||||
]);
|
||||
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
|
|
@ -407,29 +520,31 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
->with($owner)
|
||||
->willReturn($account);
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
/** @var IShare */
|
||||
$share = \OCP\Server::get(Manager::class)->newShare();
|
||||
$share->setId(42);
|
||||
$share->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setSharedBy('initiatorUID')
|
||||
->setNode($file)
|
||||
->setNote($note)
|
||||
->setToken('token')
|
||||
->setPermissions(\OCP\Constants::PERMISSION_ALL & ~\OCP\Constants::PERMISSION_SHARE)
|
||||
->setTarget("/$filename");
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
|
||||
$this->urlGenerator->expects($this->exactly(3))
|
||||
$this->urlGenerator->expects(self::atLeast(2))
|
||||
->method('linkToRouteAbsolute')
|
||||
->withConsecutive(
|
||||
['files_sharing.sharecontroller.downloadShare', ['token' => 'token', 'filename' => $filename]],
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token']],
|
||||
['files_sharing.PublicPreview.getPreview', ['token' => 'token', 'x' => 200, 'y' => 200, 'file' => '/'.$filename]],
|
||||
)->willReturnOnConsecutiveCalls(
|
||||
'downloadURL',
|
||||
'shareUrl',
|
||||
'previewImage',
|
||||
);
|
||||
->willReturnMap([
|
||||
// every file has the show show share url in the opengraph url prop
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token'], 'shareUrl'],
|
||||
// this share is not an image to the default preview is used
|
||||
['files_sharing.PublicPreview.getPreview', ['x' => 256, 'y' => 256, 'file' => $share->getTarget(), 'token' => 'token'], 'previewUrl'],
|
||||
// for the direct link
|
||||
['files_sharing.sharecontroller.downloadShare', ['token' => 'token', 'filename' => $filename ], 'downloadUrl'],
|
||||
]);
|
||||
|
||||
$this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true);
|
||||
|
||||
|
|
@ -450,11 +565,6 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('My disclaimer text');
|
||||
|
||||
$this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) {
|
||||
if ($uid === 'ownerUID') {
|
||||
|
|
@ -487,334 +597,22 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
->willReturn('Nextcloud');
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
$sharedTmplParams = [
|
||||
'owner' => '',
|
||||
'filename' => $filename,
|
||||
'directory_path' => "/$filename",
|
||||
'mimetype' => 'text/plain',
|
||||
'dirToken' => 'token',
|
||||
'sharingToken' => 'token',
|
||||
'server2serversharing' => true,
|
||||
'protected' => 'true',
|
||||
'dir' => '',
|
||||
'downloadURL' => 'downloadURL',
|
||||
'fileSize' => '33 B',
|
||||
'nonHumanFileSize' => 33,
|
||||
'maxSizeAnimateGif' => 10,
|
||||
'previewSupported' => true,
|
||||
'previewEnabled' => true,
|
||||
'previewMaxX' => 1024,
|
||||
'previewMaxY' => 1024,
|
||||
'hideFileList' => false,
|
||||
'shareOwner' => '',
|
||||
'disclaimer' => 'My disclaimer text',
|
||||
'shareUrl' => 'shareUrl',
|
||||
'previewImage' => 'previewImage',
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note,
|
||||
'hideDownload' => false,
|
||||
'showgridview' => false,
|
||||
'label' => ''
|
||||
];
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse($this->appName, 'public', $sharedTmplParams);
|
||||
$expectedResponse = new PublicTemplateResponse('files', 'index');
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($sharedTmplParams['filename']);
|
||||
$expectedResponse->setHeaderTitle($filename);
|
||||
$expectedResponse->setHeaderDetails('');
|
||||
$expectedResponse->setHeaderActions([
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $sharedTmplParams['downloadURL'], 0, $sharedTmplParams['fileSize']),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $sharedTmplParams['previewURL']),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $sharedTmplParams['owner'], $sharedTmplParams['shareOwner'], $sharedTmplParams['filename']),
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', 'downloadUrl', 0, '33'),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', 'owner', 'ownerDisplay', $filename),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'downloadUrl'),
|
||||
]);
|
||||
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testShowShareHideDownload() {
|
||||
$note = 'personal note';
|
||||
$filename = 'file1.txt';
|
||||
|
||||
$this->shareController->setToken('token');
|
||||
|
||||
$owner = $this->getMockBuilder(IUser::class)->getMock();
|
||||
$owner->method('getDisplayName')->willReturn('ownerDisplay');
|
||||
$owner->method('getUID')->willReturn('ownerUID');
|
||||
$owner->method('isEnabled')->willReturn(true);
|
||||
|
||||
$initiator = $this->createMock(IUser::class);
|
||||
$initiator->method('getDisplayName')->willReturn('initiatorDisplay');
|
||||
$initiator->method('getUID')->willReturn('initiatorUID');
|
||||
$initiator->method('isEnabled')->willReturn(true);
|
||||
|
||||
$file = $this->getMockBuilder('OCP\Files\File')->getMock();
|
||||
$file->method('getName')->willReturn($filename);
|
||||
$file->method('getMimetype')->willReturn('text/plain');
|
||||
$file->method('getSize')->willReturn(33);
|
||||
$file->method('isReadable')->willReturn(true);
|
||||
$file->method('isShareable')->willReturn(true);
|
||||
$file->method('getId')->willReturn(111);
|
||||
|
||||
$accountName = $this->createMock(IAccountProperty::class);
|
||||
$accountName->method('getScope')
|
||||
->willReturn(IAccountManager::SCOPE_PUBLISHED);
|
||||
$account = $this->createMock(IAccount::class);
|
||||
$account->method('getProperty')
|
||||
->with(IAccountManager::PROPERTY_DISPLAYNAME)
|
||||
->willReturn($accountName);
|
||||
$this->accountManager->expects($this->once())
|
||||
->method('getAccount')
|
||||
->with($owner)
|
||||
->willReturn($account);
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
$share->setId(42);
|
||||
$share->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setSharedBy('initiatorUID')
|
||||
->setNode($file)
|
||||
->setNote($note)
|
||||
->setTarget("/$filename")
|
||||
->setHideDownload(true);
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
|
||||
// Even if downloads are disabled the "downloadURL" parameter is
|
||||
// provided to the template, as it is needed to preview audio and GIF
|
||||
// files.
|
||||
$this->urlGenerator->expects($this->exactly(3))
|
||||
->method('linkToRouteAbsolute')
|
||||
->withConsecutive(
|
||||
['files_sharing.sharecontroller.downloadShare', ['token' => 'token', 'filename' => $filename]],
|
||||
['files_sharing.sharecontroller.showShare', ['token' => 'token']],
|
||||
['files_sharing.PublicPreview.getPreview', ['token' => 'token', 'x' => 200, 'y' => 200, 'file' => '/'.$filename]],
|
||||
)->willReturnOnConsecutiveCalls(
|
||||
'downloadURL',
|
||||
'shareUrl',
|
||||
'previewImage',
|
||||
);
|
||||
|
||||
$this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true);
|
||||
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturnMap(
|
||||
[
|
||||
['max_filesize_animated_gifs_public_sharing', 10, 10],
|
||||
['enable_previews', true, true],
|
||||
['preview_max_x', 1024, 1024],
|
||||
['preview_max_y', 1024, 1024],
|
||||
]
|
||||
);
|
||||
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
|
||||
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('My disclaimer text');
|
||||
|
||||
$this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) {
|
||||
if ($uid === 'ownerUID') {
|
||||
return $owner;
|
||||
}
|
||||
if ($uid === 'initiatorUID') {
|
||||
return $initiator;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->eventDispatcher->method('dispatchTyped')->with(
|
||||
$this->callback(function ($event) use ($share) {
|
||||
if ($event instanceof BeforeTemplateRenderedEvent) {
|
||||
return $event->getShare() === $share;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
$this->l10n->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
$sharedTmplParams = [
|
||||
'owner' => 'ownerUID',
|
||||
'filename' => $filename,
|
||||
'directory_path' => "/$filename",
|
||||
'mimetype' => 'text/plain',
|
||||
'dirToken' => 'token',
|
||||
'sharingToken' => 'token',
|
||||
'server2serversharing' => true,
|
||||
'protected' => 'true',
|
||||
'dir' => '',
|
||||
'downloadURL' => 'downloadURL',
|
||||
'fileSize' => '33 B',
|
||||
'nonHumanFileSize' => 33,
|
||||
'maxSizeAnimateGif' => 10,
|
||||
'previewSupported' => true,
|
||||
'previewEnabled' => true,
|
||||
'previewMaxX' => 1024,
|
||||
'previewMaxY' => 1024,
|
||||
'hideFileList' => false,
|
||||
'shareOwner' => 'ownerDisplay',
|
||||
'disclaimer' => 'My disclaimer text',
|
||||
'shareUrl' => 'shareUrl',
|
||||
'previewImage' => 'previewImage',
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note,
|
||||
'hideDownload' => true,
|
||||
'showgridview' => false,
|
||||
'label' => ''
|
||||
];
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse($this->appName, 'public', $sharedTmplParams);
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($sharedTmplParams['filename']);
|
||||
$expectedResponse->setHeaderDetails('shared by ' . $sharedTmplParams['shareOwner']);
|
||||
$expectedResponse->setHeaderActions([]);
|
||||
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks file drop shares:
|
||||
* - there must not be any header action
|
||||
* - the template param "hideFileList" should be true
|
||||
*
|
||||
* @test
|
||||
* @return void
|
||||
*/
|
||||
public function testShareFileDrop() {
|
||||
$this->shareController->setToken('token');
|
||||
|
||||
$owner = $this->getMockBuilder(IUser::class)->getMock();
|
||||
$owner->method('getDisplayName')->willReturn('ownerDisplay');
|
||||
$owner->method('getUID')->willReturn('ownerUID');
|
||||
$owner->method('isEnabled')->willReturn(true);
|
||||
|
||||
$initiator = $this->createMock(IUser::class);
|
||||
$initiator->method('getDisplayName')->willReturn('initiatorDisplay');
|
||||
$initiator->method('getUID')->willReturn('initiatorUID');
|
||||
$initiator->method('isEnabled')->willReturn(true);
|
||||
|
||||
/* @var MockObject|Storage $storage */
|
||||
$storage = $this->getMockBuilder(Storage::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
/* @var MockObject|Folder $folder */
|
||||
$folder = $this->getMockBuilder(Folder::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$folder->method('getName')->willReturn('/fileDrop');
|
||||
$folder->method('isReadable')->willReturn(true);
|
||||
$folder->method('isShareable')->willReturn(true);
|
||||
$folder->method('getStorage')->willReturn($storage);
|
||||
$folder->method('get')->with('')->willReturn($folder);
|
||||
$folder->method('getSize')->willReturn(1337);
|
||||
$folder->method('getId')->willReturn(111);
|
||||
|
||||
$accountName = $this->createMock(IAccountProperty::class);
|
||||
$accountName->method('getScope')
|
||||
->willReturn(IAccountManager::SCOPE_PUBLISHED);
|
||||
$account = $this->createMock(IAccount::class);
|
||||
$account->method('getProperty')
|
||||
->with(IAccountManager::PROPERTY_DISPLAYNAME)
|
||||
->willReturn($accountName);
|
||||
$this->accountManager->expects($this->once())
|
||||
->method('getAccount')
|
||||
->with($owner)
|
||||
->willReturn($account);
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
$share->setId(42);
|
||||
$share->setPermissions(Constants::PERMISSION_CREATE)
|
||||
->setShareOwner('ownerUID')
|
||||
->setSharedBy('initiatorUID')
|
||||
->setNode($folder)
|
||||
->setTarget('/fileDrop');
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
|
||||
$this->userManager->method('get')->willReturnCallback(function (string $uid) use ($owner, $initiator) {
|
||||
if ($uid === 'ownerUID') {
|
||||
return $owner;
|
||||
}
|
||||
if ($uid === 'initiatorUID') {
|
||||
return $initiator;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->l10n->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
// skip the "folder" param for tests
|
||||
$responseParams = $response->getParams();
|
||||
unset($responseParams['folder']);
|
||||
$response->setParams($responseParams);
|
||||
|
||||
$sharedTmplParams = [
|
||||
'owner' => 'ownerUID',
|
||||
'filename' => '/fileDrop',
|
||||
'directory_path' => '/fileDrop',
|
||||
'mimetype' => null,
|
||||
'dirToken' => 'token',
|
||||
'sharingToken' => 'token',
|
||||
'server2serversharing' => true,
|
||||
'protected' => 'false',
|
||||
'dir' => null,
|
||||
'downloadURL' => '',
|
||||
'fileSize' => '1 KB',
|
||||
'nonHumanFileSize' => 1337,
|
||||
'maxSizeAnimateGif' => null,
|
||||
'previewSupported' => null,
|
||||
'previewEnabled' => null,
|
||||
'previewMaxX' => null,
|
||||
'previewMaxY' => null,
|
||||
'hideFileList' => true,
|
||||
'shareOwner' => 'ownerDisplay',
|
||||
'disclaimer' => null,
|
||||
'shareUrl' => '',
|
||||
'previewImage' => '',
|
||||
'previewURL' => '',
|
||||
'note' => '',
|
||||
'hideDownload' => false,
|
||||
'showgridview' => false,
|
||||
'label' => ''
|
||||
];
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse($this->appName, 'public', $sharedTmplParams);
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($sharedTmplParams['filename']);
|
||||
$expectedResponse->setHeaderDetails('shared by ' . $sharedTmplParams['shareOwner']);
|
||||
|
||||
self::assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
|
||||
public function testShowShareInvalid() {
|
||||
$this->expectException(\OCP\Files\NotFoundException::class);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ module.exports = {
|
|||
collaboration: path.join(__dirname, 'apps/files_sharing/src', 'collaborationresourceshandler.js'),
|
||||
files_sharing_tab: path.join(__dirname, 'apps/files_sharing/src', 'files_sharing_tab.js'),
|
||||
init: path.join(__dirname, 'apps/files_sharing/src', 'init.ts'),
|
||||
'init-public': path.join(__dirname, 'apps/files_sharing/src', 'init-public.ts'),
|
||||
main: path.join(__dirname, 'apps/files_sharing/src', 'main.ts'),
|
||||
'personal-settings': path.join(__dirname, 'apps/files_sharing/src', 'personal-settings.js'),
|
||||
'public-file-request': path.join(__dirname, 'apps/files_sharing/src', 'public-file-request.ts'),
|
||||
|
|
|
|||
Loading…
Reference in a new issue