fix: Create new files on public pages for 31+

Signed-off-by: Julius Knorr <jus@bitgrid.net>
This commit is contained in:
Julius Knorr 2025-05-22 15:38:41 +02:00
parent 6e43e3b87a
commit d82b195294
4 changed files with 143 additions and 170 deletions

View file

@ -49,7 +49,10 @@ const getFileTypes = () => {
}
const getFileType = (document) => {
return getFileTypes()[document]
return {
...getFileTypes()[document],
name: document,
}
}
export default {

View file

@ -10,16 +10,14 @@ import {
isDownloadHidden,
} from './helpers/index.js'
import { getCapabilities } from './services/capabilities.ts'
import NewFileMenu from './view/NewFileMenu.js'
import { registerNewFileMenuEntries } from './view/NewFileMenu.js'
document.addEventListener('DOMContentLoaded', () => {
if (!isPublicShare() || !OCA.Viewer) {
return
}
if (OCA.Files && OCA.Files.fileActions) {
OC.Plugins.register('OCA.Files.NewFileMenu', NewFileMenu)
}
registerNewFileMenuEntries()
const isEnabledFilesPdfViewer = getCapabilities().mimetypesNoDefaultOpen.includes('application/pdf')

View file

@ -6,16 +6,14 @@
import axios from '@nextcloud/axios'
import { generateOcsUrl, generateFilePath } from '@nextcloud/router'
import { getSharingToken } from '@nextcloud/sharing/public'
import { getCurrentDirectory } from '../helpers/filesApp.js'
export const createEmptyFile = async (mimeType, fileName, templateId = null) => {
export const createEmptyFile = async (context, mimeType, fileName, templateId = null) => {
const shareToken = getSharingToken()
const directoryPath = getCurrentDirectory()
const response = await axios.post(generateOcsUrl('apps/richdocuments/api/v1/file', 2), {
mimeType,
fileName,
directoryPath,
directoryPath: context.dirname,
shareToken,
templateId,
})

View file

@ -4,174 +4,148 @@
*/
import axios from '@nextcloud/axios'
import { getCurrentDirectory } from '../helpers/filesApp.js'
import { emit } from '@nextcloud/event-bus'
import Types from '../helpers/types.js'
import { createEmptyFile } from '../services/api.js'
import { generateUrl, generateFilePath, generateOcsUrl } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs'
import { getCapabilities } from '../services/capabilities.ts'
import { File, addNewFileMenuEntry, isFilenameValid, getUniqueName } from '@nextcloud/files'
/** @type OC.Plugin */
const NewFileMenu = {
attach(newFileMenu) {
const self = this
const document = Types.getFileType('document')
const spreadsheet = Types.getFileType('spreadsheet')
const presentation = Types.getFileType('presentation')
const drawing = Types.getFileType('drawing')
newFileMenu.addMenuEntry({
id: 'add-' + document.extension,
displayName: t('richdocuments', 'New document'),
templateName: t('richdocuments', 'New document') + '.' + document.extension,
iconClass: 'icon-filetype-document',
fileType: 'x-office-document',
actionHandler(filename) {
if (getCapabilities().templates) {
self._openTemplatePicker('document', document.mime, filename)
} else {
self._createDocument(document.mime, filename)
}
},
})
newFileMenu.addMenuEntry({
id: 'add-' + spreadsheet.extension,
displayName: t('richdocuments', 'New spreadsheet'),
templateName: t('richdocuments', 'New spreadsheet') + '.' + spreadsheet.extension,
iconClass: 'icon-filetype-spreadsheet',
fileType: 'x-office-spreadsheet',
actionHandler(filename) {
if (getCapabilities().templates) {
self._openTemplatePicker('spreadsheet', spreadsheet.mime, filename)
} else {
self._createDocument(spreadsheet.mime, filename)
}
},
})
newFileMenu.addMenuEntry({
id: 'add-' + presentation.extension,
displayName: t('richdocuments', 'New presentation'),
templateName: t('richdocuments', 'New presentation') + '.' + presentation.extension,
iconClass: 'icon-filetype-presentation',
fileType: 'x-office-presentation',
actionHandler(filename) {
if (getCapabilities().templates) {
self._openTemplatePicker('presentation', presentation.mime, filename)
} else {
self._createDocument(presentation.mime, filename)
}
},
})
newFileMenu.addMenuEntry({
id: 'add-' + drawing.extension,
displayName: t('richdocuments', 'New drawing'),
templateName: t('richdocuments', 'New drawing') + '.' + drawing.extension,
iconClass: 'icon-filetype-draw',
fileType: 'x-office-drawing',
actionHandler(filename) {
if (getCapabilities().templates) {
self._openTemplatePicker('drawing', drawing.mime, filename)
} else {
self._createDocument(drawing.mime, filename)
}
},
})
},
_createDocument(mimetype, filename, templateId = null) {
OCA.Files.Files.isFileNameValid(filename)
filename = FileList.getUniqueName(filename)
createEmptyFile(mimetype, filename, templateId).then((response) => {
if (response && response.status === 'success') {
FileList.add(response.data, { animate: true, scrollTo: true })
const fileModel = FileList.getModelForFile(filename)
const fileAction = OCA.Files.fileActions.getDefaultFileAction(fileModel.get('mimetype'), 'file', OC.PERMISSION_ALL)
fileAction.action(filename, {
$file: null,
fileId: fileModel.id,
dir: getCurrentDirectory(),
fileList: FileList,
fileActions: FileList.fileActions,
})
} else {
showError(t('core', 'Could not create file') + ': ' + response.data.message)
}
})
},
_openTemplatePicker(type, mimetype, filename) {
axios.get(generateOcsUrl(`apps/richdocuments/api/v1/templates/${type}`, 2)).then(({ data: response }) => {
if (response.ocs.data.length === 1) {
const { id } = response.ocs.data[0]
this._createDocument(mimetype, filename, id)
return
}
this._buildTemplatePicker(response.ocs.data)
.then(() => {
const self = this
const buttonlist = [{
text: t('core', 'Cancel'),
classes: 'cancel',
click() {
$(this).ocdialog('close')
},
}, {
text: t('richdocuments', 'Create'),
classes: 'primary',
click() {
const templateId = this.dataset.templateId
self._createDocument(mimetype, filename, templateId)
$(this).ocdialog('close')
},
}]
$('#template-picker').ocdialog({
closeOnEscape: true,
modal: true,
buttons: buttonlist,
})
})
})
},
_buildTemplatePicker(data) {
return axios.get(generateFilePath('richdocuments', 'templates', 'templatePicker.html')).then(({ data: tmpl }) => {
const $tmpl = $(tmpl).eq(2)
// init template picker
const $dlg = $tmpl.octemplate({
dialog_name: 'template-picker',
dialog_title: t('richdocuments', 'Select template'),
})
// create templates list
const templates = Object.values(data)
templates.forEach((template) => {
this._appendTemplateFromData($dlg[0], template)
})
$('body').append($dlg)
})
},
_appendTemplateFromData(dlg, data) {
const template = dlg.querySelector('.template-model').cloneNode(true)
template.className = ''
template.querySelector('img').src = generateUrl('apps/richdocuments/template/preview/' + data.id)
template.querySelector('h2').textContent = data.name
template.onclick = function(e) {
e.preventDefault()
dlg.dataset.templateId = data.id
const createFileTypeEntry = (type, displayName, iconClass, index) => ({
id: 'add-' + type.extension,
displayName: t('richdocuments', displayName),
iconClass,
order: 10 + index,
async handler(context, content) {
const filename = t('richdocuments', displayName) + '.' + type.extension
if (getCapabilities().templates) {
await openTemplatePicker(context, type.name, type.mime, filename, content)
} else {
await createDocument(context, type.mime, filename, null, content)
}
if (!dlg.dataset.templateId) {
dlg.dataset.templateId = data.id
}
dlg.querySelector('.template-container').appendChild(template)
},
})
export const registerNewFileMenuEntries = () => {
const fileTypes = [
{ type: Types.getFileType('document'), displayName: 'New document', iconClass: 'icon-filetype-document' },
{ type: Types.getFileType('spreadsheet'), displayName: 'New spreadsheet', iconClass: 'icon-filetype-spreadsheet' },
{ type: Types.getFileType('presentation'), displayName: 'New presentation', iconClass: 'icon-filetype-presentation' },
{ type: Types.getFileType('drawing'), displayName: 'New drawing', iconClass: 'icon-filetype-draw' },
]
fileTypes.forEach(({ type, displayName, iconClass }, index) => {
addNewFileMenuEntry(createFileTypeEntry(type, displayName, iconClass, index))
})
}
export default NewFileMenu
const createDocument = async (context, mimetype, filename, templateId = null, content = []) => {
if (!isFilenameValid(filename)) {
return
}
filename = getUniqueName(
filename,
content.map((n) => n.basename),
)
try {
const response = await createEmptyFile(context, mimetype, filename, templateId)
const { data } = response
const file = new File({
source: context.source + '/' + filename,
basename: filename,
id: data.id,
mtime: new Date(data.mtime),
mime: data.mimetype,
owner: null,
permissions: data.permissions,
root: context?.root,
})
emit('files:node:created', file)
if (response && response.status === 'success') {
OCA.Viewer.open({ path: context.dirname + '/' + filename })
} else {
showError(t('core', 'Could not create file') + ': ' + response.data.message)
}
} catch (error) {
console.error(error)
showError(t('core', 'Could not create file'))
}
}
const openTemplatePicker = async (context, type, mimetype, filename, content = []) => {
try {
const { data: response } = await axios.get(generateOcsUrl(`apps/richdocuments/api/v1/templates/${type}`, 2))
const templates = response.ocs.data
if (templates.length === 1) {
await createDocument(context, mimetype, filename, templates[0].id, content)
return
}
await showTemplatePickerDialog(context, type, mimetype, filename, templates, content)
} catch (error) {
console.error(error)
showError(t('core', 'Could not load templates'))
}
}
const showTemplatePickerDialog = async (context, type, mimetype, filename, templates, content) => {
const { data: tmpl } = await axios.get(generateFilePath('richdocuments', 'templates', 'templatePicker.html'))
const $tmpl = $(tmpl).eq(2)
const $dlg = $tmpl.octemplate({
dialog_name: 'template-picker',
dialog_title: t('richdocuments', 'Select template'),
})
templates.forEach((template) => {
appendTemplateFromData($dlg[0], template)
})
$('body').append($dlg)
const buttonlist = [
{
text: t('core', 'Cancel'),
classes: 'cancel',
click() {
$(this).ocdialog('close')
},
},
{
text: t('richdocuments', 'Create'),
classes: 'primary',
click() {
const templateId = this.dataset.templateId
createDocument(context, mimetype, filename, templateId, content)
$(this).ocdialog('close')
},
}
]
$('#template-picker').ocdialog({
closeOnEscape: true,
modal: true,
buttons: buttonlist,
})
}
const appendTemplateFromData = (dlg, data) => {
const template = dlg.querySelector('.template-model').cloneNode(true)
template.className = ''
template.querySelector('img').src = generateUrl('apps/richdocuments/template/preview/' + data.id)
template.querySelector('h2').textContent = data.name
template.onclick = function(e) {
e.preventDefault()
dlg.dataset.templateId = data.id
}
if (!dlg.dataset.templateId) {
dlg.dataset.templateId = data.id
}
dlg.querySelector('.template-container').appendChild(template)
}