Skip to content

Commit 1ff6a91

Browse files
committed
Refactor: Move personal user settings page to Vue
Signed-off-by: codewithvk <[email protected]>
1 parent 705cdf6 commit 1ff6a91

File tree

4 files changed

+355
-221
lines changed

4 files changed

+355
-221
lines changed

src/components/DocSigningField.vue

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<template>
2+
<p>
3+
<label :for="id">
4+
{{ label }}
5+
</label>
6+
<br>
7+
<textarea :id="id"
8+
v-model="localValue"
9+
:name="name" />
10+
<br>
11+
<button :id="id + 'Save'" @click="onClickSave">
12+
<span :title="saveTooltip" data-toggle="tooltip">
13+
Save
14+
</span>
15+
</button>
16+
17+
<button :id="id + 'Remove'" @click="onClickRemove">
18+
<span class="icon-delete"
19+
:title="removeTooltip"
20+
data-toggle="tooltip" />
21+
</button>
22+
</p>
23+
</template>
24+
25+
<script>
26+
export default {
27+
name: 'DocSigningField',
28+
props: {
29+
id: {
30+
type: String,
31+
required: true,
32+
},
33+
name: {
34+
type: String,
35+
default: '',
36+
},
37+
label: {
38+
type: String,
39+
required: true,
40+
},
41+
value: {
42+
type: String,
43+
default: '',
44+
},
45+
saveTooltip: {
46+
type: String,
47+
default: 'Save',
48+
},
49+
removeTooltip: {
50+
type: String,
51+
default: 'Remove',
52+
},
53+
},
54+
data() {
55+
return {
56+
localValue: this.value,
57+
}
58+
},
59+
watch: {
60+
value(newVal) {
61+
this.localValue = newVal
62+
},
63+
},
64+
methods: {
65+
onClickSave() {
66+
this.$emit('save', this.localValue)
67+
},
68+
onClickRemove() {
69+
this.$emit('remove')
70+
},
71+
},
72+
}
73+
</script>
74+
75+
<style scoped>
76+
</style>

