Skip to content

Commit 4f90766

Browse files
skjnldsvjuliusknorr
authored andcommitted
Skip template picker if none available
Signed-off-by: John Molakvoæ (skjnldsv) <[email protected]>
1 parent 7e6d69d commit 4f90766

File tree

14 files changed

+294
-104
lines changed

14 files changed

+294
-104
lines changed

apps/files/lib/Controller/TemplateController.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ public function create(string $filePath, string $templatePath = '', string $temp
6464
*/
6565
public function path(string $templatePath = '', bool $copySystemTemplates = false) {
6666
try {
67-
$this->templateManager->setTemplatePath($templatePath);
68-
if ($copySystemTemplates) {
69-
$this->templateManager->initializeTemplateDirectory($templatePath);
70-
}
71-
return new DataResponse();
72-
} catch (GenericFileException $e) {
67+
$templatePath = $this->templateManager->initializeTemplateDirectory($templatePath, null, $copySystemTemplates);
68+
return new DataResponse([
69+
'template_path' => $templatePath,
70+
'templates' => $this->templateManager->listCreators()
71+
]);
72+
} catch (\Exception $e) {
7373
throw new OCSForbiddenException($e->getMessage());
7474
}
7575
}

apps/files/lib/Controller/ViewController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ public function index($dir = '', $view = '', $fileid = null, $fileNotFound = fal
294294
if (class_exists(LoadViewer::class)) {
295295
$this->eventDispatcher->dispatchTyped(new LoadViewer());
296296
}
297-
$this->initialState->provideInitialState('template_path', $this->templateManager->hasTemplateDirectory() ? $this->templateManager->getTemplatePath() : null);
297+
$this->initialState->provideInitialState('templates_path', $this->templateManager->hasTemplateDirectory() ? $this->templateManager->getTemplatePath() : null);
298298
$this->initialState->provideInitialState('templates', $this->templateManager->listCreators());
299299

300300
$params = [];

apps/files/src/components/TemplatePreview.vue

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@
3030
@change="onCheck">
3131

3232
<label :for="id" class="template-picker__label">
33-
<div class="template-picker__preview">
33+
<div class="template-picker__preview"
34+
:class="failedPreview ? 'template-picker__preview--failed' : ''">
3435
<img class="template-picker__image"
35-
:class="failedPreview ? 'template-picker__image--failed' : ''"
3636
:src="realPreviewUrl"
3737
alt=""
3838
draggable="false"
3939
@error="onFailure">
4040
</div>
4141

4242
<span class="template-picker__title">
43-
{{ basename }}
43+
{{ nameWithoutExt }}
4444
</span>
4545
</label>
4646
</li>
@@ -100,14 +100,22 @@ export default {
100100
},
101101
102102
computed: {
103+
/**
104+
* Strip away extension from name
105+
* @returns {string}
106+
*/
107+
nameWithoutExt() {
108+
return this.basename.indexOf('.') > -1 ? this.basename.split('.').slice(0, -1).join('.') : this.basename
109+
},
110+
103111
id() {
104112
return `template-picker-${this.fileid}`
105113
},
106114
107115
realPreviewUrl() {
108116
// If original preview failed, fallback to mime icon
109117
if (this.failedPreview && this.mimeIcon) {
110-
return generateUrl(this.mimeIcon)
118+
return this.mimeIcon
111119
}
112120
113121
if (this.previewUrl) {
@@ -149,7 +157,6 @@ export default {
149157
align-items: center;
150158
flex: 1 1;
151159
flex-direction: column;
152-
margin: var(--margin);
153160
154161
&, * {
155162
cursor: pointer;
@@ -162,32 +169,42 @@ export default {
162169
}
163170
164171
&__preview {
165-
display: flex;
172+
display: block;
166173
overflow: hidden;
167174
// Stretch so all entries are the same width
168175
flex: 1 1;
169176
width: var(--width);
170-
min-height: var(--width);
177+
min-height: var(--height);
171178
max-height: var(--height);
172-
padding: var(--margin);
179+
padding: 0;
173180
border: var(--border) solid var(--color-border);
174181
border-radius: var(--border-radius-large);
175182
176183
input:checked + label > & {
177184
border-color: var(--color-primary);
178185
}
186+
187+
&--failed {
188+
// Make sure to properly center fallback icon
189+
display: flex;
190+
}
179191
}
180192
181193
&__image {
182194
max-width: 100%;
183195
background-color: var(--color-main-background);
184196
185-
&--failed {
186-
width: calc(var(--margin) * 8);
187-
// Center mime icon
188-
margin: auto;
189-
background-color: transparent !important;
190-
}
197+
object-fit: cover;
198+
}
199+
200+
// Failed preview, fallback to mime icon
201+
&__preview--failed &__image {
202+
width: calc(var(--margin) * 8);
203+
// Center mime icon
204+
margin: auto;
205+
background-color: transparent !important;
206+
207+
object-fit: initial;
191208
}
192209
193210
&__title {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @copyright Copyright (c) 2021 John Molakvoæ <[email protected]>
3+
*
4+
* @author John Molakvoæ <[email protected]>
5+
*
6+
* @license GNU AGPL version 3 or any later version
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
import { generateOcsUrl } from '@nextcloud/router'
24+
import axios from '@nextcloud/axios'
25+
26+
export const getTemplates = async function() {
27+
const response = await axios.get(generateOcsUrl('apps/files/api/v1', 2) + 'templates')
28+
return response.data.ocs.data
29+
}

apps/files/src/templates.js

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,14 @@
2323
import { getLoggerBuilder } from '@nextcloud/logger'
2424
import { loadState } from '@nextcloud/initial-state'
2525
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
26+
import { generateOcsUrl } from '@nextcloud/router'
27+
import { getCurrentDirectory } from './utils/davUtils'
28+
import axios from '@nextcloud/axios'
2629
import Vue from 'vue'
2730

2831
import TemplatePickerView from './views/TemplatePicker'
32+
import { getCurrentUser } from '@nextcloud/auth'
33+
import { showError } from '@nextcloud/dialogs'
2934

3035
// Set up logger
3136
const logger = getLoggerBuilder()
@@ -47,8 +52,10 @@ TemplatePickerRoot.id = 'template-picker'
4752
document.body.appendChild(TemplatePickerRoot)
4853

4954
// Retrieve and init templates
50-
const templates = loadState('files', 'templates', [])
55+
let templates = loadState('files', 'templates', [])
56+
let templatesPath = loadState('files', 'templates_path', false)
5157
logger.debug('Templates providers', templates)
58+
logger.debug('Templates folder', { templatesPath })
5259

5360
// Init vue app
5461
const View = Vue.extend(TemplatePickerView)
@@ -60,33 +67,77 @@ const TemplatePicker = new View({
6067
})
6168
TemplatePicker.$mount('#template-picker')
6269

63-
// Init template engine after load
70+
// Init template engine after load to make sure it's the last injected entry
6471
window.addEventListener('DOMContentLoaded', function() {
65-
// Init template files menu
66-
templates.forEach((provider, index) => {
67-
68-
const newTemplatePlugin = {
72+
if (!templatesPath) {
73+
logger.debug('Templates folder not initialized')
74+
const initTemplatesPlugin = {
6975
attach(menu) {
70-
const fileList = menu.fileList
71-
72-
// only attach to main file list, public view is not supported yet
73-
if (fileList.id !== 'files' && fileList.id !== 'files.public') {
74-
return
75-
}
76-
7776
// register the new menu entry
7877
menu.addMenuEntry({
79-
id: `template-new-${provider.app}-${index}`,
80-
displayName: provider.label,
81-
templateName: provider.label + provider.extension,
82-
iconClass: provider.iconClass || 'icon-file',
78+
id: 'template-init',
79+
displayName: t('files', 'Set up templates folder'),
80+
templateName: t('files', 'Templates'),
81+
iconClass: 'icon-template-add',
8382
fileType: 'file',
8483
actionHandler(name) {
85-
TemplatePicker.open(name, provider)
84+
initTemplatesFolder(name)
8685
},
8786
})
8887
},
8988
}
90-
OC.Plugins.register('OCA.Files.NewFileMenu', newTemplatePlugin)
91-
})
89+
OC.Plugins.register('OCA.Files.NewFileMenu', initTemplatesPlugin)
90+
}
91+
})
92+
93+
// Init template files menu
94+
templates.forEach((provider, index) => {
95+
const newTemplatePlugin = {
96+
attach(menu) {
97+
const fileList = menu.fileList
98+
99+
// only attach to main file list, public view is not supported yet
100+
if (fileList.id !== 'files' && fileList.id !== 'files.public') {
101+
return
102+
}
103+
104+
// register the new menu entry
105+
menu.addMenuEntry({
106+
id: `template-new-${provider.app}-${index}`,
107+
displayName: provider.label,
108+
templateName: provider.label + provider.extension,
109+
iconClass: provider.iconClass || 'icon-file',
110+
fileType: 'file',
111+
actionHandler(name) {
112+
TemplatePicker.open(name, provider)
113+
},
114+
})
115+
},
116+
}
117+
OC.Plugins.register('OCA.Files.NewFileMenu', newTemplatePlugin)
92118
})
119+
120+
/**
121+
* Init the template directory
122+
*
123+
* @param {string} name the templates folder name
124+
*/
125+
const initTemplatesFolder = async function(name) {
126+
const templatePath = (getCurrentDirectory() + `/${name}`).replace('//', '/')
127+
try {
128+
logger.debug('Initializing the templates directory', { templatePath })
129+
const response = await axios.post(generateOcsUrl('apps/files/api/v1/templates', 2) + 'path', {
130+
templatePath,
131+
copySystemTemplates: true,
132+
})
133+
134+
// Go to template directory
135+
OCA.Files.App.currentFileList.changeDirectory(templatePath, true, true)
136+
137+
templates = response.data.ocs.data.templates
138+
templatesPath = response.data.ocs.data.template_path
139+
} catch (error) {
140+
logger.error('Unable to initialize the templates directory')
141+
showError(t('files', 'Unable to initialize the templates directory'))
142+
}
143+
}

apps/files/src/utils/davUtils.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,30 @@
2323
import { generateRemoteUrl } from '@nextcloud/router'
2424
import { getCurrentUser } from '@nextcloud/auth'
2525

26-
const getRootPath = function() {
26+
export const getRootPath = function() {
2727
if (getCurrentUser()) {
2828
return generateRemoteUrl(`dav/files/${getCurrentUser().uid}`)
2929
} else {
3030
return generateRemoteUrl('webdav').replace('/remote.php', '/public.php')
3131
}
3232
}
3333

34-
const isPublic = function() {
34+
export const isPublic = function() {
3535
return !getCurrentUser()
3636
}
3737

38-
const getToken = function() {
38+
export const getToken = function() {
3939
return document.getElementById('sharingToken') && document.getElementById('sharingToken').value
4040
}
4141

42-
export { getRootPath, getToken, isPublic }
42+
/**
43+
* Return the current directory, fallback to root
44+
* @returns {string}
45+
*/
46+
export const getCurrentDirectory = function() {
47+
const currentDirInfo = OCA?.Files?.App?.currentFileList?.dirInfo
48+
|| { path: '/', name: '' }
49+
50+
// Make sure we don't have double slashes
51+
return `${currentDirInfo.path}/${currentDirInfo.name}`.replace(/\/\//gi, '/')
52+
}

0 commit comments

Comments
 (0)