|
| 1 | +<!-- |
| 2 | + - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors |
| 3 | + - SPDX-License-Identifier: AGPL-3.0-or-later |
| 4 | +--> |
| 5 | + |
| 6 | +<template> |
| 7 | + <NcSettingsSection :name="t('richdocuments', 'Nextcloud Office')" |
| 8 | + :description="t('richdocuments', 'Personal Settings for Nextcloud Office')" |
| 9 | + :limit-width="true"> |
| 10 | + <span id="documents-admin-msg" class="msg" /> |
| 11 | + |
| 12 | + <!-- Template folder selection --> |
| 13 | + <div class="template-folder-settings"> |
| 14 | + <div class="template-input-wrapper"> |
| 15 | + <NcTextField v-model="templateFolder" |
| 16 | + :label="t('richdocuments', 'Select a template directory')" |
| 17 | + :disabled="true" /> |
| 18 | + </div> |
| 19 | + <NcButton id="templateSelectButton" |
| 20 | + type="secondary" |
| 21 | + @click="onTemplateSelectButtonClick"> |
| 22 | + <span class="icon-folder" |
| 23 | + :title="t('richdocuments', 'Select a personal template folder')" |
| 24 | + data-toggle="tooltip" /> |
| 25 | + </NcButton> |
| 26 | + <NcButton id="templateResetButton" |
| 27 | + type="secondary" |
| 28 | + :title="t('richdocuments', 'Remove personal template folder')" |
| 29 | + @click="resetTemplate"> |
| 30 | + <DeleteIcon :size="20" /> |
| 31 | + </NcButton> |
| 32 | + </div> |
| 33 | + <p> |
| 34 | + <em> |
| 35 | + {{ t('richdocuments', 'Templates inside of this directory will be added to the template selector of Nextcloud Office.') }} |
| 36 | + </em> |
| 37 | + </p> |
| 38 | + |
| 39 | + <!-- Zotero --> |
| 40 | + <div class="zotero-section"> |
| 41 | + <p><strong>{{ t('richdocuments', 'Zotero') }}</strong></p> |
| 42 | + <template v-if="hasZoteroSupport"> |
| 43 | + <div class="input-wrapper"> |
| 44 | + <div class="zotero-inline"> |
| 45 | + <div class="zotero-input-wrapper"> |
| 46 | + <NcTextField id="zoteroAPIKeyField" |
| 47 | + :value="zoteroAPIKey" |
| 48 | + :label="t('richdocuments', 'Enter Zotero API Key')" |
| 49 | + @update:value="setZoteroAPIKey" /> |
| 50 | + </div> |
| 51 | + <NcButton id="zoteroAPIKeySave" |
| 52 | + type="secondary" |
| 53 | + @click="saveZoteroAPIKey"> |
| 54 | + {{ t('richdocuments', 'Save') }} |
| 55 | + </NcButton> |
| 56 | + <NcButton id="zoteroAPIKeyRemove" |
| 57 | + type="secondary" |
| 58 | + :title="t('richdocuments', 'Remove Zotero API Key')" |
| 59 | + @click="resetZoteroAPI"> |
| 60 | + <DeleteIcon :size="20" /> |
| 61 | + </NcButton> |
| 62 | + </div> |
| 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 | + </div> |
| 79 | + |
| 80 | + <!-- Document signing --> |
| 81 | + <div class="docsign-section"> |
| 82 | + <p class="doc_sign_head"> |
| 83 | + <strong>{{ t('richdocuments', 'Document signing') }}</strong> |
| 84 | + </p> |
| 85 | + <template v-if="hasDocumentSigningSupport"> |
| 86 | + <div class="input-wrapper"> |
| 87 | + <!-- Document Signing Cert --> |
| 88 | + <DocSigningField v-model="documentSigningCert" |
| 89 | + :label="t('richdocuments', 'Enter document signing cert (in PEM format)')" |
| 90 | + @save="val => setDocumentSigningCert(val)" |
| 91 | + @remove="() => setDocumentSigningCert('')" /> |
| 92 | + <!-- Document Signing Key --> |
| 93 | + <DocSigningField v-model="documentSigningKey" |
| 94 | + :label="t('richdocuments', 'Enter document signing key')" |
| 95 | + @save="val => setDocumentSigningKey(val)" |
| 96 | + @remove="() => setDocumentSigningKey('')" /> |
| 97 | + <!-- Document Signing CA --> |
| 98 | + <DocSigningField v-model="documentSigningCa" |
| 99 | + :label="t('richdocuments', 'Enter document signing CA chain')" |
| 100 | + @save="val => setDocumentSigningCa(val)" |
| 101 | + @remove="() => setDocumentSigningCa('')" /> |
| 102 | + <p> |
| 103 | + <em> |
| 104 | + {{ t('richdocuments', 'To use document signing, specify your signing certificate, key and CA chain here.') }} |
| 105 | + </em> |
| 106 | + </p> |
| 107 | + </div> |
| 108 | + </template> |
| 109 | + <p v-else> |
| 110 | + <em> |
| 111 | + {{ t('richdocuments', 'This instance does not support document signing, because the feature is missing or disabled. Please contact the administrator.') }} |
| 112 | + </em> |
| 113 | + </p> |
| 114 | + </div> |
| 115 | + </NcSettingsSection> |
| 116 | +</template> |
| 117 | + |
| 118 | +<script> |
| 119 | +import { generateFilePath } from '@nextcloud/router' |
| 120 | +import { showError, showSuccess } from '@nextcloud/dialogs' |
| 121 | +import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js' |
| 122 | +import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' |
| 123 | +import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' |
| 124 | +import DocSigningField from './DocSigningField.vue' |
| 125 | +import DeleteIcon from 'vue-material-design-icons/Delete.vue' |
| 126 | +
|
| 127 | +export default { |
| 128 | + name: 'PersonalSettings', |
| 129 | + components: { |
| 130 | + NcSettingsSection, |
| 131 | + NcTextField, |
| 132 | + NcButton, |
| 133 | + DocSigningField, |
| 134 | + DeleteIcon, |
| 135 | + }, |
| 136 | + props: { |
| 137 | + initial: { |
| 138 | + type: Object, |
| 139 | + required: true, |
| 140 | + }, |
| 141 | + }, |
| 142 | + data() { |
| 143 | + return { |
| 144 | + templateFolder: this.initial.templateFolder || '', |
| 145 | + hasZoteroSupport: this.initial.hasZoteroSupport || false, |
| 146 | + zoteroAPIKey: this.initial.zoteroAPIKey || '', |
| 147 | + hasDocumentSigningSupport: this.initial.hasDocumentSigningSupport || false, |
| 148 | + documentSigningCert: this.initial.documentSigningCert || '', |
| 149 | + documentSigningKey: this.initial.documentSigningKey || '', |
| 150 | + documentSigningCa: this.initial.documentSigningCa || '', |
| 151 | + } |
| 152 | + }, |
| 153 | + methods: { |
| 154 | + setZoteroAPIKey(newVal) { |
| 155 | + this.zoteroAPIKey = newVal |
| 156 | + }, |
| 157 | + onTemplateSelectButtonClick() { |
| 158 | + OC.dialogs.filepicker( |
| 159 | + this.t('richdocuments', 'Select a personal template folder'), |
| 160 | + (datapath) => { |
| 161 | + this.updateSetting( |
| 162 | + { templateFolder: datapath }, |
| 163 | + () => { this.templateFolder = datapath }, |
| 164 | + () => {}, |
| 165 | + ) |
| 166 | + }, |
| 167 | + false, |
| 168 | + 'httpd/unix-directory', |
| 169 | + true, |
| 170 | + OC.dialogs.FILEPICKER_TYPE_CHOOSE, |
| 171 | + ) |
| 172 | + }, |
| 173 | + resetTemplate() { |
| 174 | + this.updateSetting( |
| 175 | + { templateFolder: '' }, |
| 176 | + () => { this.templateFolder = '' }, |
| 177 | + () => {}, |
| 178 | + ) |
| 179 | + }, |
| 180 | + saveZoteroAPIKey() { |
| 181 | + this.updateSetting( |
| 182 | + { zoteroAPIKeyInput: this.zoteroAPIKey }, |
| 183 | + () => { showSuccess(this.t('richdocuments', 'Zotero API key saved')) }, |
| 184 | + () => { showError(this.t('richdocuments', 'Failed to update the Zotero API key')) }, |
| 185 | + ) |
| 186 | + }, |
| 187 | + resetZoteroAPI() { |
| 188 | + this.updateSetting( |
| 189 | + { zoteroAPIKeyInput: '' }, |
| 190 | + () => { this.zoteroAPIKey = '' }, |
| 191 | + () => { showError(this.t('richdocuments', 'Failed to reset the Zotero API key')) }, |
| 192 | + ) |
| 193 | + }, |
| 194 | + setDocumentSigningCert(val) { |
| 195 | + this.updateSetting( |
| 196 | + { documentSigningCertInput: val }, |
| 197 | + () => { |
| 198 | + this.documentSigningCert = val |
| 199 | + if (val === '') { |
| 200 | + showSuccess(this.t('richdocuments', 'Document signing cert removed')) |
| 201 | + } else { |
| 202 | + showSuccess(this.t('richdocuments', 'Document signing cert saved')) |
| 203 | + } |
| 204 | + }, |
| 205 | + () => { showError(this.t('richdocuments', 'Failed to update the document signing CA chain')) }, |
| 206 | + ) |
| 207 | + }, |
| 208 | + setDocumentSigningKey(val) { |
| 209 | + this.updateSetting( |
| 210 | + { documentSigningKeyInput: val }, |
| 211 | + () => { |
| 212 | + this.documentSigningKey = val |
| 213 | + if (val === '') { |
| 214 | + showSuccess(this.t('richdocuments', 'Document signing key removed')) |
| 215 | + } else { |
| 216 | + showSuccess(this.t('richdocuments', 'Document signing key saved')) |
| 217 | + } |
| 218 | + }, |
| 219 | + () => { showError(this.t('richdocuments', 'Failed to update the document signing CA chain')) }, |
| 220 | + ) |
| 221 | + }, |
| 222 | + setDocumentSigningCa(val) { |
| 223 | + this.updateSetting( |
| 224 | + { documentSigningCaInput: val }, |
| 225 | + () => { |
| 226 | + this.documentSigningCa = val |
| 227 | + if (val === '') { |
| 228 | + showSuccess(this.t('richdocuments', 'Document signing CA chain removed')) |
| 229 | + } else { |
| 230 | + showSuccess(this.t('richdocuments', 'Document signing CA chain saved')) |
| 231 | + } |
| 232 | + }, |
| 233 | + () => { showError(this.t('richdocuments', 'Failed to update the document signing CA chain')) }, |
| 234 | + ) |
| 235 | + }, |
| 236 | + updateSetting(data, successCallback, errorCallback) { |
| 237 | + OC.msg.startAction('#documents-admin-msg', this.t('richdocuments', 'Saving …')) |
| 238 | + const request = new XMLHttpRequest() |
| 239 | + request.open('POST', generateFilePath('richdocuments', 'ajax', 'personal.php'), true) |
| 240 | + request.setRequestHeader('Content-Type', 'application/json') |
| 241 | + request.setRequestHeader('requesttoken', OC.requestToken) |
| 242 | + request.onload = function() { |
| 243 | + if (request.status >= 200 && request.status < 400) { |
| 244 | + const response = JSON.parse(request.response) |
| 245 | + OC.msg.finishedAction('#documents-admin-msg', response) |
| 246 | + successCallback(response) |
| 247 | + } else { |
| 248 | + errorCallback(this.response) |
| 249 | + } |
| 250 | + } |
| 251 | + request.onerror = function() { |
| 252 | + errorCallback(this.response) |
| 253 | + } |
| 254 | + request.send(JSON.stringify(data)) |
| 255 | + }, |
| 256 | + }, |
| 257 | +} |
| 258 | +</script> |
| 259 | + |
| 260 | + <style scoped> |
| 261 | + .template-folder-settings { |
| 262 | + display: flex; |
| 263 | + align-items: center; |
| 264 | + gap: 1rem; |
| 265 | + margin-bottom: 2rem; |
| 266 | + } |
| 267 | + .template-input-wrapper { |
| 268 | + width: 300px; |
| 269 | + flex-shrink: 0; |
| 270 | + } |
| 271 | + .input-wrapper { |
| 272 | + display: flex; |
| 273 | + flex-direction: column; |
| 274 | + gap: 1rem; |
| 275 | + } |
| 276 | + .zotero-section p { |
| 277 | + margin-top: 5px !important; |
| 278 | + } |
| 279 | + .zotero-inline { |
| 280 | + display: flex; |
| 281 | + align-items: center; |
| 282 | + gap: 1rem; |
| 283 | + } |
| 284 | + .doc_sign_head { |
| 285 | + padding-top: 10px; |
| 286 | + padding-bottom: 5px; |
| 287 | + } |
| 288 | + .msg { |
| 289 | + display: inline-block; |
| 290 | + margin-bottom: 1rem; |
| 291 | + color: var(--color-warning); |
| 292 | + } |
| 293 | + .icon-folder::before { |
| 294 | + content: "\1F4C1"; |
| 295 | + } |
| 296 | + </style> |
0 commit comments