зеркало из https://github.com/nextcloud/server.git
chore: Enable ESLint for apps and fix all errors
Nevertheless this causes a huge amount of new warnings. Previously the shell script for directories to lint was wrong it was generating all app names to lint, but was missing the `apps/` prefix. Causing only `core` to be linted. Co-authored-by: Grigorii K. Shartsev <me@shgk.me> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Родитель
3a97dbf248
Коммит
691f570237
|
@ -36,4 +36,13 @@ module.exports = {
|
|||
mode: 'typescript',
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
// Allow any in tests
|
||||
{
|
||||
files: ['**/*.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
window.OC = { ...window.OC }
|
||||
window.OCA = { ...window.OCA }
|
||||
window.OCP = { ...window.OCP }
|
|
@ -12,12 +12,12 @@ const client = createClient(getRootPath())
|
|||
|
||||
// set CSRF token header
|
||||
const setHeaders = (token) => {
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
// refresh headers when request token changes
|
||||
|
|
|
@ -60,7 +60,7 @@ const getDirectoryFiles = function(
|
|||
// Map all items to a consistent output structure (results)
|
||||
return responseItems.map(item => {
|
||||
// Each item should contain a stat object
|
||||
const props = item.propstat!.prop!;
|
||||
const props = item.propstat!.prop!
|
||||
|
||||
return prepareFileFromProps(props, props.id!.toString(), isDetailed)
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ const cancelableRequest = function(request) {
|
|||
const fetch = async function(url, options) {
|
||||
const response = await request(
|
||||
url,
|
||||
Object.assign({ signal }, options)
|
||||
Object.assign({ signal }, options),
|
||||
)
|
||||
return response
|
||||
}
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
:clear-search-on-blur="() => false"
|
||||
:user-select="true"
|
||||
:options="options"
|
||||
@search="asyncFind"
|
||||
>
|
||||
@search="asyncFind">
|
||||
<template #no-options="{ search }">
|
||||
{{ search ?$t('dav', 'No results.') : $t('dav', 'Start typing.') }}
|
||||
</template>
|
||||
|
@ -51,21 +50,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { ShareType } from '@nextcloud/sharing'
|
||||
import { formatDateAsYMD } from '../utils/date.js'
|
||||
import axios from '@nextcloud/axios'
|
||||
import debounce from 'debounce'
|
||||
import logger from '../service/logger.js'
|
||||
|
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
|
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||
import NcTextArea from '@nextcloud/vue/dist/Components/NcTextArea.js'
|
||||
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
|
||||
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import debounce from 'debounce'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { formatDateAsYMD } from '../utils/date.js'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import { Type as ShareTypes } from '@nextcloud/sharing'
|
||||
|
||||
import logger from '../service/logger.js'
|
||||
|
||||
export default {
|
||||
name: 'AbsenceForm',
|
||||
|
@ -74,17 +73,17 @@ export default {
|
|||
NcTextField,
|
||||
NcTextArea,
|
||||
NcDateTimePickerNative,
|
||||
NcSelect
|
||||
NcSelect,
|
||||
},
|
||||
data() {
|
||||
const { firstDay, lastDay, status, message ,replacementUserId ,replacementUserDisplayName } = loadState('dav', 'absence', {})
|
||||
const { firstDay, lastDay, status, message, replacementUserId, replacementUserDisplayName } = loadState('dav', 'absence', {})
|
||||
return {
|
||||
loading: false,
|
||||
status: status ?? '',
|
||||
message: message ?? '',
|
||||
firstDay: firstDay ? new Date(firstDay) : new Date(),
|
||||
lastDay: lastDay ? new Date(lastDay) : null,
|
||||
replacementUserId: replacementUserId ,
|
||||
replacementUserId,
|
||||
replacementUser: replacementUserId ? { user: replacementUserId, displayName: replacementUserDisplayName } : null,
|
||||
searchLoading: false,
|
||||
options: [],
|
||||
|
@ -126,10 +125,10 @@ export default {
|
|||
return {
|
||||
user: result.uuid || result.value.shareWith,
|
||||
displayName: result.name || result.label,
|
||||
subtitle: result.dsc | ''
|
||||
subtitle: result.dsc | '',
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async asyncFind(query) {
|
||||
this.searchLoading = true
|
||||
await this.debounceGetSuggestions(query.trim())
|
||||
|
@ -142,7 +141,7 @@ export default {
|
|||
async getSuggestions(search) {
|
||||
|
||||
const shareType = [
|
||||
ShareTypes.SHARE_TYPE_USER,
|
||||
ShareType.SHARE_TYPE_USER,
|
||||
]
|
||||
|
||||
let request = null
|
||||
|
|
|
@ -27,5 +27,5 @@ export const getClient = memoize((service) => {
|
|||
onRequestTokenUpdate(setHeaders)
|
||||
setHeaders(getRequestToken())
|
||||
|
||||
return client;
|
||||
return client
|
||||
})
|
||||
|
|
|
@ -17,7 +17,7 @@ export async function enableUserStatusAutomation() {
|
|||
}),
|
||||
{
|
||||
configValue: 'yes',
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,6 @@ export async function disableUserStatusAutomation() {
|
|||
generateOcsUrl('/apps/provisioning_api/api/v1/config/users/{appId}/{configKey}', {
|
||||
appId: 'dav',
|
||||
configKey: 'user_status_automation',
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@ const CalDavSettingsView = new View({
|
|||
sendInvitations: loadState('dav', 'sendInvitations'),
|
||||
generateBirthdayCalendar: loadState(
|
||||
'dav',
|
||||
'generateBirthdayCalendar'
|
||||
'generateBirthdayCalendar',
|
||||
),
|
||||
sendEventReminders: loadState('dav', 'sendEventReminders'),
|
||||
sendEventRemindersToSharedUsers: loadState(
|
||||
'dav',
|
||||
'sendEventRemindersToSharedUsers'
|
||||
'sendEventRemindersToSharedUsers',
|
||||
),
|
||||
sendEventRemindersPush: loadState('dav', 'sendEventRemindersPush'),
|
||||
}
|
||||
|
|
|
@ -54,27 +54,27 @@ describe('CalDavSettings', () => {
|
|||
},
|
||||
Vue => {
|
||||
Vue.prototype.$t = jest.fn((app, text) => text)
|
||||
}
|
||||
},
|
||||
)
|
||||
expect(TLUtils.container).toMatchSnapshot()
|
||||
const sendInvitations = TLUtils.getByLabelText(
|
||||
'Send invitations to attendees'
|
||||
'Send invitations to attendees',
|
||||
)
|
||||
expect(sendInvitations).toBeChecked()
|
||||
const generateBirthdayCalendar = TLUtils.getByLabelText(
|
||||
'Automatically generate a birthday calendar'
|
||||
'Automatically generate a birthday calendar',
|
||||
)
|
||||
expect(generateBirthdayCalendar).toBeChecked()
|
||||
const sendEventReminders = TLUtils.getByLabelText(
|
||||
'Send notifications for events'
|
||||
'Send notifications for events',
|
||||
)
|
||||
expect(sendEventReminders).toBeChecked()
|
||||
const sendEventRemindersToSharedUsers = TLUtils.getByLabelText(
|
||||
'Send reminder notifications to calendar sharees as well'
|
||||
'Send reminder notifications to calendar sharees as well',
|
||||
)
|
||||
expect(sendEventRemindersToSharedUsers).toBeChecked()
|
||||
const sendEventRemindersPush = TLUtils.getByLabelText(
|
||||
'Enable notifications for events via push'
|
||||
'Enable notifications for events via push',
|
||||
)
|
||||
expect(sendEventRemindersPush).toBeChecked()
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
|||
OCP.AppConfig.setValue(
|
||||
'dav',
|
||||
'sendInvitations',
|
||||
value ? 'yes' : 'no'
|
||||
value ? 'yes' : 'no',
|
||||
)
|
||||
},
|
||||
sendEventReminders(value) {
|
||||
|
@ -138,7 +138,7 @@ export default {
|
|||
OCP.AppConfig.setValue(
|
||||
'dav',
|
||||
'sendEventRemindersToSharedUsers',
|
||||
value ? 'yes' : 'no'
|
||||
value ? 'yes' : 'no',
|
||||
)
|
||||
},
|
||||
sendEventRemindersPush(value) {
|
||||
|
|
|
@ -47,13 +47,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import axios from '@nextcloud/axios'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M502 197q-96 0-96.5 1.5t-1.5 137-1.5 138-2 2.5T266 432.5 132.5 390t-30 94T74 578l232 77q21 8 21 10t-79.5 117.5T168 899t79.5 56.5T328 1011t81-110 82-110 41 55l83 115q43 60 44 60t79.5-58 79-59-76-112.5-76-113.5T795 632.5t129.5-44-28-94T867 400t-128 42-128.5 43-2.5-7.5-1-38.5l-3-108q-4-133-5-133.5t-97-.5z" /></svg>
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton @click="showHtml = !showHtml"
|
||||
class="social-button__website-button">
|
||||
<NcButton class="social-button__website-button"
|
||||
@click="showHtml = !showHtml">
|
||||
<template #icon>
|
||||
<Web :size="20" />
|
||||
</template>
|
||||
|
|
|
@ -109,7 +109,7 @@ const processIncomingShareFromUrl = function() {
|
|||
password,
|
||||
},
|
||||
).done(function(data) {
|
||||
if (data.hasOwnProperty('legacyMount')) {
|
||||
if (Object.hasOwn(data, 'legacyMount')) {
|
||||
reloadFilesList()
|
||||
} else {
|
||||
window.OC.Notification.showTemporary(data.message)
|
||||
|
|
|
@ -126,7 +126,7 @@ export const action = new FileAction({
|
|||
.every(permission => (permission & Permission.DELETE) !== 0)
|
||||
},
|
||||
|
||||
async exec(node: Node, view: View, dir: string) {
|
||||
async exec(node: Node) {
|
||||
try {
|
||||
await axios.delete(node.encodedSource)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { Permission, type Node, View, FileAction, FileType } from '@nextcloud/files'
|
||||
import { Permission, type Node, View, FileAction } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import InformationSvg from '@mdi/svg/svg/information-variant.svg?raw'
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import type { ComponentPublicInstance, PropType } from 'vue'
|
|||
import type { FileSource } from '../types.ts'
|
||||
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { FileType, Permission, Folder, File as NcFile, NodeStatus, Node, View } from '@nextcloud/files'
|
||||
import { FileType, Permission, Folder, File as NcFile, NodeStatus, Node } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { vOnClickOutside } from '@vueuse/components'
|
||||
|
@ -179,6 +179,8 @@ export default defineComponent({
|
|||
/**
|
||||
* When the source changes, reset the preview
|
||||
* and fetch the new one.
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
source(a: Node, b: Node) {
|
||||
if (a.source !== b.source) {
|
||||
|
|
|
@ -94,12 +94,12 @@ export default {
|
|||
mounted() {
|
||||
// If the user has a quota set, warn if the available account storage is <=0
|
||||
//
|
||||
// NOTE: This doesn't catch situations where actual *server*
|
||||
// NOTE: This doesn't catch situations where actual *server*
|
||||
// disk (non-quota) space is low, but those should probably
|
||||
// be handled differently anyway since a regular user can't
|
||||
// can't do much about them (If we did want to indicate server disk
|
||||
// can't do much about them (If we did want to indicate server disk
|
||||
// space matters to users, we'd probably want to use a warning
|
||||
// specific to that situation anyhow. So this covers warning covers
|
||||
// specific to that situation anyhow. So this covers warning covers
|
||||
// our primary day-to-day concern (individual account quota usage).
|
||||
//
|
||||
if (this.storageStats?.quota > 0 && this.storageStats?.free <= 0) {
|
||||
|
@ -121,7 +121,7 @@ export default {
|
|||
* Update the storage stats
|
||||
* Throttled at max 1 refresh per minute
|
||||
*
|
||||
* @param {Event} [event = null] if user interaction
|
||||
* @param {Event} [event] if user interaction
|
||||
*/
|
||||
async updateStorageStats(event = null) {
|
||||
if (this.loadingStorageStats) {
|
||||
|
@ -135,7 +135,7 @@ export default {
|
|||
throw new Error('Invalid storage stats')
|
||||
}
|
||||
|
||||
// Warn the user if the available account storage changed from > 0 to 0
|
||||
// Warn the user if the available account storage changed from > 0 to 0
|
||||
// (unless only because quota was intentionally set to 0 by admin in the interim)
|
||||
if (this.storageStats?.free > 0 && response.data.data?.free <= 0 && response.data.data?.quota > 0) {
|
||||
this.showStorageFullWarning()
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<form @submit.prevent="submit">
|
||||
<p class="transfer-select-row">
|
||||
<span>{{ readableDirectory }}</span>
|
||||
<NcButton v-if="directory === undefined"
|
||||
<NcButton v-if="directory === undefined"
|
||||
class="transfer-select-row__choose_button"
|
||||
@click.prevent="start">
|
||||
{{ t('files', 'Choose file or folder to transfer') }}
|
||||
|
@ -22,8 +22,8 @@
|
|||
<label for="targetUser">
|
||||
<span>{{ t('files', 'New owner') }}</span>
|
||||
</label>
|
||||
<NcSelect input-id="targetUser"
|
||||
v-model="selectedUser"
|
||||
<NcSelect v-model="selectedUser"
|
||||
input-id="targetUser"
|
||||
:options="formatedUserSuggestions"
|
||||
:multiple="false"
|
||||
:loading="loadingUsers"
|
||||
|
|
|
@ -62,6 +62,10 @@ interface RecycledPoolItem {
|
|||
item: Node,
|
||||
}
|
||||
|
||||
type DataSource = File | Folder
|
||||
|
||||
type DataSourceKey = keyof DataSource
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'VirtualList',
|
||||
|
||||
|
@ -73,11 +77,11 @@ export default Vue.extend({
|
|||
required: true,
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
type: String as PropType<DataSourceKey>,
|
||||
required: true,
|
||||
},
|
||||
dataSources: {
|
||||
type: Array as PropType<(File | Folder)[]>,
|
||||
type: Array as PropType<DataSource[]>,
|
||||
required: true,
|
||||
},
|
||||
extraProps: {
|
||||
|
@ -260,7 +264,7 @@ export default Vue.extend({
|
|||
// Adding scroll listener AFTER the initial scroll to index
|
||||
this.$el.addEventListener('scroll', this.onScroll, { passive: true })
|
||||
|
||||
this.$_recycledPool = {} as Record<string, any>
|
||||
this.$_recycledPool = {} as Record<string, DataSource[DataSourceKey]>
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, jest } from '@jest/globals'
|
||||
import { Navigation, View } from '@nextcloud/files'
|
||||
import nextcloudFiles, { Navigation, View } from '@nextcloud/files'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { useNavigation } from './useNavigation'
|
||||
|
||||
import nextcloudFiles from '@nextcloud/files'
|
||||
|
||||
// Just a wrapper so we can test the composable
|
||||
const TestComponent = defineComponent({
|
||||
template: '<div></div>',
|
||||
|
@ -38,7 +36,7 @@ describe('Composables: useNavigation', () => {
|
|||
})
|
||||
|
||||
it('should return already active navigation', async () => {
|
||||
const view = new View({ getContents: () => Promise.reject(), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
navigation.register(view)
|
||||
navigation.setActive(view)
|
||||
// Now the navigation is already set it should take the active navigation
|
||||
|
@ -47,7 +45,7 @@ describe('Composables: useNavigation', () => {
|
|||
})
|
||||
|
||||
it('should be reactive on updating active navigation', async () => {
|
||||
const view = new View({ getContents: () => Promise.reject(), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
navigation.register(view)
|
||||
const wrapper = mount(TestComponent)
|
||||
|
||||
|
@ -72,7 +70,7 @@ describe('Composables: useNavigation', () => {
|
|||
})
|
||||
|
||||
it('should return already registered views', () => {
|
||||
const view = new View({ getContents: () => Promise.reject(), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
// register before mount
|
||||
navigation.register(view)
|
||||
// now mount and check that the view is listed
|
||||
|
@ -81,8 +79,8 @@ describe('Composables: useNavigation', () => {
|
|||
})
|
||||
|
||||
it('should be reactive on registering new views', () => {
|
||||
const view = new View({ getContents: () => Promise.reject(), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
const view2 = new View({ getContents: () => Promise.reject(), icon: '<svg></svg>', id: 'view-2', name: 'My View 2', order: 1 })
|
||||
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
|
||||
const view2 = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-2', name: 'My View 2', order: 1 })
|
||||
|
||||
// register before mount
|
||||
navigation.register(view)
|
||||
|
|
|
@ -24,7 +24,6 @@ import registerPersonalFilesView from './views/personal-files'
|
|||
import registerFilesView from './views/files'
|
||||
import registerPreviewServiceWorker from './services/ServiceWorker.js'
|
||||
|
||||
|
||||
import { initLivePhotos } from './services/LivePhotos'
|
||||
|
||||
// Register file actions
|
||||
|
|
|
@ -19,9 +19,9 @@ __webpack_nonce__ = btoa(getRequestToken())
|
|||
|
||||
declare global {
|
||||
interface Window {
|
||||
OC: any;
|
||||
OCA: any;
|
||||
OCP: any;
|
||||
OC: Nextcloud.v28.OC;
|
||||
OCA: Record<string, unknown>;
|
||||
OCP: Nextcloud.v28.OCP;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { getFilePickerBuilder } from '@nextcloud/dialogs';
|
||||
import { getFilePickerBuilder } from '@nextcloud/dialogs'
|
||||
import { imagePath } from '@nextcloud/router'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import logger from '../../logger'
|
||||
|
@ -19,7 +19,7 @@ function init() {
|
|||
return
|
||||
}
|
||||
|
||||
logger.info('Initializing unified search plugin: folder search from files app');
|
||||
logger.info('Initializing unified search plugin: folder search from files app')
|
||||
OCA.UnifiedSearch.registerFilterAction({
|
||||
id: 'files',
|
||||
appId: 'files',
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
*/
|
||||
import { Node, registerDavProperty } from '@nextcloud/files'
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function initLivePhotos(): void {
|
||||
registerDavProperty('nc:metadata-files-live-photo', { nc: 'http://nextcloud.org/ns' })
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ const SWCacheName = 'previews'
|
|||
|
||||
/**
|
||||
* Check if the preview is already cached by the service worker
|
||||
* @param previewUrl
|
||||
*/
|
||||
export const isCachedPreview = function(previewUrl: string): Promise<boolean> {
|
||||
if (!window?.caches?.open) {
|
||||
|
|
|
@ -14,6 +14,7 @@ export const useDragAndDropStore = defineStore('dragging', {
|
|||
actions: {
|
||||
/**
|
||||
* Set the selection of fileIds
|
||||
* @param selection
|
||||
*/
|
||||
set(selection = [] as FileSource[]) {
|
||||
Vue.set(this, 'dragging', selection)
|
||||
|
|
|
@ -34,12 +34,14 @@ export const useFilesStore = function(...args) {
|
|||
getters: {
|
||||
/**
|
||||
* Get a file or folder by its source
|
||||
* @param state
|
||||
*/
|
||||
getNode: (state) => (source: FileSource): Node|undefined => state.files[source],
|
||||
|
||||
/**
|
||||
* Get a list of files or folders by their IDs
|
||||
* Note: does not return undefined values
|
||||
* @param state
|
||||
*/
|
||||
getNodes: (state) => (sources: FileSource[]): Node[] => sources
|
||||
.map(source => state.files[source])
|
||||
|
@ -49,11 +51,13 @@ export const useFilesStore = function(...args) {
|
|||
* Get files or folders by their file ID
|
||||
* Multiple nodes can have the same file ID but different sources
|
||||
* (e.g. in a shared context)
|
||||
* @param state
|
||||
*/
|
||||
getNodesById: (state) => (fileId: number): Node[] => Object.values(state.files).filter(node => node.fileid === fileId),
|
||||
|
||||
/**
|
||||
* Get the root folder of a service
|
||||
* @param state
|
||||
*/
|
||||
getRoot: (state) => (service: Service): Folder|undefined => state.roots[service],
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import Vue from 'vue'
|
|||
* Observe various events and save the current
|
||||
* special keys states. Useful for checking the
|
||||
* current status of a key when executing a method.
|
||||
* @param {...any} args
|
||||
*/
|
||||
export const useKeyboardStore = function(...args) {
|
||||
const store = defineStore('keyboard', {
|
||||
|
|
|
@ -16,6 +16,7 @@ export const useSelectionStore = defineStore('selection', {
|
|||
actions: {
|
||||
/**
|
||||
* Set the selection of fileIds
|
||||
* @param selection
|
||||
*/
|
||||
set(selection = [] as FileSource[]) {
|
||||
Vue.set(this, 'selected', [...new Set(selection)])
|
||||
|
@ -23,6 +24,7 @@ export const useSelectionStore = defineStore('selection', {
|
|||
|
||||
/**
|
||||
* Set the last selected index
|
||||
* @param lastSelectedIndex
|
||||
*/
|
||||
setLastIndex(lastSelectedIndex = null as number | null) {
|
||||
// Update the last selection if we provided a new selection starting point
|
||||
|
|
|
@ -27,6 +27,8 @@ export const useUserConfigStore = function(...args) {
|
|||
actions: {
|
||||
/**
|
||||
* Update the user config local store
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
onUpdate(key: string, value: boolean) {
|
||||
Vue.set(this.userConfig, key, value)
|
||||
|
@ -34,6 +36,8 @@ export const useUserConfigStore = function(...args) {
|
|||
|
||||
/**
|
||||
* Update the user config local store AND on server side
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
async update(key: string, value: boolean) {
|
||||
await axios.put(generateUrl('/apps/files/api/v1/config/' + key), {
|
||||
|
|
|
@ -26,6 +26,9 @@ export const useViewConfigStore = function(...args) {
|
|||
actions: {
|
||||
/**
|
||||
* Update the view config local store
|
||||
* @param view
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
onUpdate(view: ViewId, key: string, value: string | number | boolean) {
|
||||
if (!this.viewConfig[view]) {
|
||||
|
@ -36,6 +39,9 @@ export const useViewConfigStore = function(...args) {
|
|||
|
||||
/**
|
||||
* Update the view config local store AND on server side
|
||||
* @param view
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
async update(view: ViewId, key: string, value: string | number | boolean) {
|
||||
axios.put(generateUrl(`/apps/files/api/v1/views/${view}/${key}`), {
|
||||
|
@ -49,6 +55,8 @@ export const useViewConfigStore = function(...args) {
|
|||
* 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
|
||||
*/
|
||||
setSortingBy(key = 'basename', view = 'files') {
|
||||
// Save new config
|
||||
|
@ -58,6 +66,7 @@ export const useViewConfigStore = function(...args) {
|
|||
|
||||
/**
|
||||
* Toggle the sorting direction
|
||||
* @param view
|
||||
*/
|
||||
toggleSortingDirection(view = 'files') {
|
||||
const config = this.getConfig(view) || { sorting_direction: 'asc' }
|
||||
|
|
|
@ -30,9 +30,10 @@ export const action = new FileAction({
|
|||
/**
|
||||
* Use this function to check the storage availability
|
||||
* We then update the node attributes directly.
|
||||
* @param node
|
||||
*/
|
||||
async renderInline(node: Node) {
|
||||
let config = null as any as StorageConfig
|
||||
let config = null as unknown as StorageConfig
|
||||
try {
|
||||
const response = await getStatus(node.attributes.id, node.attributes.scope === 'system')
|
||||
config = response.data
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('OCA.Files_External.App tests', function() {
|
|||
+ '<div id="app-content-extstoragemounts" class="hidden">'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>',
|
||||
)
|
||||
fileList = App.initList($('#app-content-extstoragemounts'))
|
||||
})
|
||||
|
|
|
@ -31,7 +31,7 @@ const laterToday: ReminderOption = {
|
|||
label: t('files_reminders', 'Later today'),
|
||||
ariaLabel: t('files_reminders', 'Set reminder for later today'),
|
||||
dateString: '',
|
||||
verboseDateString: ''
|
||||
verboseDateString: '',
|
||||
}
|
||||
|
||||
const tomorrow: ReminderOption = {
|
||||
|
@ -39,7 +39,7 @@ const tomorrow: ReminderOption = {
|
|||
label: t('files_reminders', 'Tomorrow'),
|
||||
ariaLabel: t('files_reminders', 'Set reminder for tomorrow'),
|
||||
dateString: '',
|
||||
verboseDateString: ''
|
||||
verboseDateString: '',
|
||||
}
|
||||
|
||||
const thisWeekend: ReminderOption = {
|
||||
|
@ -47,7 +47,7 @@ const thisWeekend: ReminderOption = {
|
|||
label: t('files_reminders', 'This weekend'),
|
||||
ariaLabel: t('files_reminders', 'Set reminder for this weekend'),
|
||||
dateString: '',
|
||||
verboseDateString: ''
|
||||
verboseDateString: '',
|
||||
}
|
||||
|
||||
const nextWeek: ReminderOption = {
|
||||
|
@ -55,7 +55,7 @@ const nextWeek: ReminderOption = {
|
|||
label: t('files_reminders', 'Next week'),
|
||||
ariaLabel: t('files_reminders', 'Set reminder for next week'),
|
||||
dateString: '',
|
||||
verboseDateString: ''
|
||||
verboseDateString: '',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
export interface FileAttributes {
|
||||
[key: string]: any
|
||||
[key: string]: unknown
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
|
|
@ -27,15 +27,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
import debounce from 'debounce'
|
||||
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
|
||||
|
||||
import Config from '../services/ConfigService.js'
|
||||
import GeneratePassword from '../utils/GeneratePassword.js'
|
||||
import Share from '../models/Share.js'
|
||||
import ShareRequests from '../mixins/ShareRequests.js'
|
||||
import ShareTypes from '../mixins/ShareTypes.js'
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
import Share from '../models/Share.js'
|
||||
import Config from '../services/ConfigService.js'
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
/* eslint-disable camelcase, n/no-extraneous-import */
|
||||
// TODO: Fix this instead of disabling ESLint!!!
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import type { AxiosPromise } from '@nextcloud/axios'
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
|
||||
|
@ -71,7 +73,7 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
|
|||
'owner-id': ocsEntry?.uid_owner,
|
||||
'owner-display-name': ocsEntry?.displayname_owner,
|
||||
'share-types': ocsEntry?.share_type,
|
||||
favorite: ocsEntry?.tags?.includes(window.OC.TAG_FAVORITE) ? 1 : 0,
|
||||
favorite: ocsEntry?.tags?.includes((window.OC as Nextcloud.v28.OC & { TAG_FAVORITE: string }).TAG_FAVORITE) ? 1 : 0,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
@ -80,12 +82,12 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
|
|||
}
|
||||
}
|
||||
|
||||
const getShares = function(shared_with_me = false): AxiosPromise<OCSResponse<any>> {
|
||||
const getShares = function(shareWithMe = false): AxiosPromise<OCSResponse<any>> {
|
||||
const url = generateOcsUrl('apps/files_sharing/api/v1/shares')
|
||||
return axios.get(url, {
|
||||
headers,
|
||||
params: {
|
||||
shared_with_me,
|
||||
shared_with_me: shareWithMe,
|
||||
include_tags: true,
|
||||
},
|
||||
})
|
||||
|
@ -142,6 +144,8 @@ const getDeletedShares = function(): AxiosPromise<OCSResponse<any>> {
|
|||
/**
|
||||
* Group an array of objects (here Nodes) by a key
|
||||
* and return an array of arrays of them.
|
||||
* @param nodes
|
||||
* @param key
|
||||
*/
|
||||
const groupBy = function(nodes: (Folder | File)[], key: string) {
|
||||
return Object.values(nodes.reduce(function(acc, curr) {
|
||||
|
|
|
@ -14,12 +14,12 @@ const client = createClient(rootUrl)
|
|||
|
||||
// set CSRF token header
|
||||
const setHeaders = (token: string | null) => {
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
// refresh headers when request token changes
|
||||
|
|
|
@ -14,16 +14,16 @@ const client = createClient(remote)
|
|||
|
||||
// set CSRF token header
|
||||
const setHeaders = (token) => {
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
client.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
// refresh headers when request token changes
|
||||
onRequestTokenUpdate(setHeaders)
|
||||
setHeaders(getRequestToken())
|
||||
|
||||
export default client
|
||||
export default client
|
||||
|
|
|
@ -102,7 +102,7 @@ export default {
|
|||
methods: {
|
||||
deleteClient(id) {
|
||||
axios.delete(generateUrl('apps/oauth2/clients/{id}', { id }))
|
||||
.then((response) => {
|
||||
.then(() => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.clients = this.clients.filter(client => client.id !== id)
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
:aria-label="toggleAriaLabel"
|
||||
@click="toggleSecret">
|
||||
<template #icon>
|
||||
<EyeOutline :size="20"/>
|
||||
<EyeOutline :size="20" />
|
||||
</template>
|
||||
</NcButton>
|
||||
</div>
|
||||
|
@ -72,9 +72,9 @@ export default {
|
|||
toggleAriaLabel() {
|
||||
if (!this.renderSecret) {
|
||||
return t('oauth2', 'Show client secret')
|
||||
}
|
||||
}
|
||||
return t('oauth2', 'Hide client secret')
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleSecret() {
|
||||
|
|
|
@ -62,18 +62,24 @@
|
|||
<label>{{ t('settings', 'Limit sharing based on groups') }}</label>
|
||||
<div class="sharing__sub-section">
|
||||
<NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
|
||||
name="excludeGroups" value="no"
|
||||
type="radio" @update:checked="onUpdateExcludeGroups">
|
||||
name="excludeGroups"
|
||||
value="no"
|
||||
type="radio"
|
||||
@update:checked="onUpdateExcludeGroups">
|
||||
{{ t('settings', 'Allow sharing for everyone (default)') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
|
||||
name="excludeGroups" value="yes"
|
||||
type="radio" @update:checked="onUpdateExcludeGroups">
|
||||
name="excludeGroups"
|
||||
value="yes"
|
||||
type="radio"
|
||||
@update:checked="onUpdateExcludeGroups">
|
||||
{{ t('settings', 'Exclude some groups from sharing') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
|
||||
name="excludeGroups" value="allow"
|
||||
type="radio" @update:checked="onUpdateExcludeGroups">
|
||||
name="excludeGroups"
|
||||
value="allow"
|
||||
type="radio"
|
||||
@update:checked="onUpdateExcludeGroups">
|
||||
{{ t('settings', 'Limit sharing to some groups') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<div v-show="settings.excludeGroups !== 'no'" class="sharing__labeled-entry sharing__input">
|
||||
|
@ -305,7 +311,7 @@ export default defineComponent({
|
|||
onUpdateExcludeGroups: debounce(function(value: string) {
|
||||
window.OCP.AppConfig.setValue('core', 'excludeGroups', value)
|
||||
this.settings.excludeGroups = value
|
||||
}, 500) as (v?: string) => void
|
||||
}, 500) as (v?: string) => void,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -63,13 +63,15 @@
|
|||
<script>
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import axios from '@nextcloud/axios'
|
||||
import moment from '@nextcloud/moment'
|
||||
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
|
||||
import moment from '@nextcloud/moment'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
const lastCron = loadState('settings', 'lastCron')
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<NcSettingsSection
|
||||
class="declarative-settings-section"
|
||||
<NcSettingsSection class="declarative-settings-section"
|
||||
:name="t(formApp, form.title)"
|
||||
:description="t(formApp, form.description)"
|
||||
:doc-url="form.doc_url || ''">
|
||||
<div v-for="formField in formFields"
|
||||
:key="formField.id"
|
||||
class="declarative-form-field"
|
||||
:key="formField.id"
|
||||
class="declarative-form-field"
|
||||
:aria-label="t('settings', '{app}\'s declarative setting field: {name}', { app: formApp, name: t(formApp, formField.title) })"
|
||||
:class="{
|
||||
'declarative-form-field-text': isTextFormField(formField),
|
||||
|
@ -20,16 +19,14 @@
|
|||
'declarative-form-field-multi_checkbox': formField.type === 'multi-checkbox',
|
||||
'declarative-form-field-radio': formField.type === 'radio'
|
||||
}">
|
||||
|
||||
<template v-if="isTextFormField(formField)">
|
||||
<div class="input-wrapper">
|
||||
<NcInputField
|
||||
:type="formField.type"
|
||||
<NcInputField :type="formField.type"
|
||||
:label="t(formApp, formField.title)"
|
||||
:value.sync="formFieldsData[formField.id].value"
|
||||
:placeholder="t(formApp, formField.placeholder)"
|
||||
@update:value="onChangeDebounced(formField)"
|
||||
@submit="updateDeclarativeSettingsValue(formField)"/>
|
||||
@submit="updateDeclarativeSettingsValue(formField)" />
|
||||
</div>
|
||||
<span class="hint">{{ t(formApp, formField.description) }}</span>
|
||||
</template>
|
||||
|
@ -37,13 +34,12 @@
|
|||
<template v-if="formField.type === 'select'">
|
||||
<label :for="formField.id + '_field'">{{ t(formApp, formField.title) }}</label>
|
||||
<div class="input-wrapper">
|
||||
<NcSelect
|
||||
:id="formField.id + '_field'"
|
||||
<NcSelect :id="formField.id + '_field'"
|
||||
:options="formField.options"
|
||||
:placeholder="t(formApp, formField.placeholder)"
|
||||
:label-outside="true"
|
||||
:value="formFieldsData[formField.id].value"
|
||||
@input="(value) => updateFormFieldDataValue(value, formField, true)"/>
|
||||
@input="(value) => updateFormFieldDataValue(value, formField, true)" />
|
||||
</div>
|
||||
<span class="hint">{{ t(formApp, formField.description) }}</span>
|
||||
</template>
|
||||
|
@ -51,8 +47,7 @@
|
|||
<template v-if="formField.type === 'multi-select'">
|
||||
<label :for="formField.id + '_field'">{{ t(formApp, formField.title) }}</label>
|
||||
<div class="input-wrapper">
|
||||
<NcSelect
|
||||
:id="formField.id + '_field'"
|
||||
<NcSelect :id="formField.id + '_field'"
|
||||
:options="formField.options"
|
||||
:placeholder="t(formApp, formField.placeholder)"
|
||||
:multiple="true"
|
||||
|
@ -62,21 +57,20 @@
|
|||
formFieldsData[formField.id].value = value
|
||||
updateDeclarativeSettingsValue(formField, JSON.stringify(formFieldsData[formField.id].value))
|
||||
}
|
||||
"/>
|
||||
" />
|
||||
</div>
|
||||
<span class="hint">{{ t(formApp, formField.description) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="formField.type === 'checkbox'">
|
||||
<label :for="formField.id + '_field'">{{ t(formApp, formField.title) }}</label>
|
||||
<NcCheckboxRadioSwitch
|
||||
:id="formField.id + '_field'"
|
||||
<NcCheckboxRadioSwitch :id="formField.id + '_field'"
|
||||
:checked="Boolean(formFieldsData[formField.id].value)"
|
||||
@update:checked="(value) => {
|
||||
formField.value = value
|
||||
updateFormFieldDataValue(+value, formField, true)
|
||||
}
|
||||
">
|
||||
">
|
||||
{{ t(formApp, formField.label) }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<span class="hint">{{ t(formApp, formField.description) }}</span>
|
||||
|
@ -84,8 +78,7 @@
|
|||
|
||||
<template v-if="formField.type === 'multi-checkbox'">
|
||||
<label :for="formField.id + '_field'">{{ t(formApp, formField.title) }}</label>
|
||||
<NcCheckboxRadioSwitch
|
||||
v-for="option in formField.options"
|
||||
<NcCheckboxRadioSwitch v-for="option in formField.options"
|
||||
:id="formField.id + '_field_' + option.value"
|
||||
:key="option.value"
|
||||
:checked="formFieldsData[formField.id].value[option.value]"
|
||||
|
@ -94,7 +87,7 @@
|
|||
// Update without re-generating initial formFieldsData.value object as the link to components are lost
|
||||
updateDeclarativeSettingsValue(formField, JSON.stringify(formFieldsData[formField.id].value))
|
||||
}
|
||||
">
|
||||
">
|
||||
{{ t(formApp, option.name) }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<span class="hint">{{ t(formApp, formField.description) }}</span>
|
||||
|
@ -102,8 +95,7 @@
|
|||
|
||||
<template v-if="formField.type === 'radio'">
|
||||
<label :for="formField.id + '_field'">{{ t(formApp, formField.title) }}</label>
|
||||
<NcCheckboxRadioSwitch
|
||||
v-for="option in formField.options"
|
||||
<NcCheckboxRadioSwitch v-for="option in formField.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
type="radio"
|
||||
|
@ -146,9 +138,6 @@ export default {
|
|||
formFieldsData: {},
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.initFormFieldsData()
|
||||
},
|
||||
computed: {
|
||||
formApp() {
|
||||
return this.form.app || ''
|
||||
|
@ -157,6 +146,9 @@ export default {
|
|||
return this.form.fields || []
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.initFormFieldsData()
|
||||
},
|
||||
methods: {
|
||||
initFormFieldsData() {
|
||||
this.form.fields.forEach((formField) => {
|
||||
|
@ -175,7 +167,7 @@ export default {
|
|||
this.$set(formField, 'value', JSON.parse(formField.value))
|
||||
// Merge possible new options
|
||||
formField.options.forEach(option => {
|
||||
if (!formField.value.hasOwnProperty(option.value)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(formField.value, option.value)) {
|
||||
this.$set(formField.value, option.value, false)
|
||||
}
|
||||
})
|
||||
|
@ -216,7 +208,7 @@ export default {
|
|||
formId: this.form.id.replace(this.formApp + '_', ''), // Remove app prefix to send clean form id
|
||||
fieldId: formField.id,
|
||||
value: value === null ? this.formFieldsData[formField.id].value : value,
|
||||
});
|
||||
})
|
||||
} catch (err) {
|
||||
console.debug(err)
|
||||
showError(t('settings', 'Failed to save setting'))
|
||||
|
|
|
@ -59,22 +59,18 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import axios from '@nextcloud/axios'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { getLoggerBuilder } from '@nextcloud/logger'
|
||||
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import logger from '../logger'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
|
||||
const logger = getLoggerBuilder()
|
||||
.setApp('settings')
|
||||
.detectUser()
|
||||
.build()
|
||||
|
||||
export default {
|
||||
name: 'Encryption',
|
||||
|
@ -122,7 +118,7 @@ export default {
|
|||
|
||||
try {
|
||||
const { data } = await axios.post(url, {
|
||||
value: value,
|
||||
value,
|
||||
})
|
||||
this.handleResponse({
|
||||
status: data.ocs?.meta?.status,
|
||||
|
|
|
@ -23,15 +23,15 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import HeaderBar from './shared/HeaderBar.vue'
|
||||
import AccountPropertySection from './shared/AccountPropertySection.vue'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { NAME_READABLE_ENUM } from '../../constants/AccountPropertyConstants.js'
|
||||
import { NcDateTimePickerNative } from '@nextcloud/vue'
|
||||
import debounce from 'debounce'
|
||||
import { savePrimaryAccountProperty } from '../../service/PersonalInfo/PersonalInfoService'
|
||||
import { handleError } from '../../utils/handlers'
|
||||
import AlertCircle from 'vue-material-design-icons/AlertCircleOutline.vue'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
import debounce from 'debounce'
|
||||
|
||||
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
|
||||
import HeaderBar from './shared/HeaderBar.vue'
|
||||
|
||||
const { birthdate } = loadState('settings', 'personalInfoParameters', {})
|
||||
|
||||
|
@ -39,8 +39,6 @@ export default {
|
|||
name: 'BirthdaySection',
|
||||
|
||||
components: {
|
||||
AlertCircle,
|
||||
AccountPropertySection,
|
||||
NcDateTimePickerNative,
|
||||
HeaderBar,
|
||||
},
|
||||
|
@ -74,7 +72,7 @@ export default {
|
|||
const month = (value.getMonth() + 1).toString().padStart(2, '0')
|
||||
const year = value.getFullYear()
|
||||
this.birthdate.value = `${year}-${month}-${day}`
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
|||
import UserRowActions from './UserRowActions.vue'
|
||||
|
||||
import UserRowMixin from '../../mixins/UserRowMixin.js'
|
||||
import { isObfuscated, unlimitedQuota } from '../../utils/userUtils.ts';
|
||||
import { isObfuscated, unlimitedQuota } from '../../utils/userUtils.ts'
|
||||
|
||||
export default {
|
||||
name: 'UserRow',
|
||||
|
|
|
@ -59,6 +59,8 @@ import {
|
|||
finishRegistration,
|
||||
} from '../../service/WebAuthnRegistrationSerice.ts'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
const logAndPass = (text) => (data) => {
|
||||
logger.debug(text)
|
||||
return data
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
import { browserSupportsWebAuthn } from '@simplewebauthn/browser'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import sortBy from 'lodash/fp/sortBy.js'
|
||||
|
||||
import AddDevice from './AddDevice.vue'
|
||||
|
@ -46,6 +45,8 @@ import Device from './Device.vue'
|
|||
import logger from '../../logger.ts'
|
||||
import { removeRegistration } from '../../service/WebAuthnRegistrationSerice.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
const sortByName = sortBy('name')
|
||||
|
||||
export default {
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import Vue from 'vue';
|
||||
import { loadState } from '@nextcloud/initial-state';
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n';
|
||||
import DeclarativeSection from './components/DeclarativeSettings/DeclarativeSection.vue';
|
||||
import type { ComponentInstance } from 'vue'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
import DeclarativeSection from './components/DeclarativeSettings/DeclarativeSection.vue'
|
||||
|
||||
interface DeclarativeFormField {
|
||||
id: string,
|
||||
|
@ -14,9 +16,9 @@ interface DeclarativeFormField {
|
|||
type: string,
|
||||
placeholder: string,
|
||||
label: string,
|
||||
options: Array<any>|null,
|
||||
value: any,
|
||||
default: any,
|
||||
options: Array<unknown>|null,
|
||||
value: unknown,
|
||||
default: unknown,
|
||||
}
|
||||
|
||||
interface DeclarativeForm {
|
||||
|
@ -32,23 +34,28 @@ interface DeclarativeForm {
|
|||
fields: Array<DeclarativeFormField>,
|
||||
}
|
||||
|
||||
const forms = loadState('settings', 'declarative-settings-forms', []) as Array<DeclarativeForm>;
|
||||
console.debug('Loaded declarative forms:', forms);
|
||||
const forms = loadState('settings', 'declarative-settings-forms', []) as Array<DeclarativeForm>
|
||||
console.debug('Loaded declarative forms:', forms)
|
||||
|
||||
function renderDeclarativeSettingsSections(forms: Array<DeclarativeForm>): void {
|
||||
/**
|
||||
*
|
||||
* @param forms
|
||||
*/
|
||||
function renderDeclarativeSettingsSections(forms: Array<DeclarativeForm>): ComponentInstance[] {
|
||||
Vue.mixin({ methods: { t, n } })
|
||||
const DeclarativeSettingsSection = Vue.extend(<any>DeclarativeSection);
|
||||
for (const form of forms) {
|
||||
const DeclarativeSettingsSection = Vue.extend(DeclarativeSection as never)
|
||||
|
||||
return forms.map((form) => {
|
||||
const el = `#${form.app}_${form.id}`
|
||||
new DeclarativeSettingsSection({
|
||||
el: el,
|
||||
return new DeclarativeSettingsSection({
|
||||
el,
|
||||
propsData: {
|
||||
form,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDeclarativeSettingsSections(forms);
|
||||
});
|
||||
renderDeclarativeSettingsSections(forms)
|
||||
})
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import { ACCOUNT_PROPERTY_ENUM, SCOPE_SUFFIX } from '../../constants/AccountPropertyConstants.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
/**
|
||||
* Save the primary email of the user
|
||||
*
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import { SCOPE_SUFFIX } from '../../constants/AccountPropertyConstants.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
/**
|
||||
* Save the primary account property value for the user
|
||||
*
|
||||
|
|
|
@ -184,7 +184,7 @@ const actions = {
|
|||
showInfo(
|
||||
t(
|
||||
'settings',
|
||||
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.'
|
||||
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.',
|
||||
),
|
||||
{
|
||||
onClick: () => window.location.reload(),
|
||||
|
|
|
@ -12,6 +12,8 @@ import { defineStore } from 'pinia'
|
|||
import axios from '@nextcloud/axios'
|
||||
import logger from '../logger'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
const BASE_URL = generateUrl('/settings/personal/authtokens')
|
||||
|
||||
const confirm = () => {
|
||||
|
|
|
@ -390,6 +390,7 @@ const actions = {
|
|||
* @param {object} options destructuring object
|
||||
* @param {number} options.offset List offset to request
|
||||
* @param {number} options.limit List number to return from offset
|
||||
* @param options.search
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
async getDisabledUsers(context, { offset, limit, search }) {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
export const unlimitedQuota = {
|
||||
id: 'none',
|
||||
label: t('settings', 'Unlimited'),
|
||||
|
@ -19,7 +21,7 @@ export const defaultQuota = {
|
|||
* @param user
|
||||
* @param user.id
|
||||
*/
|
||||
export const isObfuscated = (user: { id: string, [key: string]: any }) => {
|
||||
export const isObfuscated = (user: { id: string, [key: string]: unknown }) => {
|
||||
const keys = Object.keys(user)
|
||||
return keys.length === 1 && keys.at(0) === 'id'
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import axios from '@nextcloud/axios'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -52,6 +52,7 @@ export const fetchLastUsedTagIds = async (): Promise<number[]> => {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* @return created tag id
|
||||
*/
|
||||
export const createTag = async (tag: Tag | ServerTag): Promise<number> => {
|
||||
|
|
|
@ -13,12 +13,12 @@ export const davClient = createClient(rootUrl)
|
|||
|
||||
// set CSRF token header
|
||||
const setHeaders = (token: string | null) => {
|
||||
davClient.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
davClient.setHeaders({
|
||||
// Add this so the server knows it is an request from the browser
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
// Inject user auth
|
||||
requesttoken: token ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
// refresh headers when request token changes
|
||||
|
|
|
@ -27,6 +27,8 @@ export const fetchTagsForFile = async (fileId: number): Promise<TagWithId[]> =>
|
|||
}
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* @param fileId
|
||||
* @return created tag id
|
||||
*/
|
||||
export const createTagForFile = async (tag: Tag, fileId: number): Promise<number> => {
|
||||
|
|
|
@ -45,12 +45,13 @@ export const parseIdFromLocation = (url: string): number => {
|
|||
}
|
||||
|
||||
export const formatTag = (initialTag: Tag | ServerTag): ServerTag => {
|
||||
const tag: any = { ...initialTag }
|
||||
if (tag.name && !tag.displayName) {
|
||||
return tag
|
||||
if ('name' in initialTag && !('displayName' in initialTag)) {
|
||||
return { ...initialTag }
|
||||
}
|
||||
|
||||
const tag: Record<string, unknown> = { ...initialTag }
|
||||
tag.name = tag.displayName
|
||||
delete tag.displayName
|
||||
|
||||
return tag
|
||||
return tag as unknown as ServerTag
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
|
|||
const backgroundImage = loadState('theming', 'userBackgroundImage')
|
||||
const {
|
||||
backgroundImage: defaultBackgroundImage,
|
||||
backgroundColor: defaultBackgroundColor,
|
||||
// backgroundColor: defaultBackgroundColor,
|
||||
backgroundMime: defaultBackgroundMime,
|
||||
defaultShippedBackground,
|
||||
} = loadState('theming', 'themingDefaults')
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
<div class="theming__preview-image" :style="{ backgroundImage: 'url(' + img + ')' }" @click="onToggle" />
|
||||
<div class="theming__preview-description">
|
||||
<h3>{{ theme.title }}</h3>
|
||||
<p class="theming__preview-explanation">{{ theme.description }}</p>
|
||||
<p class="theming__preview-explanation">
|
||||
{{ theme.description }}
|
||||
</p>
|
||||
<span v-if="enforced" class="theming__preview-warning" role="note">
|
||||
{{ t('theming', 'Theme selection is enforced') }}
|
||||
</span>
|
||||
|
|
|
@ -7,15 +7,17 @@
|
|||
<div class="field">
|
||||
<label :for="id">{{ displayName }}</label>
|
||||
<div class="field__row">
|
||||
<NcCheckboxRadioSwitch type="switch"
|
||||
:id="id"
|
||||
<NcCheckboxRadioSwitch :id="id"
|
||||
type="switch"
|
||||
:checked.sync="localValue"
|
||||
@update:checked="save">
|
||||
{{ label }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</div>
|
||||
|
||||
<p class="field__description">{{ description }}</p>
|
||||
<p class="field__description">
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
<NcNoteCard v-if="errorMessage"
|
||||
type="error"
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<div class="field">
|
||||
<label :for="id">{{ displayName }}</label>
|
||||
<div class="field__row">
|
||||
<NcButton type="secondary"
|
||||
:id="id"
|
||||
<NcButton :id="id"
|
||||
type="secondary"
|
||||
:aria-label="ariaLabel"
|
||||
data-admin-theming-setting-file-picker
|
||||
@click="activateLocalFilePicker">
|
||||
|
|
|
@ -49,9 +49,10 @@
|
|||
|
||||
<script>
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import { print } from '../service/PrintService.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
export default {
|
||||
name: 'PersonalSettings',
|
||||
data() {
|
||||
|
@ -97,7 +98,7 @@ export default {
|
|||
// Hide old codes
|
||||
this.generatingCodes = true
|
||||
|
||||
this.$store.dispatch('generate').then(data => {
|
||||
this.$store.dispatch('generate').then(() => {
|
||||
this.generatingCodes = false
|
||||
}).catch(err => {
|
||||
OC.Notification.showTemporary(t('twofactor_backupcodes', 'An error occurred while generating your backup codes'))
|
||||
|
|
|
@ -132,7 +132,7 @@ export default {
|
|||
this.$set(this.rule, 'operation', operation)
|
||||
await this.updateRule()
|
||||
},
|
||||
validate(state) {
|
||||
validate(/* state */) {
|
||||
this.error = null
|
||||
this.$store.dispatch('updateRule', this.rule)
|
||||
},
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
import Vue from 'vue'
|
||||
import Vuex, { Store } from 'vuex'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { getApiUrl } from './helpers/api.js'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { getApiUrl } from './helpers/api.js'
|
||||
|
||||
import '@nextcloud/password-confirmation/dist/style.css'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-transform-class-properties',
|
||||
// We need the bundler entry not the web one
|
||||
// Jest will otherwise resolve the wrong one
|
||||
[
|
||||
|
|
|
@ -104,7 +104,7 @@ export const hideMenus = function(complete) {
|
|||
/**
|
||||
* Shows a given element as menu
|
||||
*
|
||||
* @param {object} [$toggle=null] menu toggle
|
||||
* @param {object} [$toggle] menu toggle
|
||||
* @param {object} $menuEl menu element
|
||||
* @param {Function} complete callback when the showing animation is done
|
||||
*/
|
||||
|
|
|
@ -76,7 +76,7 @@ export default {
|
|||
* @param {string} html Message to display
|
||||
* @param {object} [options] options
|
||||
* @param {string} [options.type] notification type
|
||||
* @param {number} [options.timeout=0] timeout value, defaults to 0 (permanent)
|
||||
* @param {number} [options.timeout] timeout value, defaults to 0 (permanent)
|
||||
* @return {jQuery} jQuery element for notification row
|
||||
* @deprecated 17.0.0 use the `@nextcloud/dialogs` package
|
||||
*/
|
||||
|
@ -95,7 +95,7 @@ export default {
|
|||
* @param {string} text Message to display
|
||||
* @param {object} [options] options
|
||||
* @param {string} [options.type] notification type
|
||||
* @param {number} [options.timeout=0] timeout value, defaults to 0 (permanent)
|
||||
* @param {number} [options.timeout] timeout value, defaults to 0 (permanent)
|
||||
* @return {jQuery} jQuery element for notification row
|
||||
* @deprecated 17.0.0 use the `@nextcloud/dialogs` package
|
||||
*/
|
||||
|
@ -138,8 +138,8 @@ export default {
|
|||
*
|
||||
* @param {string} text Message to show
|
||||
* @param {Array} [options] options array
|
||||
* @param {number} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently
|
||||
* @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)
|
||||
* @param {number} [options.timeout] timeout in seconds, if this is 0 it will show the message permanently
|
||||
* @param {boolean} [options.isHTML] an indicator for HTML notifications (true) or text (false)
|
||||
* @param {string} [options.type] notification type
|
||||
* @return {JQuery} the toast element
|
||||
* @deprecated 17.0.0 use the `@nextcloud/dialogs` package
|
||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
|||
* or a map
|
||||
* @param {string} [url] URL to be used, otherwise the current URL will be used,
|
||||
* using the params as query string
|
||||
* @param {boolean} [replace=false] whether to replace instead of pushing
|
||||
* @param {boolean} [replace] whether to replace instead of pushing
|
||||
*/
|
||||
_pushState(params, url, replace) {
|
||||
let strParams
|
||||
|
|
|
@ -47,7 +47,7 @@ export const processAjaxError = xhr => {
|
|||
OC.reload()
|
||||
}
|
||||
timer++
|
||||
}, 1000 // 1 second interval
|
||||
}, 1000, // 1 second interval
|
||||
)
|
||||
|
||||
// only call reload once
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
export default function getURLParameter(name) {
|
||||
return decodeURIComponent(
|
||||
// eslint-disable-next-line no-sparse-arrays
|
||||
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20')
|
||||
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20'),
|
||||
) || ''
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ export default {
|
|||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -111,7 +111,7 @@ export default {
|
|||
loadingApps: true,
|
||||
loadingAppsError: false,
|
||||
apps: [],
|
||||
defaultPageUrl: loadState('core', 'defaultPageUrl')
|
||||
defaultPageUrl: loadState('core', 'defaultPageUrl'),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -89,7 +89,7 @@ const Template = {
|
|||
function(a, b) {
|
||||
const r = o[b]
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a
|
||||
}
|
||||
},
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e, 'data:', data)
|
||||
|
|
|
@ -52,8 +52,8 @@ const getInterval = () => {
|
|||
24 * 3600,
|
||||
Math.max(
|
||||
60,
|
||||
isNaN(interval) ? 900 : interval
|
||||
)
|
||||
isNaN(interval) ? 900 : interval,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* @param OC
|
||||
*/
|
||||
|
||||
(function(OC) {
|
||||
|
|
|
@ -17,19 +17,19 @@ describe('Contact', function() {
|
|||
topAction: {
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:deboraoliver%40centrexin.com'
|
||||
hyperlink: 'mailto:deboraoliver%40centrexin.com',
|
||||
},
|
||||
emailAddresses: [],
|
||||
actions: [
|
||||
{
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:mathisholland%40virxo.com'
|
||||
hyperlink: 'mailto:mathisholland%40virxo.com',
|
||||
},
|
||||
{
|
||||
title: 'Details',
|
||||
icon: 'icon-info',
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts'
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts',
|
||||
},
|
||||
],
|
||||
lastMessage: '',
|
||||
|
|
|
@ -82,19 +82,19 @@ describe('ContactsMenu', function() {
|
|||
topAction: {
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:deboraoliver%40centrexin.com'
|
||||
hyperlink: 'mailto:deboraoliver%40centrexin.com',
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:mathisholland%40virxo.com'
|
||||
hyperlink: 'mailto:mathisholland%40virxo.com',
|
||||
},
|
||||
{
|
||||
title: 'Details',
|
||||
icon: 'icon-info',
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts'
|
||||
}
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts',
|
||||
},
|
||||
],
|
||||
lastMessage: '',
|
||||
emailAddresses: [],
|
||||
|
@ -105,23 +105,23 @@ describe('ContactsMenu', function() {
|
|||
topAction: {
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:ceciliasoto%40essensia.com'
|
||||
hyperlink: 'mailto:ceciliasoto%40essensia.com',
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
title: 'Mail',
|
||||
icon: 'icon-mail',
|
||||
hyperlink: 'mailto:pearliesellers%40inventure.com'
|
||||
hyperlink: 'mailto:pearliesellers%40inventure.com',
|
||||
},
|
||||
{
|
||||
title: 'Details',
|
||||
icon: 'icon-info',
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts'
|
||||
}
|
||||
hyperlink: 'https://localhost/index.php/apps/contacts',
|
||||
},
|
||||
],
|
||||
lastMessage: 'cu',
|
||||
emailAddresses: [],
|
||||
}
|
||||
},
|
||||
],
|
||||
contactsAppEnabled: true,
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ import { getRootUrl } from '@nextcloud/router'
|
|||
/**
|
||||
*
|
||||
* @param {string} url the URL to check
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
const isRelativeUrl = (url) => {
|
||||
return !url.startsWith('https://') && !url.startsWith('http://')
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
</template>
|
||||
<div class="contactsmenu__menu">
|
||||
<div class="contactsmenu__menu__input-wrapper">
|
||||
<NcTextField :value.sync="searchTerm"
|
||||
trailing-button-icon="close"
|
||||
<NcTextField id="contactsmenu__menu__search"
|
||||
ref="contactsMenuInput"
|
||||
:value.sync="searchTerm"
|
||||
trailing-button-icon="close"
|
||||
:label="t('core', 'Search contacts')"
|
||||
:trailing-button-label="t('core','Reset search')"
|
||||
:show-trailing-button="searchTerm !== ''"
|
||||
:placeholder="t('core', 'Search contacts …')"
|
||||
id="contactsmenu__menu__search"
|
||||
class="contactsmenu__menu__search"
|
||||
@input="onInputDebounced"
|
||||
@trailing-button-click="onReset" />
|
||||
|
|
|
@ -38,7 +38,10 @@ const config: Config = {
|
|||
testMatch: ['<rootDir>/**/*.(spec|test).(ts|js)'],
|
||||
|
||||
clearMocks: true,
|
||||
setupFilesAfterEnv: ['<rootDir>/__tests__/jest-setup.ts'],
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/__tests__/jest-setup.ts',
|
||||
'<rootDir>/__tests__/mock-window.js',
|
||||
],
|
||||
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
preset: 'ts-jest/presets/js-with-ts',
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
22
package.json
22
package.json
|
@ -13,8 +13,8 @@
|
|||
"postbuild": "build/npm-post-build.sh",
|
||||
"dev": "webpack --node-env development --progress",
|
||||
"watch": "webpack --node-env development --progress --watch",
|
||||
"lint": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"$appdir \"; fi; done) core --no-error-on-unmatched-pattern",
|
||||
"lint:fix": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"$appdir \"; fi; done) core --no-error-on-unmatched-pattern --fix",
|
||||
"lint": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"apps/$appdir \"; fi; done) core --no-error-on-unmatched-pattern",
|
||||
"lint:fix": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"apps/$appdir \"; fi; done) core --no-error-on-unmatched-pattern --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
|
@ -56,7 +56,7 @@
|
|||
"@nextcloud/password-confirmation": "^5.1.1",
|
||||
"@nextcloud/paths": "^2.1.0",
|
||||
"@nextcloud/router": "^3.0.0",
|
||||
"@nextcloud/sharing": "^0.1.0",
|
||||
"@nextcloud/sharing": "^0.2.2",
|
||||
"@nextcloud/upload": "^1.4.1",
|
||||
"@nextcloud/vue": "^8.14.0",
|
||||
"@simplewebauthn/browser": "^10.0.0",
|
||||
|
@ -116,18 +116,18 @@
|
|||
"webdav": "^5.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/node": "^7.22.10",
|
||||
"@babel/preset-typescript": "^7.24.1",
|
||||
"@cypress/vue2": "^2.1.0",
|
||||
"@cypress/webpack-preprocessor": "^6.0.1",
|
||||
"@babel/node": "^7.24.7",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@cypress/vue2": "^2.1.1",
|
||||
"@cypress/webpack-preprocessor": "^6.0.2",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@nextcloud/babel-config": "^1.0.0",
|
||||
"@nextcloud/babel-config": "^1.2.0",
|
||||
"@nextcloud/cypress": "^1.0.0-beta.8",
|
||||
"@nextcloud/eslint-config": "^v8.4.1",
|
||||
"@nextcloud/eslint-config": "^8.4.1",
|
||||
"@nextcloud/stylelint-config": "^3.0.1",
|
||||
"@nextcloud/typings": "^1.8.0",
|
||||
"@nextcloud/typings": "^1.9.1",
|
||||
"@nextcloud/webpack-vue-config": "^6.0.1",
|
||||
"@pinia/testing": "^0.1.2",
|
||||
"@pinia/testing": "^0.1.3",
|
||||
"@simplewebauthn/types": "^10.0.0",
|
||||
"@testing-library/cypress": "^10.0.2",
|
||||
"@testing-library/jest-dom": "^6.4.6",
|
||||
|
|
Загрузка…
Ссылка в новой задаче