mirror of
https://github.com/nextcloud/server.git
synced 2025-12-18 15:56:14 -05:00
refactor(federatedfilesharing): migrate to Typescript and Vue 3
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
3efb1d80e9
commit
14f52a2303
22 changed files with 373 additions and 423 deletions
|
|
@ -16,12 +16,14 @@ use OCP\AppFramework\App;
|
|||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\Federation\ICloudFederationProviderManager;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
|
||||
public const APP_ID = 'federatedfilesharing';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct('federatedfilesharing');
|
||||
parent::__construct(self::APP_ID);
|
||||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
|
|
@ -33,14 +35,13 @@ class Application extends App implements IBootstrap {
|
|||
$context->injectFn(Closure::fromCallable([$this, 'registerCloudFederationProvider']));
|
||||
}
|
||||
|
||||
private function registerCloudFederationProvider(ICloudFederationProviderManager $manager,
|
||||
IAppContainer $appContainer): void {
|
||||
private function registerCloudFederationProvider(ICloudFederationProviderManager $manager): void {
|
||||
$fileResourceTypes = ['file', 'folder'];
|
||||
foreach ($fileResourceTypes as $type) {
|
||||
$manager->addCloudFederationProvider($type,
|
||||
'Federated Files Sharing',
|
||||
function () use ($appContainer): CloudFederationProviderFiles {
|
||||
return $appContainer->get(CloudFederationProviderFiles::class);
|
||||
function (): CloudFederationProviderFiles {
|
||||
return \OCP\Server::get(CloudFederationProviderFiles::class);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\FederatedFileSharing\Listeners;
|
||||
|
||||
use OCA\FederatedFileSharing\AppInfo\Application;
|
||||
use OCA\FederatedFileSharing\FederatedShareProvider;
|
||||
use OCA\Files\Event\LoadAdditionalScriptsEvent;
|
||||
use OCP\App\IAppManager;
|
||||
|
|
@ -35,7 +36,8 @@ class LoadAdditionalScriptsListener implements IEventListener {
|
|||
|
||||
if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled()) {
|
||||
$this->initialState->provideInitialState('notificationsEnabled', $this->appManager->isEnabledForUser('notifications'));
|
||||
Util::addInitScript('federatedfilesharing', 'external');
|
||||
Util::addStyle(Application::APP_ID, 'init-files');
|
||||
Util::addInitScript(Application::APP_ID, 'init-files');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
namespace OCA\FederatedFileSharing\Settings;
|
||||
|
||||
use OCA\FederatedFileSharing\AppInfo\Application;
|
||||
use OCA\FederatedFileSharing\FederatedShareProvider;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
|
|
@ -43,7 +44,9 @@ class Admin implements IDelegatedSettings {
|
|||
$this->initialState->provideInitialState('lookupServerUploadEnabled', $this->fedShareProvider->isLookupServerUploadEnabled());
|
||||
$this->initialState->provideInitialState('federatedTrustedShareAutoAccept', $this->fedShareProvider->isFederatedTrustedShareAutoAccept());
|
||||
|
||||
return new TemplateResponse('federatedfilesharing', 'settings-admin', [], '');
|
||||
\OCP\Util::addStyle(Application::APP_ID, 'settings-admin');
|
||||
\OCP\Util::addScript(Application::APP_ID, 'settings-admin');
|
||||
return new TemplateResponse(Application::APP_ID, 'settings-admin', renderAs: '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\FederatedFileSharing\Settings;
|
||||
|
||||
use OCA\FederatedFileSharing\AppInfo\Application;
|
||||
use OCA\FederatedFileSharing\FederatedShareProvider;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
|
|
@ -41,7 +42,9 @@ class Personal implements ISettings {
|
|||
$this->initialState->provideInitialState('cloudId', $cloudID);
|
||||
$this->initialState->provideInitialState('docUrlFederated', $this->urlGenerator->linkToDocs('user-sharing-federated'));
|
||||
|
||||
return new TemplateResponse('federatedfilesharing', 'settings-personal', [], TemplateResponse::RENDER_AS_BLANK);
|
||||
\OCP\Util::addStyle(Application::APP_ID, 'settings-personal');
|
||||
\OCP\Util::addScript(Application::APP_ID, 'settings-personal');
|
||||
return new TemplateResponse(Application::APP_ID, 'settings-personal', renderAs: TemplateResponse::RENDER_AS_BLANK);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,38 +2,184 @@
|
|||
- SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { showConfirmation, showError } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { reactive } from 'vue'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
|
||||
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
|
||||
import logger from '../services/logger.ts'
|
||||
|
||||
const sharingFederatedDocUrl = loadState<string>('federatedfilesharing', 'sharingFederatedDocUrl')
|
||||
|
||||
const internalState = new Proxy({
|
||||
outgoingServer2serverShareEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'outgoingServer2serverShareEnabled'),
|
||||
'outgoing_server2server_share_enabled',
|
||||
],
|
||||
incomingServer2serverShareEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'incomingServer2serverShareEnabled'),
|
||||
'incoming_server2server_share_enabled',
|
||||
],
|
||||
outgoingServer2serverGroupShareEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'outgoingServer2serverGroupShareEnabled'),
|
||||
'outgoing_server2server_group_share_enabled',
|
||||
],
|
||||
incomingServer2serverGroupShareEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'incomingServer2serverGroupShareEnabled'),
|
||||
'incoming_server2server_group_share_enabled',
|
||||
],
|
||||
federatedGroupSharingSupported: [
|
||||
loadState<boolean>('federatedfilesharing', 'federatedGroupSharingSupported'),
|
||||
'federated_group_sharing_supported',
|
||||
],
|
||||
federatedTrustedShareAutoAccept: [
|
||||
loadState<boolean>('federatedfilesharing', 'federatedTrustedShareAutoAccept'),
|
||||
'federatedTrustedShareAutoAccept',
|
||||
],
|
||||
lookupServerEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'lookupServerEnabled'),
|
||||
'lookupServerEnabled',
|
||||
],
|
||||
lookupServerUploadEnabled: [
|
||||
loadState<boolean>('federatedfilesharing', 'lookupServerUploadEnabled'),
|
||||
'lookupServerUploadEnabled',
|
||||
],
|
||||
}, {
|
||||
get(target, prop) {
|
||||
return target[prop]?.[0]
|
||||
},
|
||||
set(target, prop, value) {
|
||||
if (prop in target) {
|
||||
target[prop][0] = value
|
||||
updateAppConfig(target[prop][1], value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive<Record<string, boolean>>(internalState as never)
|
||||
|
||||
/**
|
||||
* Show confirmation dialog for enabling lookup server upload
|
||||
*
|
||||
* @param value - The new state
|
||||
*/
|
||||
async function showLookupServerUploadConfirmation(value: boolean) {
|
||||
// No confirmation needed for disabling
|
||||
if (value === false) {
|
||||
return state.lookupServerUploadEnabled = false
|
||||
}
|
||||
|
||||
await showConfirmation({
|
||||
name: t('federatedfilesharing', 'Confirm data upload to lookup server'),
|
||||
text: t('federatedfilesharing', 'When enabled, all account properties (e.g. email address) with scope visibility set to "published", will be automatically synced and transmitted to an external system and made available in a public, global address book.'),
|
||||
labelConfirm: t('federatedfilesharing', 'Enable data upload'),
|
||||
labelReject: t('federatedfilesharing', 'Disable upload'),
|
||||
severity: 'warning',
|
||||
}).then(() => {
|
||||
state.lookupServerUploadEnabled = true
|
||||
}).catch(() => {
|
||||
state.lookupServerUploadEnabled = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Show confirmation dialog for enabling lookup server
|
||||
*
|
||||
* @param value - The new state
|
||||
*/
|
||||
async function showLookupServerConfirmation(value: boolean) {
|
||||
// No confirmation needed for disabling
|
||||
if (value === false) {
|
||||
return state.lookupServerEnabled = false
|
||||
}
|
||||
|
||||
await showConfirmation({
|
||||
name: t('federatedfilesharing', 'Confirm querying lookup server'),
|
||||
text: t('federatedfilesharing', 'When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book.')
|
||||
+ t('federatedfilesharing', 'This is used to retrieve the federated cloud ID to make federated sharing easier.')
|
||||
+ t('federatedfilesharing', 'Moreover, email addresses of users might be sent to that system in order to verify them.'),
|
||||
labelConfirm: t('federatedfilesharing', 'Enable querying'),
|
||||
labelReject: t('federatedfilesharing', 'Disable querying'),
|
||||
severity: 'warning',
|
||||
}).then(() => {
|
||||
state.lookupServerEnabled = true
|
||||
}).catch(() => {
|
||||
state.lookupServerEnabled = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the app config
|
||||
*
|
||||
* @param key - The config key
|
||||
* @param value - The config value
|
||||
*/
|
||||
async function updateAppConfig(key: string, value: boolean) {
|
||||
await confirmPassword()
|
||||
|
||||
const url = generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/{appId}/{key}', {
|
||||
appId: 'files_sharing',
|
||||
key,
|
||||
})
|
||||
|
||||
const stringValue = value ? 'yes' : 'no'
|
||||
try {
|
||||
const { data } = await axios.post<OCSResponse>(url, {
|
||||
value: stringValue,
|
||||
})
|
||||
if (data.ocs.meta.status !== 'ok') {
|
||||
if (data.ocs.meta.message) {
|
||||
showError(data.ocs.meta.message)
|
||||
logger.error('Error updating federated files sharing config', { error: data.ocs })
|
||||
} else {
|
||||
throw new Error(`Failed to update federatedfilesharing config, ${data.ocs.meta.statuscode}`)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error updating federated files sharing config', { error })
|
||||
showError(t('federatedfilesharing', 'Unable to update federated files sharing config'))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcSettingsSection
|
||||
:name="t('federatedfilesharing', 'Federated Cloud Sharing')"
|
||||
:description="t('federatedfilesharing', 'Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing.')"
|
||||
:doc-url="sharingFederatedDocUrl">
|
||||
<NcCheckboxRadioSwitch
|
||||
v-model="outgoingServer2serverShareEnabled"
|
||||
type="switch"
|
||||
@update:modelValue="update('outgoing_server2server_share_enabled', outgoingServer2serverShareEnabled)">
|
||||
v-model="state.outgoingServer2serverShareEnabled"
|
||||
type="switch">
|
||||
{{ t('federatedfilesharing', 'Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcCheckboxRadioSwitch
|
||||
v-model="incomingServer2serverShareEnabled"
|
||||
type="switch"
|
||||
@update:modelValue="update('incoming_server2server_share_enabled', incomingServer2serverShareEnabled)">
|
||||
v-model="state.incomingServer2serverShareEnabled"
|
||||
type="switch">
|
||||
{{ t('federatedfilesharing', 'Allow people on this server to receive shares from other servers') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcCheckboxRadioSwitch
|
||||
v-if="federatedGroupSharingSupported"
|
||||
v-model="outgoingServer2serverGroupShareEnabled"
|
||||
type="switch"
|
||||
@update:modelValue="update('outgoing_server2server_group_share_enabled', outgoingServer2serverGroupShareEnabled)">
|
||||
v-if="state.federatedGroupSharingSupported"
|
||||
v-model="state.outgoingServer2serverGroupShareEnabled"
|
||||
type="switch">
|
||||
{{ t('federatedfilesharing', 'Allow people on this server to send shares to groups on other servers') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcCheckboxRadioSwitch
|
||||
v-if="federatedGroupSharingSupported"
|
||||
v-model="incomingServer2serverGroupShareEnabled"
|
||||
type="switch"
|
||||
@update:modelValue="update('incoming_server2server_group_share_enabled', incomingServer2serverGroupShareEnabled)">
|
||||
v-if="state.federatedGroupSharingSupported"
|
||||
v-model="state.incomingServer2serverGroupShareEnabled"
|
||||
type="switch">
|
||||
{{ t('federatedfilesharing', 'Allow people on this server to receive group shares from other servers') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
|
|
@ -42,17 +188,17 @@
|
|||
|
||||
<NcCheckboxRadioSwitch
|
||||
type="switch"
|
||||
:model-value="lookupServerEnabled"
|
||||
:model-value="state.lookupServerEnabled"
|
||||
disabled
|
||||
@update:modelValue="showLookupServerConfirmation">
|
||||
@update:model-value="showLookupServerConfirmation">
|
||||
{{ t('federatedfilesharing', 'Search global and public address book for people') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcCheckboxRadioSwitch
|
||||
type="switch"
|
||||
:model-value="lookupServerUploadEnabled"
|
||||
:model-value="state.lookupServerUploadEnabled"
|
||||
disabled
|
||||
@update:modelValue="showLookupServerUploadConfirmation">
|
||||
@update:model-value="showLookupServerUploadConfirmation">
|
||||
{{ t('federatedfilesharing', 'Allow people to publish their data to a global and public address book') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</fieldset>
|
||||
|
|
@ -63,147 +209,14 @@
|
|||
{{ t('federatedfilesharing', 'Trusted federation') }}
|
||||
</h3>
|
||||
<NcCheckboxRadioSwitch
|
||||
v-model="federatedTrustedShareAutoAccept"
|
||||
type="switch"
|
||||
@update:modelValue="update('federatedTrustedShareAutoAccept', federatedTrustedShareAutoAccept)">
|
||||
v-model="state.federatedTrustedShareAutoAccept"
|
||||
type="switch">
|
||||
{{ t('federatedfilesharing', 'Automatically accept shares from trusted federated accounts and groups by default') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</div>
|
||||
</NcSettingsSection>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '@nextcloud/axios'
|
||||
import { DialogBuilder, showError } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { confirmPassword } from '@nextcloud/password-confirmation'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
|
||||
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
|
||||
import logger from '../services/logger.ts'
|
||||
|
||||
export default {
|
||||
name: 'AdminSettings',
|
||||
|
||||
components: {
|
||||
NcCheckboxRadioSwitch,
|
||||
NcSettingsSection,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
outgoingServer2serverShareEnabled: loadState('federatedfilesharing', 'outgoingServer2serverShareEnabled'),
|
||||
incomingServer2serverShareEnabled: loadState('federatedfilesharing', 'incomingServer2serverShareEnabled'),
|
||||
outgoingServer2serverGroupShareEnabled: loadState('federatedfilesharing', 'outgoingServer2serverGroupShareEnabled'),
|
||||
incomingServer2serverGroupShareEnabled: loadState('federatedfilesharing', 'incomingServer2serverGroupShareEnabled'),
|
||||
federatedGroupSharingSupported: loadState('federatedfilesharing', 'federatedGroupSharingSupported'),
|
||||
lookupServerEnabled: loadState('federatedfilesharing', 'lookupServerEnabled'),
|
||||
lookupServerUploadEnabled: loadState('federatedfilesharing', 'lookupServerUploadEnabled'),
|
||||
federatedTrustedShareAutoAccept: loadState('federatedfilesharing', 'federatedTrustedShareAutoAccept'),
|
||||
internalOnly: loadState('federatedfilesharing', 'internalOnly'),
|
||||
sharingFederatedDocUrl: loadState('federatedfilesharing', 'sharingFederatedDocUrl'),
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setLookupServerUploadEnabled(state) {
|
||||
if (state === this.lookupServerUploadEnabled) {
|
||||
return
|
||||
}
|
||||
this.lookupServerUploadEnabled = state
|
||||
this.update('lookupServerUploadEnabled', state)
|
||||
},
|
||||
|
||||
async showLookupServerUploadConfirmation(state) {
|
||||
// No confirmation needed for disabling
|
||||
if (state === false) {
|
||||
return this.setLookupServerUploadEnabled(false)
|
||||
}
|
||||
|
||||
const dialog = new DialogBuilder(t('federatedfilesharing', 'Confirm data upload to lookup server'))
|
||||
await dialog
|
||||
.setSeverity('warning')
|
||||
.setText(t('federatedfilesharing', 'When enabled, all account properties (e.g. email address) with scope visibility set to "published", will be automatically synced and transmitted to an external system and made available in a public, global address book.'))
|
||||
.addButton({
|
||||
callback: () => this.setLookupServerUploadEnabled(false),
|
||||
label: t('federatedfilesharing', 'Disable upload'),
|
||||
})
|
||||
.addButton({
|
||||
callback: () => this.setLookupServerUploadEnabled(true),
|
||||
label: t('federatedfilesharing', 'Enable data upload'),
|
||||
type: 'error',
|
||||
})
|
||||
.build()
|
||||
.show()
|
||||
},
|
||||
|
||||
setLookupServerEnabled(state) {
|
||||
if (state === this.lookupServerEnabled) {
|
||||
return
|
||||
}
|
||||
this.lookupServerEnabled = state
|
||||
this.update('lookupServerEnabled', state)
|
||||
},
|
||||
|
||||
async showLookupServerConfirmation(state) {
|
||||
// No confirmation needed for disabling
|
||||
if (state === false) {
|
||||
return this.setLookupServerEnabled(false)
|
||||
}
|
||||
|
||||
const dialog = new DialogBuilder(t('federatedfilesharing', 'Confirm querying lookup server'))
|
||||
await dialog
|
||||
.setSeverity('warning')
|
||||
.setText(t('federatedfilesharing', 'When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book.')
|
||||
+ t('federatedfilesharing', 'This is used to retrieve the federated cloud ID to make federated sharing easier.')
|
||||
+ t('federatedfilesharing', 'Moreover, email addresses of users might be sent to that system in order to verify them.'))
|
||||
.addButton({
|
||||
callback: () => this.setLookupServerEnabled(false),
|
||||
label: t('federatedfilesharing', 'Disable querying'),
|
||||
})
|
||||
.addButton({
|
||||
callback: () => this.setLookupServerEnabled(true),
|
||||
label: t('federatedfilesharing', 'Enable querying'),
|
||||
type: 'error',
|
||||
})
|
||||
.build()
|
||||
.show()
|
||||
},
|
||||
|
||||
async update(key, value) {
|
||||
await confirmPassword()
|
||||
|
||||
const url = generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/{appId}/{key}', {
|
||||
appId: 'files_sharing',
|
||||
key,
|
||||
})
|
||||
|
||||
const stringValue = value ? 'yes' : 'no'
|
||||
try {
|
||||
const { data } = await axios.post(url, {
|
||||
value: stringValue,
|
||||
})
|
||||
this.handleResponse({
|
||||
status: data.ocs?.meta?.status,
|
||||
})
|
||||
} catch (e) {
|
||||
this.handleResponse({
|
||||
errorMessage: t('federatedfilesharing', 'Unable to update federated files sharing config'),
|
||||
error: e,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async handleResponse({ status, errorMessage, error }) {
|
||||
if (status !== 'ok') {
|
||||
showError(errorMessage)
|
||||
logger.error(errorMessage, { error })
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settings-subsection {
|
||||
margin-top: 20px;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,75 @@
|
|||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { showSuccess } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { imagePath } from '@nextcloud/router'
|
||||
import { computed, ref } from 'vue'
|
||||
import NcButton from '@nextcloud/vue/components/NcButton'
|
||||
import NcInputField from '@nextcloud/vue/components/NcInputField'
|
||||
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
|
||||
import IconCheck from 'vue-material-design-icons/Check.vue'
|
||||
import IconClipboard from 'vue-material-design-icons/ContentCopy.vue'
|
||||
import IconWeb from 'vue-material-design-icons/Web.vue'
|
||||
|
||||
const productName = window.OC.theme.productName
|
||||
const color = loadState<string>('federatedfilesharing', 'color')
|
||||
const textColor = loadState<string>('federatedfilesharing', 'textColor')
|
||||
const cloudId = loadState<string>('federatedfilesharing', 'cloudId')
|
||||
const docUrlFederated = loadState<string>('federatedfilesharing', 'docUrlFederated')
|
||||
const logoPath = loadState<string>('federatedfilesharing', 'logoPath')
|
||||
const reference = loadState<string>('federatedfilesharing', 'reference')
|
||||
const urlFacebookIcon = imagePath('core', 'facebook')
|
||||
const urlMastodonIcon = imagePath('core', 'mastodon')
|
||||
const urlBlueSkyIcon = imagePath('core', 'bluesky')
|
||||
const messageWithURL = t('federatedfilesharing', 'Share with me through my #Nextcloud Federated Cloud ID, see {url}', { url: reference })
|
||||
const messageWithoutURL = t('federatedfilesharing', 'Share with me through my #Nextcloud Federated Cloud ID')
|
||||
const shareMastodonUrl = `https://mastodon.social/?text=${encodeURIComponent(messageWithoutURL)}&url=${encodeURIComponent(reference)}`
|
||||
const shareFacebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(reference)}`
|
||||
const shareBlueSkyUrl = `https://bsky.app/intent/compose?text=${encodeURIComponent(messageWithURL)}`
|
||||
const logoPathAbsolute = new URL(logoPath, location.origin)
|
||||
|
||||
const showHtml = ref(false)
|
||||
const isCopied = ref(false)
|
||||
|
||||
const backgroundStyle = computed(() => `
|
||||
padding:10px;
|
||||
background-color:${color};
|
||||
color:${textColor};
|
||||
border-radius:3px;
|
||||
padding-inline-start:4px;`)
|
||||
|
||||
const linkStyle = `background-image:url(${logoPathAbsolute});width:50px;height:30px;position:relative;top:8px;background-size:contain;display:inline-block;background-repeat:no-repeat; background-position: center center;`
|
||||
const htmlCode = computed(() => `<a target="_blank" rel="noreferrer noopener" href="${reference}" style="${backgroundStyle.value}">
|
||||
<span style="${linkStyle}"></span>
|
||||
${t('federatedfilesharing', 'Share with me via Nextcloud')}
|
||||
</a>`)
|
||||
|
||||
const copyLinkTooltip = computed(() => isCopied.value
|
||||
? t('federatedfilesharing', 'Cloud ID copied')
|
||||
: t('federatedfilesharing', 'Copy'))
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
async function copyCloudId(): Promise<void> {
|
||||
try {
|
||||
await navigator.clipboard.writeText(cloudId)
|
||||
showSuccess(t('federatedfilesharing', 'Cloud ID copied'))
|
||||
} catch {
|
||||
// no secure context or really old browser - need a fallback
|
||||
window.prompt(t('federatedfilesharing', 'Clipboard not available. Please copy the cloud ID manually.'), cloudId)
|
||||
}
|
||||
isCopied.value = true
|
||||
showSuccess(t('federatedfilesharing', 'Copied!'))
|
||||
setTimeout(() => {
|
||||
isCopied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcSettingsSection
|
||||
:name="t('federatedfilesharing', 'Federated Cloud')"
|
||||
|
|
@ -25,32 +94,24 @@
|
|||
|
||||
<p class="social-button">
|
||||
{{ t('federatedfilesharing', 'Share it so your friends can share files with you:') }}<br>
|
||||
<NcButton :href="shareBlueSkyUrl">
|
||||
{{ t('federatedfilesharing', 'Bluesky') }}
|
||||
<template #icon>
|
||||
<img class="social-button__icon" :src="urlBlueSkyIcon">
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton :href="shareFacebookUrl">
|
||||
{{ t('federatedfilesharing', 'Facebook') }}
|
||||
<template #icon>
|
||||
<img class="social-button__icon social-button__icon--bright" :src="urlFacebookIcon">
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton
|
||||
:aria-label="t('federatedfilesharing', 'X (formerly Twitter)')"
|
||||
:href="shareXUrl">
|
||||
{{ t('federatedfilesharing', 'formerly Twitter') }}
|
||||
<template #icon>
|
||||
<img class="social-button__icon" :src="urlXIcon">
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton :href="shareMastodonUrl">
|
||||
{{ t('federatedfilesharing', 'Mastodon') }}
|
||||
<template #icon>
|
||||
<img class="social-button__icon" :src="urlMastodonIcon">
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton :href="shareBlueSkyUrl">
|
||||
{{ t('federatedfilesharing', 'Bluesky') }}
|
||||
<template #icon>
|
||||
<img class="social-button__icon" :src="urlBlueSkyIcon">
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton
|
||||
class="social-button__website-button"
|
||||
@click="showHtml = !showHtml">
|
||||
|
|
@ -82,126 +143,6 @@
|
|||
</NcSettingsSection>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { showSuccess } from '@nextcloud/dialogs'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { imagePath } from '@nextcloud/router'
|
||||
import NcButton from '@nextcloud/vue/components/NcButton'
|
||||
import NcInputField from '@nextcloud/vue/components/NcInputField'
|
||||
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
|
||||
import IconCheck from 'vue-material-design-icons/Check.vue'
|
||||
import IconClipboard from 'vue-material-design-icons/ContentCopy.vue'
|
||||
import IconWeb from 'vue-material-design-icons/Web.vue'
|
||||
|
||||
export default {
|
||||
name: 'PersonalSettings',
|
||||
components: {
|
||||
NcButton,
|
||||
NcInputField,
|
||||
NcSettingsSection,
|
||||
IconCheck,
|
||||
IconClipboard,
|
||||
IconWeb,
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
t,
|
||||
|
||||
productName: window.OC.theme.productName,
|
||||
cloudId: loadState<string>('federatedfilesharing', 'cloudId'),
|
||||
reference: loadState<string>('federatedfilesharing', 'reference'),
|
||||
urlFacebookIcon: imagePath('core', 'facebook'),
|
||||
urlMastodonIcon: imagePath('core', 'mastodon'),
|
||||
urlBlueSkyIcon: imagePath('core', 'bluesky'),
|
||||
urlXIcon: imagePath('core', 'x'),
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
color: loadState('federatedfilesharing', 'color'),
|
||||
textColor: loadState('federatedfilesharing', 'textColor'),
|
||||
logoPath: loadState('federatedfilesharing', 'logoPath'),
|
||||
docUrlFederated: loadState('federatedfilesharing', 'docUrlFederated'),
|
||||
showHtml: false,
|
||||
isCopied: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
messageWithURL() {
|
||||
return t('federatedfilesharing', 'Share with me through my #Nextcloud Federated Cloud ID, see {url}', { url: this.reference })
|
||||
},
|
||||
|
||||
messageWithoutURL() {
|
||||
return t('federatedfilesharing', 'Share with me through my #Nextcloud Federated Cloud ID')
|
||||
},
|
||||
|
||||
shareMastodonUrl() {
|
||||
return `https://mastodon.social/?text=${encodeURIComponent(this.messageWithoutURL)}&url=${encodeURIComponent(this.reference)}`
|
||||
},
|
||||
|
||||
shareXUrl() {
|
||||
return `https://x.com/intent/tweet?text=${encodeURIComponent(this.messageWithURL)}`
|
||||
},
|
||||
|
||||
shareFacebookUrl() {
|
||||
return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(this.reference)}`
|
||||
},
|
||||
|
||||
shareBlueSkyUrl() {
|
||||
return `https://bsky.app/intent/compose?text=${encodeURIComponent(this.messageWithURL)}`
|
||||
},
|
||||
|
||||
logoPathAbsolute() {
|
||||
return window.location.protocol + '//' + window.location.host + this.logoPath
|
||||
},
|
||||
|
||||
backgroundStyle() {
|
||||
return `padding:10px;background-color:${this.color};color:${this.textColor};border-radius:3px;padding-inline-start:4px;`
|
||||
},
|
||||
|
||||
linkStyle() {
|
||||
return `background-image:url(${this.logoPathAbsolute});width:50px;height:30px;position:relative;top:8px;background-size:contain;display:inline-block;background-repeat:no-repeat; background-position: center center;`
|
||||
},
|
||||
|
||||
htmlCode() {
|
||||
return `<a target="_blank" rel="noreferrer noopener" href="${this.reference}" style="${this.backgroundStyle}">
|
||||
<span style="${this.linkStyle}"></span>
|
||||
${t('federatedfilesharing', 'Share with me via Nextcloud')}
|
||||
</a>`
|
||||
},
|
||||
|
||||
copyLinkTooltip() {
|
||||
return this.isCopied ? t('federatedfilesharing', 'Cloud ID copied') : t('federatedfilesharing', 'Copy')
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async copyCloudId(): Promise<void> {
|
||||
try {
|
||||
await navigator.clipboard.writeText(this.cloudId)
|
||||
showSuccess(t('federatedfilesharing', 'Cloud ID copied'))
|
||||
} catch {
|
||||
// no secure context or really old browser - need a fallback
|
||||
window.prompt(t('federatedfilesharing', 'Clipboard not available. Please copy the cloud ID manually.'), this.reference)
|
||||
}
|
||||
this.isCopied = true
|
||||
showSuccess(t('federatedfilesharing', 'Copied!'))
|
||||
setTimeout(() => {
|
||||
this.isCopied = false
|
||||
}, 2000)
|
||||
},
|
||||
|
||||
goTo(url: string): void {
|
||||
window.location.href = url
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.social-button {
|
||||
margin-top: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -3,31 +3,36 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { cleanup, fireEvent, render } from '@testing-library/vue'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { VueWrapper } from '@vue/test-utils'
|
||||
|
||||
import { findByLabelText, findByRole, fireEvent, getByLabelText, getByRole } from '@testing-library/vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import RemoteShareDialog from './RemoteShareDialog.vue'
|
||||
|
||||
describe('RemoteShareDialog', () => {
|
||||
beforeEach(cleanup)
|
||||
let component: VueWrapper
|
||||
|
||||
afterEach(() => {
|
||||
component?.unmount()
|
||||
})
|
||||
|
||||
it('can be mounted', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
component = mount(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
attachTo: 'body',
|
||||
})
|
||||
|
||||
await expect(component.findByRole('dialog', { name: 'Remote share' })).resolves.not.toThrow()
|
||||
expect(component.getByRole('dialog').innerText).toContain(/my-photos from user123@nextcloud.local/)
|
||||
await expect(component.findByRole('button', { name: 'Cancel' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: /Add remote share/ })).resolves.not.toThrow()
|
||||
await expect(findByRole(document.body, 'dialog', { name: 'Remote share' })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('does not show password input if not enabled', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
component = mount(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
|
|
@ -36,15 +41,15 @@ describe('RemoteShareDialog', () => {
|
|||
},
|
||||
})
|
||||
|
||||
await expect(component.findByLabelText('Remote share password')).rejects.toThrow()
|
||||
await expect(findByLabelText(component.element, 'Remote share password')).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('emits true when accepted', () => {
|
||||
it('emits true when accepted', async () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
component = mount(RemoteShareDialog, {
|
||||
attrs: {
|
||||
onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'user123',
|
||||
|
|
@ -54,12 +59,13 @@ describe('RemoteShareDialog', () => {
|
|||
},
|
||||
})
|
||||
|
||||
component.getByRole('button', { name: 'Cancel' }).click()
|
||||
const button = getByRole(component.element, 'button', { name: 'Cancel' })
|
||||
await fireEvent.click(button)
|
||||
expect(onClose).toHaveBeenCalledWith(false)
|
||||
})
|
||||
|
||||
it('show password input if needed', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
component = mount(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
|
|
@ -68,15 +74,15 @@ describe('RemoteShareDialog', () => {
|
|||
},
|
||||
})
|
||||
|
||||
await expect(component.findByLabelText('Remote share password')).resolves.not.toThrow()
|
||||
await expect(findByLabelText(component.element, 'Remote share password')).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('emits the submitted password', async () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
component = mount(RemoteShareDialog, {
|
||||
attrs: {
|
||||
onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'admin',
|
||||
|
|
@ -86,18 +92,19 @@ describe('RemoteShareDialog', () => {
|
|||
},
|
||||
})
|
||||
|
||||
const input = component.getByLabelText('Remote share password')
|
||||
const input = getByLabelText(component.element, 'Remote share password')
|
||||
await fireEvent.update(input, 'my password')
|
||||
component.getByRole('button', { name: 'Add remote share' }).click()
|
||||
const button = getByRole(component.element, 'button', { name: 'Add remote share' })
|
||||
await fireEvent.click(button)
|
||||
expect(onClose).toHaveBeenCalledWith(true, 'my password')
|
||||
})
|
||||
|
||||
it('emits no password if cancelled', async () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
component = mount(RemoteShareDialog, {
|
||||
attrs: {
|
||||
onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'admin',
|
||||
|
|
@ -107,9 +114,10 @@ describe('RemoteShareDialog', () => {
|
|||
},
|
||||
})
|
||||
|
||||
const input = component.getByLabelText('Remote share password')
|
||||
const input = getByLabelText(component.element, 'Remote share password')
|
||||
await fireEvent.update(input, 'my password')
|
||||
component.getByRole('button', { name: 'Cancel' }).click()
|
||||
const button = getByRole(component.element, 'button', { name: 'Cancel' })
|
||||
await fireEvent.click(button)
|
||||
expect(onClose).toHaveBeenCalledWith(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,15 +20,17 @@ const props = defineProps<{
|
|||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close', state: boolean, password?: string): void
|
||||
close: [state: boolean, password?: string]
|
||||
}>()
|
||||
|
||||
const password = ref('')
|
||||
|
||||
type INcDialogButtons = InstanceType<typeof NcDialog>['$props']['buttons']
|
||||
|
||||
/**
|
||||
* The dialog buttons
|
||||
*/
|
||||
const buttons = computed(() => [
|
||||
const buttons = computed<INcDialogButtons>(() => [
|
||||
{
|
||||
label: t('federatedfilesharing', 'Cancel'),
|
||||
callback: () => emit('close', false),
|
||||
|
|
@ -54,16 +56,13 @@ const buttons = computed(() => [
|
|||
<NcPasswordField
|
||||
v-if="passwordRequired"
|
||||
v-model="password"
|
||||
class="remote-share-dialog__password"
|
||||
:class="$style.remoteShareDialog__password"
|
||||
:label="t('federatedfilesharing', 'Remote share password')" />
|
||||
</NcDialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.remote-share-dialog {
|
||||
|
||||
&__password {
|
||||
margin-block: 1em .5em;
|
||||
}
|
||||
<style module>
|
||||
.remoteShareDialog__password {
|
||||
margin-block: 1em .5em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -13,33 +13,6 @@ import { generateUrl } from '@nextcloud/router'
|
|||
import { showRemoteShareDialog } from './services/dialogService.ts'
|
||||
import logger from './services/logger.ts'
|
||||
|
||||
window.OCA.Sharing = window.OCA.Sharing ?? {}
|
||||
|
||||
/**
|
||||
* Shows "add external share" dialog.
|
||||
*
|
||||
* @param {object} share the share
|
||||
* @param {string} share.remote remote server URL
|
||||
* @param {string} share.owner owner name
|
||||
* @param {string} share.name name of the shared folder
|
||||
* @param {string} share.token authentication token
|
||||
* @param {boolean} passwordProtected true if the share is password protected
|
||||
* @param {Function} callback the callback
|
||||
*/
|
||||
window.OCA.Sharing.showAddExternalDialog = function(share, passwordProtected, callback) {
|
||||
const owner = share.ownerDisplayName || share.owner
|
||||
const name = share.name
|
||||
|
||||
// Clean up the remote URL for display
|
||||
const remote = share.remote
|
||||
.replace(/^https?:\/\//, '') // remove http:// or https://
|
||||
.replace(/\/$/, '') // remove trailing slash
|
||||
|
||||
showRemoteShareDialog(name, owner, remote, passwordProtected)
|
||||
.then((password) => callback(true, { ...share, password }))
|
||||
.catch(() => callback(false, share))
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
processIncomingShareFromUrl()
|
||||
|
||||
|
|
@ -118,7 +91,7 @@ function processIncomingShareFromUrl() {
|
|||
// clear hash, it is unlikely that it contain any extra parameters
|
||||
location.hash = ''
|
||||
params.passwordProtected = parseInt(params.protected, 10) === 1
|
||||
window.OCA.Sharing.showAddExternalDialog(
|
||||
showAddExternalDialog(
|
||||
params,
|
||||
params.passwordProtected,
|
||||
callbackAddShare,
|
||||
|
|
@ -133,7 +106,7 @@ async function processSharesToConfirm() {
|
|||
// check for new server-to-server shares which need to be approved
|
||||
const { data: shares } = await axios.get(generateUrl('/apps/files_sharing/api/externalShares'))
|
||||
for (let index = 0; index < shares.length; ++index) {
|
||||
window.OCA.Sharing.showAddExternalDialog(
|
||||
showAddExternalDialog(
|
||||
shares[index],
|
||||
false,
|
||||
function(result, share) {
|
||||
|
|
@ -149,3 +122,28 @@ async function processSharesToConfirm() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows "add external share" dialog.
|
||||
*
|
||||
* @param {object} share the share
|
||||
* @param {string} share.remote remote server URL
|
||||
* @param {string} share.owner owner name
|
||||
* @param {string} share.name name of the shared folder
|
||||
* @param {string} share.token authentication token
|
||||
* @param {boolean} passwordProtected true if the share is password protected
|
||||
* @param {Function} callback the callback
|
||||
*/
|
||||
function showAddExternalDialog(share, passwordProtected, callback) {
|
||||
const owner = share.ownerDisplayName || share.owner
|
||||
const name = share.name
|
||||
|
||||
// Clean up the remote URL for display
|
||||
const remote = share.remote
|
||||
.replace(/^https?:\/\//, '') // remove http:// or https://
|
||||
.replace(/\/$/, '') // remove trailing slash
|
||||
|
||||
showRemoteShareDialog(name, owner, remote, passwordProtected)
|
||||
.then((password) => callback(true, { ...share, password }))
|
||||
.catch(() => callback(false, share))
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
import PersonalSettings from './components/PersonalSettings.vue'
|
||||
|
||||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
t,
|
||||
},
|
||||
})
|
||||
|
||||
const PersonalSettingsView = Vue.extend(PersonalSettings)
|
||||
new PersonalSettingsView().$mount('#vue-personal-federated')
|
||||
|
|
@ -47,7 +47,7 @@ describe('federatedfilesharing: dialog service', () => {
|
|||
}
|
||||
}
|
||||
|
||||
expect(await promise).toBe('')
|
||||
await expect(promise).resolves.toBe('')
|
||||
})
|
||||
|
||||
it('rejects if cancelled', async () => {
|
||||
|
|
@ -60,6 +60,6 @@ describe('federatedfilesharing: dialog service', () => {
|
|||
}
|
||||
}
|
||||
|
||||
expect(async () => await promise).rejects.toThrow()
|
||||
await expect(promise).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,23 +14,24 @@ import RemoteShareDialog from '../components/RemoteShareDialog.vue'
|
|||
* @param remote The remote address
|
||||
* @param passwordRequired True if the share is password protected
|
||||
*/
|
||||
export function showRemoteShareDialog(
|
||||
export async function showRemoteShareDialog(
|
||||
name: string,
|
||||
owner: string,
|
||||
remote: string,
|
||||
passwordRequired = false,
|
||||
): Promise<string | void> {
|
||||
const { promise, reject, resolve } = Promise.withResolvers<string | void>()
|
||||
|
||||
spawnDialog(RemoteShareDialog, { name, owner, remote, passwordRequired }, (status, password) => {
|
||||
if (passwordRequired && status) {
|
||||
resolve(password as string)
|
||||
} else if (status) {
|
||||
resolve(undefined)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
const [status, password] = await spawnDialog(RemoteShareDialog, {
|
||||
name,
|
||||
owner,
|
||||
remote,
|
||||
passwordRequired,
|
||||
})
|
||||
|
||||
return promise
|
||||
if (passwordRequired && status) {
|
||||
return password as string
|
||||
} else if (status) {
|
||||
return
|
||||
} else {
|
||||
throw new Error('Dialog was cancelled')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,23 +3,15 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
import { createApp } from 'vue'
|
||||
import AdminSettings from './components/AdminSettings.vue'
|
||||
|
||||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
t,
|
||||
},
|
||||
})
|
||||
import 'vite/modulepreload-polyfill'
|
||||
|
||||
const internalOnly = loadState('federatedfilesharing', 'internalOnly', false)
|
||||
|
||||
if (!internalOnly) {
|
||||
const AdminSettingsView = Vue.extend(AdminSettings)
|
||||
new AdminSettingsView().$mount('#vue-admin-federated')
|
||||
const app = createApp(AdminSettings)
|
||||
app.mount('#vue-admin-federated')
|
||||
}
|
||||
12
apps/federatedfilesharing/src/settings-personal.ts
Normal file
12
apps/federatedfilesharing/src/settings-personal.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import PersonalSettings from './components/PersonalSettings.vue'
|
||||
|
||||
import 'vite/modulepreload-polyfill'
|
||||
|
||||
const app = createApp(PersonalSettings)
|
||||
app.mount('#vue-personal-federated')
|
||||
|
|
@ -3,8 +3,6 @@
|
|||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
\OCP\Util::addScript('federatedfilesharing', 'vue-settings-admin');
|
||||
?>
|
||||
|
||||
<div id="vue-admin-federated"></div>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
\OCP\Util::addScript('federatedfilesharing', 'vue-settings-personal');
|
||||
?>
|
||||
|
||||
<div id="vue-personal-federated"></div>
|
||||
|
|
|
|||
|
|
@ -57,11 +57,6 @@ module.exports = {
|
|||
oauth2: {
|
||||
oauth2: path.join(__dirname, 'apps/oauth2/src', 'main.js'),
|
||||
},
|
||||
federatedfilesharing: {
|
||||
external: path.join(__dirname, 'apps/federatedfilesharing/src', 'external.js'),
|
||||
'vue-settings-admin': path.join(__dirname, 'apps/federatedfilesharing/src', 'main-admin.js'),
|
||||
'vue-settings-personal': path.join(__dirname, 'apps/federatedfilesharing/src', 'main-personal.js'),
|
||||
},
|
||||
profile: {
|
||||
main: path.join(__dirname, 'apps/profile/src', 'main.ts'),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ const modules = {
|
|||
'settings-admin-example-content': resolve(import.meta.dirname, 'apps/dav/src', 'settings-admin-example-content.ts'),
|
||||
'settings-personal-availability': resolve(import.meta.dirname, 'apps/dav/src', 'settings-personal-availability.ts'),
|
||||
},
|
||||
federatedfilesharing: {
|
||||
'init-files': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'init-files.js'),
|
||||
'settings-admin': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-admin.ts'),
|
||||
'settings-personal': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-personal.ts'),
|
||||
},
|
||||
files_reminders: {
|
||||
init: resolve(import.meta.dirname, 'apps/files_reminders/src', 'files-init.ts'),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1209,11 +1209,6 @@
|
|||
)]]></code>
|
||||
</DeprecatedMethod>
|
||||
</file>
|
||||
<file src="apps/federatedfilesharing/lib/AppInfo/Application.php">
|
||||
<DeprecatedInterface>
|
||||
<code><![CDATA[IAppContainer]]></code>
|
||||
</DeprecatedInterface>
|
||||
</file>
|
||||
<file src="apps/federatedfilesharing/lib/Controller/RequestHandlerController.php">
|
||||
<InvalidArgument>
|
||||
<code><![CDATA[$id]]></code>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"include": ["./apps/**/*.ts", "./apps/**/*.vue", "./core/**/*.ts", "./core/**/*.vue", "./*.d.ts"],
|
||||
"exclude": ["./**/*.cy.ts"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"lib": ["DOM", "DOM.Iterable", "DOM.AsyncIterable", "ESNext"],
|
||||
"outDir": "./dist/",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
|
|
|
|||
|
|
@ -3,5 +3,11 @@
|
|||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
// stub - for the moment see build/frontend/vitest.config.ts
|
||||
export default {}
|
||||
export default defineConfig({
|
||||
test: {
|
||||
projects: ['build/frontend*'],
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue