Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/files/src/newMenu/newFromTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { TemplateFile } from '../types.ts'

import { Folder, Node, Permission, addNewFileMenuEntry } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { isPublicShare } from '@nextcloud/sharing/public'
import { newNodeName } from '../utils/newNodeDialog'
import { translate as t } from '@nextcloud/l10n'
import Vue, { defineAsyncComponent } from 'vue'
Expand Down Expand Up @@ -46,7 +47,12 @@ const getTemplatePicker = async (context: Folder) => {
* Register all new-file-menu entries for all template providers
*/
export function registerTemplateEntries() {
const templates = loadState<TemplateFile[]>('files', 'templates', [])
let templates: TemplateFile[]
if (isPublicShare()) {
templates = loadState<TemplateFile[]>('files_sharing', 'templates', [])
} else {
templates = loadState<TemplateFile[]>('files', 'templates', [])
}

// Init template files menu
templates.forEach((provider, index) => {
Expand Down
124 changes: 78 additions & 46 deletions apps/files/src/views/TemplatePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ import type { TemplateFile } from '../types.ts'
import { getCurrentUser } from '@nextcloud/auth'
import { showError, spawnDialog } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import { File } from '@nextcloud/files'
import { File, Node } from '@nextcloud/files'
import { getClient, getRootPath, resultToNode, getDefaultPropfind } from '@nextcloud/files/dav'
import { translate as t } from '@nextcloud/l10n'
import { generateRemoteUrl } from '@nextcloud/router'
import { normalize, extname, join } from 'path'
Expand All @@ -64,6 +65,7 @@ import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import TemplatePreview from '../components/TemplatePreview.vue'
import TemplateFiller from '../components/TemplateFiller.vue'
import logger from '../logger.ts'
import type { FileStat, ResponseDataDetailed } from 'webdav'

const border = 2
const margin = 8
Expand Down Expand Up @@ -167,6 +169,12 @@ export default defineComponent({
this.name = name
this.provider = provider

// Skip templates logic for external users.
if (getCurrentUser() === null) {
this.onSubmit()
return
}

const templates = await getTemplates()
const fetchedProvider = templates.find((fetchedProvider) => fetchedProvider.app === provider.app && fetchedProvider.label === provider.label)
if (fetchedProvider === null) {
Expand Down Expand Up @@ -224,56 +232,80 @@ export default defineComponent({
this.name = `${this.name}${this.provider?.extension ?? ''}`
}

try {
const fileInfo = await createFromTemplate(
normalize(`${currentDirectory}/${this.name}`),
this.selectedTemplate?.filename as string ?? '',
this.selectedTemplate?.templateType as string ?? '',
templateFields,
)
logger.debug('Created new file', fileInfo)

const owner = getCurrentUser()?.uid || null
const node = new File({
id: fileInfo.fileid,
source: generateRemoteUrl(join(`dav/files/${owner}`, fileInfo.filename)),
root: `/files/${owner}`,
mime: fileInfo.mime,
mtime: new Date(fileInfo.lastmod * 1000),
owner,
size: fileInfo.size,
permissions: fileInfo.permissions,
attributes: {
// Inherit some attributes from parent folder like the mount type and real owner
'mount-type': this.parent?.attributes?.['mount-type'],
'owner-id': this.parent?.attributes?.['owner-id'],
'owner-display-name': this.parent?.attributes?.['owner-display-name'],
...fileInfo,
'has-preview': fileInfo.hasPreview,
},
})
// Create a blank file for external users as we can't use the templates.
if (getCurrentUser() === null) {
const client = getClient()
const filename = join(getRootPath(), currentDirectory, this.name ?? '')

await client.putFileContents(filename, '')
const response = await client.stat(filename, { data: getDefaultPropfind(), details: true }) as ResponseDataDetailed<FileStat>
logger.debug('Created new file', { fileInfo: response.data })

const node = resultToNode(response.data)

// Update files list
emit('files:node:created', node)

// Open the new file
window.OCP.Files.Router.goToRoute(
null, // use default route
{ view: 'files', fileid: node.fileid },
{ dir: node.dirname, openfile: 'true' },
)

// Close the picker
this.close()
} catch (error) {
logger.error('Error while creating the new file from template', { error })
showError(t('files', 'Unable to create new file from template'))
} finally {
this.loading = false
this.handleFileCreation(node)
} else {
try {
const fileInfo = await createFromTemplate(
normalize(`${currentDirectory}/${this.name}`),
this.selectedTemplate?.filename as string ?? '',
this.selectedTemplate?.templateType as string ?? '',
templateFields,
)
logger.debug('Created new file', { fileInfo })

const owner = getCurrentUser()?.uid || null
const node = new File({
id: fileInfo.fileid,
source: generateRemoteUrl(join(`dav/files/${owner}`, fileInfo.filename)),
root: `/files/${owner}`,
mime: fileInfo.mime,
mtime: new Date(fileInfo.lastmod * 1000),
owner,
size: fileInfo.size,
permissions: fileInfo.permissions,
attributes: {
// Inherit some attributes from parent folder like the mount type and real owner
'mount-type': this.parent?.attributes?.['mount-type'],
'owner-id': this.parent?.attributes?.['owner-id'],
'owner-display-name': this.parent?.attributes?.['owner-display-name'],
...fileInfo,
'has-preview': fileInfo.hasPreview,
},
})

this.handleFileCreation(node)

// Close the picker
this.close()
} catch (error) {
logger.error('Error while creating the new file from template', { error })
showError(t('files', 'Unable to create new file from template'))
} finally {
this.loading = false
}
}
},

handleFileCreation(node: Node) {
// Update files list
emit('files:node:created', node)

// Open the new file
window.OCP.Files.Router.goToRoute(
null, // use default route
{ view: 'files', fileid: node.fileid },
{ dir: node.dirname, openfile: 'true' },
)
},

async onSubmit() {
// Skip templates logic for external users.
if (getCurrentUser() === null) {
this.loading = true
return this.createFile()
}

const fileId = this.selectedTemplate?.fileid

// Only request field extraction if there is a valid template
Expand Down
4 changes: 4 additions & 0 deletions apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use OCP\Defaults;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\Template\ITemplateManager;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
Expand All @@ -49,6 +50,7 @@ public function __construct(
private Defaults $defaults,
private IConfig $config,
private IRequest $request,
private ITemplateManager $templateManager,
private IInitialState $initialState,
private IAppConfig $appConfig,
) {
Expand Down Expand Up @@ -120,6 +122,8 @@ public function renderPage(IShare $share, string $token, string $path): Template
$this->eventDispatcher->dispatchTyped(new LoadViewer());
}

$this->initialState->provideInitialState('templates', $this->templateManager->listCreators());

// Allow external apps to register their scripts
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));

Expand Down
6 changes: 6 additions & 0 deletions apps/files_sharing/tests/Controller/ShareControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Template\ITemplateManager;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
Expand Down Expand Up @@ -67,6 +68,7 @@ class ShareControllerTest extends \Test\TestCase {
private Manager&MockObject $shareManager;
private IPreview&MockObject $previewManager;
private IUserManager&MockObject $userManager;
private ITemplateManager&MockObject $templateManager;
private IInitialState&MockObject $initialState;
private IURLGenerator&MockObject $urlGenerator;
private ISecureRandom&MockObject $secureRandom;
Expand All @@ -86,6 +88,7 @@ protected function setUp(): void {
$this->config = $this->createMock(IConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->templateManager = $this->createMock(ITemplateManager::class);
$this->initialState = $this->createMock(IInitialState::class);
$this->federatedShareProvider = $this->createMock(FederatedShareProvider::class);
$this->federatedShareProvider->expects($this->any())
Expand Down Expand Up @@ -113,6 +116,7 @@ protected function setUp(): void {
$this->defaults,
$this->config,
$this->createMock(IRequest::class),
$this->templateManager,
$this->initialState,
$this->appConfig,
)
Expand Down Expand Up @@ -336,6 +340,7 @@ public function testShowShare(): void {
'fileId' => 111,
'owner' => 'ownerUID',
'ownerDisplayName' => 'ownerDisplay',
'templates' => [],
];

$response = $this->shareController->showShare();
Expand Down Expand Up @@ -482,6 +487,7 @@ public function testShowFileDropShare(): void {
'ownerDisplayName' => 'ownerDisplay',
'note' => 'The note',
'label' => 'A label',
'templates' => [],
];

$response = $this->shareController->showShare();
Expand Down
2 changes: 0 additions & 2 deletions dist/3435-3435.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/3435-3435.js.map

This file was deleted.

1 change: 0 additions & 1 deletion dist/3435-3435.js.map.license

This file was deleted.

2 changes: 2 additions & 0 deletions dist/3902-3902.js

Large diffs are not rendered by default.

File renamed without changes.
1 change: 1 addition & 0 deletions dist/3902-3902.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/3902-3902.js.map.license
4 changes: 2 additions & 2 deletions dist/files-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-init.js.map

Large diffs are not rendered by default.

Loading