src/components/PersonalSettings.vue

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
<template>
2+
<div id="richdocuments" class="section">
3+
<h2>
4+
{{ t('richdocuments', 'Nextcloud Office') }}
5+
</h2>
6+
7+
<span id="documents-admin-msg" class="msg" />
8+
9+
<!-- Template folder selection -->
10+
<p>
11+
<label for="templateInputField">
12+
{{ t('richdocuments', 'Select a template directory') }}
13+
</label><br>
14+
<input id="templateInputField"
15+
type="text"
16+
name="templateInputField"
17+
:value="templateFolder"
18+
disabled>
19+
<button id="templateSelectButton"
20+
@click="onTemplateSelectButtonClick">
21+
<span class="icon-folder"
22+
:title="t('richdocuments', 'Select a personal template folder')"
23+
data-toggle="tooltip" />
24+
</button>
25+
<button id="templateResetButton"
26+
@click="resetTemplate">
27+
<span class="icon-delete"
28+
:title="t('richdocuments', 'Remove personal template folder')"
29+
data-toggle="tooltip" />
30+
</button>
31+
</p>
32+
<p>
33+
<em>
34+
{{ t('richdocuments', 'Templates inside of this directory will be added to the template selector of Nextcloud Office.') }}
35+
</em>
36+
</p>
37+
38+
<!-- Zotero -->
39+
<p><strong>{{ t('richdocuments', 'Zotero') }}</strong></p>
40+
<template v-if="hasZoteroSupport">
41+
<div class="input-wrapper">
42+
<p>
43+
<label for="zoteroAPIKeyField">
44+
{{ t('richdocuments', 'Enter Zotero API Key') }}
45+
</label><br>
46+
<input id="zoteroAPIKeyField"
47+
v-model="zoteroAPIKey"
48+
type="text"
49+
name="zoteroAPIKeyField">
50+
<button id="zoteroAPIKeySave"
51+
@click="saveZoteroAPIKey">
52+
<span :title="t('richdocuments', 'Save Zotero API key')" data-toggle="tooltip">
53+
Save
54+
</span>
55+
</button>
56+
<button id="zoteroAPIKeyRemove"
57+
@click="resetZoteroAPI">
58+
<span class="icon-delete"
59+
:title="t('richdocuments', 'Remove Zotero API Key')"
60+
data-toggle="tooltip" />
61+
</button>
62+
</p>
63+
<p>
64+
<em>
65+
{{ t('richdocuments', 'To use Zotero specify your API key here. You can create your API key in your') }}
66+
<a href="https://www.zotero.org/settings/keys" target="_blank">
67+
{{ t('richdocuments', 'Zotero account API settings.') }}
68+
</a>
69+
</em>
70+
</p>
71+
</div>
72+
</template>
73+
<p v-else>
74+
<em>
75+
{{ t('richdocuments', 'This instance does not support Zotero, because the feature is missing or disabled. Please contact the administration.') }}
76+
</em>
77+
</p>
78+
79+
<!-- Document signing -->
80+
<p><strong>{{ t('richdocuments', 'Document signing') }}</strong></p>
81+
<template v-if="hasDocumentSigningSupport">
82+
<div class="input-wrapper">
83+
<!-- Cert -->
84+
<DocSigningField id="documentSigningCertField"
85+
name="documentSigningCertField"
86+
:label="t('richdocuments', 'Enter document signing cert (in PEM format)')"
87+
:value="documentSigningCert"
88+
:save-tooltip="t('richdocuments', 'Save document signing cert')"
89+
:remove-tooltip="t('richdocuments', 'Remove document signing cert')"
90+
@save="val => setDocumentSigningCert(val)"
91+
@remove="() => setDocumentSigningCert('')" />
92+
93+
<!-- Key -->
94+
<DocSigningField id="documentSigningKeyField"
95+
name="documentSigningKeyField"
96+
:label="t('richdocuments', 'Enter document signing key')"
97+
:value="documentSigningKey"
98+
:save-tooltip="t('richdocuments', 'Save document signing key')"
99+
:remove-tooltip="t('richdocuments', 'Remove document signing key')"
100+
@save="val => setDocumentSigningKey(val)"
101+
@remove="() => setDocumentSigningKey('')" />
102+
103+
<!-- CA -->
104+
<DocSigningField id="documentSigningCaField"
105+
name="documentSigningCaField"
106+
:label="t('richdocuments', 'Enter document signing CA chain')"
107+
:value="documentSigningCa"
108+
:save-tooltip="t('richdocuments', 'Save document signing CA chain')"
109+
:remove-tooltip="t('richdocuments', 'Remove document signing CA chain')"
110+
@save="val => setDocumentSigningCa(val)"
111+
@remove="() => setDocumentSigningCa('')" />
112+
113+
<p>
114+
<em>
115+
{{ t('richdocuments', 'To use document signing, specify your signing certificate, key and CA chain here.') }}
116+
</em>
117+
</p>
118+
</div>
119+
</template>
120+
<p v-else>
121+
<em>
122+
{{ t('richdocuments', 'This instance does not support document signing, because the feature is missing or disabled. Please contact the administrator.') }}
123+
</em>
124+
</p>
125+
</div>
126+
</template>
127+
128+
<script>
129+
import { generateFilePath } from '@nextcloud/router'
130+
import { showError, showSuccess } from '@nextcloud/dialogs'
131+
import DocSigningField from './DocSigningField.vue'
132+
133+
export default {
134+
name: 'PersonalSettings',
135+
components: {
136+
DocSigningField,
137+
},
138+
props: {
139+
initial: {
140+
type: Object,
141+
required: true,
142+
},
143+
},
144+
data() {
145+
return {
146+
templateFolder: this.initial.templateFolder || '',
147+
hasZoteroSupport: this.initial.hasZoteroSupport || false,
148+
zoteroAPIKey: this.initial.zoteroAPIKey || '',
149+
hasDocumentSigningSupport: this.initial.hasDocumentSigningSupport || false,
150+
documentSigningCert: this.initial.documentSigningCert || '',
151+
documentSigningKey: this.initial.documentSigningKey || '',
152+
documentSigningCa: this.initial.documentSigningCa || '',
153+
}
154+
},
155+
methods: {
156+
onTemplateSelectButtonClick() {
157+
OC.dialogs.filepicker(
158+
this.t('richdocuments', 'Select a personal template folder'),
159+
(datapath) => {
160+
this.updateSetting({ templateFolder: datapath }, () => {
161+
this.templateFolder = datapath
162+
}, () => {})
163+
},
164+
false,
165+
'httpd/unix-directory',
166+
true,
167+
OC.dialogs.FILEPICKER_TYPE_CHOOSE,
168+
)
169+
},
170+
resetTemplate() {
171+
this.updateSetting({ templateFolder: '' }, () => {
172+
this.templateFolder = ''
173+
}, () => {})
174+
},
175+
saveZoteroAPIKey() {
176+
this.updateSetting({ zoteroAPIKeyInput: this.zoteroAPIKey }, () => {
177+
showSuccess(this.t('richdocuments', 'Zotero API key saved'))
178+
}, () => {
179+
showError(this.t('richdocuments', 'Failed to update the Zotero API key'))
180+
})
181+
},
182+
resetZoteroAPI() {
183+
this.updateSetting({ zoteroAPIKeyInput: '' }, () => {
184+
this.zoteroAPIKey = ''
185+
}, () => {
186+
showError(this.t('richdocuments', 'Failed to reset the Zotero API key'))
187+
})
188+
},
189+
setDocumentSigningCert(val) {
190+
this.updateSetting({ documentSigningCertInput: val }, () => {
191+
this.documentSigningCert = val
192+
if (val === '') {
193+
showSuccess(this.t('richdocuments', 'Document signing cert removed'))
194+
} else {
195+
showSuccess(this.t('richdocuments', 'Document signing cert saved'))
196+
}
197+
}, () => {
198+
showError(this.t('richdocuments', 'Failed to update the document signing CA chain'))
199+
})
200+
},
201+
setDocumentSigningKey(val) {
202+
this.updateSetting({ documentSigningKeyInput: val }, () => {
203+
this.documentSigningKey = val
204+
if (val === '') {
205+
showSuccess(this.t('richdocuments', 'Document signing key removed'))
206+
} else {
207+
showSuccess(this.t('richdocuments', 'Document signing key saved'))
208+
}
209+
}, () => {
210+
showError(this.t('richdocuments', 'Failed to update the document signing CA chain'))
211+
})
212+
},
213+
setDocumentSigningCa(val) {
214+
this.updateSetting({ documentSigningCaInput: val }, () => {
215+
this.documentSigningCa = val
216+
if (val === '') {
217+
showSuccess(this.t('richdocuments', 'Document signing CA chain removed'))
218+
} else {
219+
showSuccess(this.t('richdocuments', 'Document signing CA chain saved'))
220+
}
221+
}, () => {
222+
showError(this.t('richdocuments', 'Failed to update the document signing CA chain'))
223+
})
224+
},
225+
226+
updateSetting(data, successCallback, errorCallback) {
227+
OC.msg.startAction('#documents-admin-msg', this.t('richdocuments', 'Saving …'))
228+
const request = new XMLHttpRequest()
229+
request.open('POST', generateFilePath('richdocuments', 'ajax', 'personal.php'), true)
230+
request.setRequestHeader('Content-Type', 'application/json')
231+
request.setRequestHeader('requesttoken', OC.requestToken)
232+
request.onload = function() {
233+
if (request.status >= 200 && request.status < 400) {
234+
const response = JSON.parse(request.response)
235+
OC.msg.finishedAction('#documents-admin-msg', response)
236+
successCallback(response)
237+
} else {
238+
errorCallback(this.response)
239+
}
240+
}
241+
request.onerror = function() {
242+
errorCallback(this.response)
243+
}
244+
request.send(JSON.stringify(data))
245+
},
246+
},
247+
}
248+
</script>
249+
250+
<style scoped>
251+
.personal-settings {
252+
padding: 2rem;
253+
font-family: Arial, sans-serif;
254+
}
255+
256+
.icon-folder::before {
257+
content: "\1F4C1";
258+
}
259+
</style>

0 commit comments

Comments
 (0)