This resolves 68 ESLint warnings about invalid code style.
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
if (!ActivityTabPluginView) {
const { default: ActivityCommentAction } = await import('./views/ActivityCommentAction.vue')
- /** @ts-expect-error Types are broken for Vue2 */
+ // @ts-expect-error Types are broken for Vue2
ActivityTabPluginView = Vue.extend(ActivityCommentAction)
}
ActivityTabPluginInstance = new ActivityTabPluginView({
const { data: comments } = await getComments({ resourceType: 'files', resourceId: fileInfo.id }, { limit, offset })
logger.debug('Loaded comments', { fileInfo, comments })
const { default: CommentView } = await import('./views/ActivityCommentEntry.vue')
- /** @ts-expect-error Types are broken for Vue2 */
+ // @ts-expect-error Types are broken for Vue2
const CommentsViewObject = Vue.extend(CommentView)
return comments.map((comment) => ({
* Set the sorting key AND sort by ASC
* The key param must be a valid key of a File object
* If not found, will be searched within the File attributes
- * @param key
- * @param view
+ * @param key Key to sort by
+ * @param view View to set the sorting key for
*/
setSortingBy(key = 'basename', view = 'files') {
// Save new config
/**
* Toggle the sorting direction
- * @param view
+ * @param view view to set the sorting order for
*/
toggleSortingDirection(view = 'files') {
const config = this.getConfig(view) || { sorting_direction: 'asc' }
showCustomEmptyView() {
return !this.loading && this.isEmptyDir && this.currentView?.emptyView !== undefined
- }
+ },
},
watch: {
password?: string,
}
+/**
+ * Set credentials for external storage
+ *
+ * @param node The node for which to set the credentials
+ * @param login The username
+ * @param password The password
+ */
async function setCredentials(node: Node, login: string, password: string): Promise<null|true> {
- const configResponse = await axios.put(generateUrl('apps/files_external/userglobalstorages/{id}', node.attributes), {
+ const configResponse = await axios.put(generateUrl('apps/files_external/userglobalstorages/{id}', { id: node.attributes.id }), {
backendOptions: { user: login, password },
}) as AxiosResponse<StorageConfig>
/**
* Use this function to check the storage availability
* We then update the node attributes directly.
- * @param node
+ *
+ * @param node The node to render inline
*/
async renderInline(node: Node) {
let config = null as unknown as StorageConfig
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
/* eslint-disable n/no-extraneous-import */
+/* eslint-disable @typescript-eslint/no-explicit-any */
import type { OCSResponse } from '@nextcloud/typings/ocs'
import { Folder, Navigation, View, getNavigation } from '@nextcloud/files'
import { beforeEach, describe, expect, test, vi } from 'vitest'
* @param {string} [data.password] password to protect public link Share with
* @param {number} [data.permissions] 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1)
* @param {boolean} [data.sendPasswordByTalk] send the password via a talk conversation
- * @param {string} [data.expireDate] expire the shareautomatically after
+ * @param {string} [data.expireDate] expire the share automatically after
* @param {string} [data.label] custom label
* @param {string} [data.attributes] Share attributes encoded as json
- * @param data.note
+ * @param {string} data.note custom note to recipient
* @return {Share} the new share
* @throws {Error}
*/
/**
* Get the shared item id
- */
+ */
get fileSource(): number {
return this._share.file_source
}
const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789'
/**
- * Generate a valid policy password or
- * request a valid password if password_policy
- * is enabled
+ * Generate a valid policy password or request a valid password if password_policy is enabled
+ *
+ * @param {boolean} verbose If enabled the the status is shown to the user via toast
*/
export default async function(verbose = false): Promise<string> {
// password policy is enabled, let's request a pass
import { emit } from '@nextcloud/event-bus'
import { getLanguage } from '@nextcloud/l10n'
import { ShareType } from '@nextcloud/sharing'
+import moment from '@nextcloud/moment'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
</template>
<script>
-import axios from '@nextcloud/axios'
+import { getCurrentUser } from '@nextcloud/auth'
import { orderBy } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { generateOcsUrl } from '@nextcloud/router'
import { CollectionList } from 'nextcloud-vue-collections'
+
+import axios from '@nextcloud/axios'
+import moment from '@nextcloud/moment'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import Config from '../services/ConfigService.ts'
import { shareWithTitle } from '../utils/SharedWithMe.js'
+
+import Config from '../services/ConfigService.ts'
import Share from '../models/Share.ts'
import ShareTypes from '../mixins/ShareTypes.js'
import SharingEntryInternal from '../components/SharingEntryInternal.vue'
updateExpirationSubtitle(share) {
const expiration = moment(share.expireDate).unix()
this.$set(this.sharedWithMe, 'subtitle', t('files_sharing', 'Expires {relativetime}', {
- relativetime: OC.Util.relativeModifiedDate(expiration * 1000),
+ relativetime: moment(expiration * 1000).fromNow(),
}))
// share have expired
// interval update
this.expirationInterval = setInterval(this.updateExpirationSubtitle, 10000, share)
}
- } else if (this.fileInfo && this.fileInfo.shareOwnerId !== undefined ? this.fileInfo.shareOwnerId !== OC.currentUser : false) {
+ } else if (this.fileInfo && this.fileInfo.shareOwnerId !== undefined ? this.fileInfo.shareOwnerId !== getCurrentUser().uid : false) {
// Fallback to compare owner and current user.
this.sharedWithMe = {
displayName: this.fileInfo.shareOwner,
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import VersionTab from './views/VersionTab.vue'
-import VTooltip from 'v-tooltip'
+import VTooltipPlugin from 'v-tooltip'
// eslint-disable-next-line n/no-missing-import, import/no-unresolved
import BackupRestore from '@mdi/svg/svg/backup-restore.svg?raw'
Vue.prototype.t = t
Vue.prototype.n = n
-Vue.use(VTooltip)
+Vue.use(VTooltipPlugin)
// Init Sharing tab component
const View = Vue.extend(VersionTab)
/**
* Handle restored event from Version.vue
*
- * @param {import('../utils/versions.ts').Version} version
+ * @param {import('../utils/versions.ts').Version} version The version to restore
*/
async handleRestore(version) {
// Update local copy of fileInfo as rendering depends on it.
/**
* Handle label-updated event from Version.vue
- * @param {import('../utils/versions.ts').Version} version
+ * @param {import('../utils/versions.ts').Version} version The version to update
*/
handleLabelUpdateRequest(version) {
this.showVersionLabelForm = true
/**
* Handle label-updated event from Version.vue
- * @param {string} newLabel
+ * @param {string} newLabel The new label
*/
async handleLabelUpdate(newLabel) {
const oldLabel = this.editedVersion.label
/**
* Handle deleted event from Version.vue
*
- * @param {import('../utils/versions.ts').Version} version
- * @param {string} newName
+ * @param {import('../utils/versions.ts').Version} version The version to delete
*/
async handleDelete(version) {
const index = this.versions.indexOf(version)
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { generateUrl } from '@nextcloud/router'
import $ from 'jquery'
window.addEventListener('DOMContentLoaded', () => {
$('#loglevel').change(function() {
- $.post(OC.generateUrl('/settings/admin/log/level'), { level: $(this).val() }, () => {
+ $.post(generateUrl('/settings/admin/log/level'), { level: $(this).val() }, () => {
OC.Log.reload()
})
})
OC.msg.startSaving('#mail_settings_msg')
$.ajax({
- url: OC.generateUrl('/settings/admin/mailsettings'),
+ url: generateUrl('/settings/admin/mailsettings'),
type: 'POST',
data: $('#mail_general_settings_form').serialize(),
success: () => {
OC.msg.startSaving('#mail_settings_msg')
$.ajax({
- url: OC.generateUrl('/settings/admin/mailsettings/credentials'),
+ url: generateUrl('/settings/admin/mailsettings/credentials'),
type: 'POST',
data: $('#mail_credentials_settings').serialize(),
success: () => {
OC.msg.startAction('#sendtestmail_msg', t('settings', 'Sending…'))
$.ajax({
- url: OC.generateUrl('/settings/admin/mailtest'),
+ url: generateUrl('/settings/admin/mailtest'),
type: 'POST',
success: () => {
OC.msg.finishedSuccess('#sendtestmail_msg', t('settings', 'Email sent'))
return this.token.type === TokenType.PERMANENT_TOKEN
},
/**
- * Object ob the current user agend used by the token
- * @return Either an object containing user agent information or null if unknown
+ * Object ob the current user agent used by the token
+ * This either returns an object containing user agent information or `null` if unknown
*/
client() {
// pretty format sync client user agent
@update:checked="onBackgroundJobModeChanged">
{{ t('settings', 'Cron (Recommended)') }}
</NcCheckboxRadioSwitch>
+ <!-- eslint-disable-next-line vue/no-v-html The translation is sanitized-->
<em v-html="cronLabel" />
</NcSettingsSection>
</template>
desc += '<br>' + t('settings', 'The PHP POSIX extension is required. See {linkstart}PHP documentation{linkend} for more details.', {
linkstart: '<a target="_blank" rel="noreferrer nofollow" class="external" href="https://www.php.net/manual/en/book.posix.php">',
linkend: '</a>',
- }, undefined, { escape: false, sanitize: false })
+ }, undefined, { escape: false })
}
return desc
},
</template>
</div>
- <div v-else-if="externalBackendsEnabled" v-html="migrationMessage" />
+ <div v-else-if="externalBackendsEnabled">
+ {{
+ t(
+ 'settings',
+ 'You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please enable the "Default encryption module" and run {command}',
+ { command: '"occ encryption:migrate"' },
+ )
+ }}
+ </div>
</div>
</NcSettingsSection>
</template>
defaultCheckedModule: Object.entries(encryptionModules).find((module) => module[1].default)[0],
}
},
- computed: {
- migrationMessage() {
- return t('settings', 'You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please enable the "Default encryption module" and run {command}', {
- command: '"occ encryption:migrate"',
- })
- },
- },
methods: {
displayWarning() {
if (!this.encryptionEnabled) {
-->
<template>
+ <!-- eslint-disable-next-line vue/no-v-html This is rendered markdown so should be "safe" -->
<div class="settings-markdown" v-html="renderMarkdown" />
</template>
:input-id="inputId"
:readable="birthdate.readable" />
- <template>
- <NcDateTimePickerNative :id="inputId"
- type="date"
- label=""
- :value="value"
- @input="onInput" />
- </template>
+ <NcDateTimePickerNative :id="inputId"
+ type="date"
+ label=""
+ :value="value"
+ @input="onInput" />
<p class="property__helper-text-message">
{{ t('settings', 'Enter your date of birth') }}
get() {
return new Date(this.birthdate.value)
},
- /** @param {Date} value */
+ /** @param {Date} value The date to set */
set(value) {
const day = value.getDate().toString().padStart(2, '0')
const month = (value.getMonth() + 1).toString().padStart(2, '0')
<div class="details__quota">
<CircleSlice :size="20" />
<div class="details__quota-info">
+ <!-- eslint-disable-next-line vue/no-v-html -->
<p class="details__quota-text" v-html="quotaText" />
<NcProgressBar size="medium"
:value="usageRelative"
<script>
import { loadState } from '@nextcloud/initial-state'
-import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js'
+import { t } from '@nextcloud/l10n'
+import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js'
import Account from 'vue-material-design-icons/Account.vue'
import CircleSlice from 'vue-material-design-icons/CircleSlice3.vue'
computed: {
quotaText() {
if (quota === SPACE_UNLIMITED) {
- return t('settings', 'You are using <strong>{usage}</strong>', { usage })
+ return t('settings', 'You are using {s}{usage}{/s}', { usage, s: '<strong>', '/s': '</strong>' }, undefined, { escape: false })
}
return t(
'settings',
- 'You are using <strong>{usage}</strong> of <strong>{totalSpace}</strong> (<strong>{usageRelative}%</strong>)',
- { usage, totalSpace, usageRelative },
+ 'You are using {s}{usage}{/s} of {s}{totalSpace}{/s} ({s}{usageRelative}%{/s})',
+ { usage, totalSpace, usageRelative, s: '<strong>', '/s': '</strong>' },
+ undefined,
+ { escape: false },
)
},
},
</template>
<script>
+import { formatFileSize, parseFileSize } from '@nextcloud/files'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
const validQuota = OC.Util.computerFileSize(quota)
if (validQuota !== null && validQuota >= 0) {
// unify format output
- quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota))
+ quota = formatFileSize(parseFileSize(quota))
this.newUser.quota = { id: quota, label: quota }
return this.newUser.quota
}
*/
import Vue from 'vue'
-import VTooltip from 'v-tooltip'
+import VTooltipPlugin from 'v-tooltip'
import { sync } from 'vuex-router-sync'
-import { translate as t, translatePlural as n } from '@nextcloud/l10n'
+import { t, n } from '@nextcloud/l10n'
import SettingsApp from './views/SettingsApp.vue'
import router from './router/index.ts'
import { getCSPNonce } from '@nextcloud/auth'
import { PiniaVuePlugin, createPinia } from 'pinia'
-Vue.use(VTooltip, { defaultHtml: false })
-
-const store = useStore()
-sync(store, router)
-
// CSP config for webpack dynamic chunk loading
// eslint-disable-next-line camelcase
__webpack_nonce__ = getCSPNonce()
+const store = useStore()
+sync(store, router)
+
// bind to window
Vue.prototype.t = t
Vue.prototype.n = n
Vue.use(PiniaVuePlugin)
+Vue.use(VTooltipPlugin, { defaultHtml: false })
const pinia = createPinia()
import type { ComponentInstance } from 'vue'
import { loadState } from '@nextcloud/initial-state'
-import { translate as t, translatePlural as n } from '@nextcloud/l10n'
+import { t, n } from '@nextcloud/l10n'
import Vue from 'vue'
import DeclarativeSection from './components/DeclarativeSettings/DeclarativeSection.vue'
+import logger from './logger'
interface DeclarativeFormField {
id: string,
fields: Array<DeclarativeFormField>,
}
-const forms = loadState('settings', 'declarative-settings-forms', []) as Array<DeclarativeForm>
-console.debug('Loaded declarative forms:', forms)
+const forms = loadState<DeclarativeForm[]>('settings', 'declarative-settings-forms', [])
/**
- *
- * @param forms
+ * @param forms The forms to render
*/
function renderDeclarativeSettingsSections(forms: Array<DeclarativeForm>): ComponentInstance[] {
Vue.mixin({ methods: { t, n } })
}
document.addEventListener('DOMContentLoaded', () => {
+ logger.debug('Loaded declarative forms', { forms })
renderDeclarativeSettingsSections(forms)
})
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { formatFileSize } from '@nextcloud/files'
+
export default {
props: {
user: {
/* QUOTA MANAGEMENT */
usedSpace() {
- if (this.user.quota.used) {
- return t('settings', '{size} used', { size: OC.Util.humanFileSize(this.user.quota.used) })
- }
- return t('settings', '{size} used', { size: OC.Util.humanFileSize(0) })
+ const quotaUsed = this.user.quota.used > 0 ? this.user.quota.used : 0
+ return t('settings', '{size} used', { size: formatFileSize(quotaUsed, true) })
},
+
usedQuota() {
let quota = this.user.quota.quota
if (quota > 0) {
}
return isNaN(quota) ? 0 : quota
},
+
// Mapping saved values to objects
userQuota() {
if (this.user.quota.quota >= 0) {
// if value is valid, let's map the quotaOptions or return custom quota
- const humanQuota = OC.Util.humanFileSize(this.user.quota.quota)
+ const humanQuota = formatFileSize(this.user.quota.quota)
const userQuota = this.quotaOptions.find(quota => quota.id === humanQuota)
return userQuota || { id: humanQuota, label: humanQuota }
} else if (this.user.quota.quota === 'default') {
/**
* Return `true` if the logged in user does not have permissions to view the
* data of `user`
- * @param user
- * @param user.id
+ * @param user The user to check
+ * @param user.id Id of the user
*/
export const isObfuscated = (user: { id: string, [key: string]: unknown }) => {
const keys = Object.keys(user)
}
/**
- * @param tag
- * @return created tag id
+ * Create a tag and return the Id of the newly created tag.
+ *
+ * @param tag The tag to create
*/
export const createTag = async (tag: Tag | ServerTag): Promise<number> => {
const path = '/systemtags'
import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { ServerTagWithId, Tag, TagWithId } from '../types.js'
+import { t } from '@nextcloud/l10n'
import { davClient } from './davClient.js'
import { createTag, fetchTagsPayload } from './api.js'
import { formatTag, parseTags } from '../utils.js'
}
/**
- * @param tag
- * @param fileId
- * @return created tag id
+ * Create a tag and apply it to a given file (by id).
+ * This returns the id of the newly created tag.
+ *
+ * @param tag The tag to create
+ * @param fileId Id of the file to tag
*/
export const createTagForFile = async (tag: Tag, fileId: number): Promise<number> => {
const tagToCreate = formatTag(tag)
</template>
<script>
+import { getCurrentUser } from '@nextcloud/auth'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { Fragment } from 'vue-frag'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
}
},
handleUserStatusUpdated(state) {
- if (OC.getCurrentUser().uid === state.userId) {
+ if (getCurrentUser()?.uid === state.userId) {
this.$store.dispatch('setStatusFromObject', {
status: state.status,
icon: state.icon,
/**
* Fetches the current user-status
*
- * @param {string} userId
+ * @param {string} userId Id of the user to fetch the status
* @return {Promise<object>}
*/
const fetchBackupStatus = async (userId) => {
/**
* Revert the automated status
*
- * @param {string} messageId
+ * @param {string} messageId ID of the message to revert
* @return {Promise<object>}
*/
const revertToBackupStatus = async (messageId) => {
(function(DOMParser) {
"use strict";
- var
- DOMParser_proto = DOMParser.prototype
- , real_parseFromString = DOMParser_proto.parseFromString
- ;
+ var DOMParser_proto = DOMParser.prototype;
+ var real_parseFromString = DOMParser_proto.parseFromString;
// Firefox/Opera/IE throw errors on unsupported types
try {
DOMParser_proto.parseFromString = function(markup, type) {
if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
- var
- doc = document.implementation.createHTMLDocument("")
- ;
- if (markup.toLowerCase().indexOf('<!doctype') > -1) {
- doc.documentElement.innerHTML = markup;
- }
- else {
- doc.body.innerHTML = markup;
- }
+ var doc = document.implementation.createHTMLDocument("");
+ if (markup.toLowerCase().indexOf('<!doctype') > -1) {
+ doc.documentElement.innerHTML = markup;
+ } else {
+ doc.body.innerHTML = markup;
+ }
return doc;
} else {
return real_parseFromString.apply(this, arguments);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { generateFilePath } from '@nextcloud/router'
+
const loadedScripts = {}
const loadedStylesheets = {}
/**
}
loadedScripts[key] = true
return new Promise(function(resolve, reject) {
- const scriptPath = OC.filePath(app, 'js', file)
+ const scriptPath = generateFilePath(app, 'js', file)
const script = document.createElement('script')
script.src = scriptPath
script.setAttribute('nonce', btoa(OC.requestToken))
}
loadedStylesheets[key] = true
return new Promise(function(resolve, reject) {
- const stylePath = OC.filePath(app, 'css', file)
+ const stylePath = generateFilePath(app, 'css', file)
const link = document.createElement('link')
link.href = stylePath
link.type = 'text/css'
import $ from 'jquery'
import { translate as t } from '@nextcloud/l10n'
+import { linkTo } from '@nextcloud/router'
+
import { getToken } from './OC/requesttoken.js'
import getURLParameter from './Util/get-url-parameter.js'
}
$('#adminpass').strengthify({
- zxcvbn: OC.linkTo('core', 'vendor/zxcvbn/dist/zxcvbn.js'),
+ zxcvbn: linkTo('core', 'vendor/zxcvbn/dist/zxcvbn.js'),
titles: [
t('core', 'Very weak password'),
t('core', 'Weak password'),
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { getCurrentUser } from '@nextcloud/auth'
+import { generateUrl } from '@nextcloud/router'
import $ from 'jquery'
-import OC from '../OC/index.js'
-
/**
* This plugin inserts the right avatar for the user, depending on, whether a
* custom avatar is uploaded - which it uses then - or not, and display a
let url
// If this is our own avatar we have to use the version attribute
- if (user === OC.getCurrentUser().uid) {
- url = OC.generateUrl(
+ if (user === getCurrentUser()?.uid) {
+ url = generateUrl(
'/avatar/{user}/{size}?v={version}',
{
user,
version: oc_userconfig.avatar.version,
})
} else {
- url = OC.generateUrl(
+ url = generateUrl(
'/avatar/{user}/{size}',
{
user,
* Get the list of active contacts
*
* @param {object} filter filter contacts by string
- * @param filter.searchTerm
+ * @param {string} filter.searchTerm the query
* @return {object} {request: Promise}
*/
export async function getContacts({ searchTerm }) {
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
- * @param OC
+ * @param {object} OC The OC namespace
*/
(function(OC) {
// up with the global namespace/classes/state
'dist/files_sharing-additionalScripts.js',
'dist/files_sharing-files_sharing_tab.js',
- 'dist/files_sharing-files_sharing.js',
'dist/files_sharing-main.js',
- 'apps/files_sharing/js/files_drop.js',
- 'apps/files_sharing/js/public.js',
- 'apps/files_sharing/js/sharedfilelist.js',
- 'apps/files_sharing/js/templates.js',
],
- testFiles: ['apps/files_sharing/tests/js/*.js']
},
'files_trashbin',
];