UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled

Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz
С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06).
Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
2026-07-06 14:07:50 +00:00
commit 01acfa3b40
1716 changed files with 613013 additions and 0 deletions
+333
View File
@@ -0,0 +1,333 @@
<!--
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<div id="sip-bridge" class="sip-bridge section">
<h2>{{ t('spreed', 'SIP configuration') }}</h2>
<NcNoteCard
v-if="!hasSignalingServers"
type="warning"
:text="t('spreed', 'SIP configuration is only possible with a High-performance backend.')" />
<template v-else>
<NcCheckboxRadioSwitch
v-model="dialOutEnabled"
type="switch"
:disabled="loading || !dialOutSupported">
{{ t('spreed', 'Enable SIP Dial-out option') }}
</NcCheckboxRadioSwitch>
<NcNoteCard
v-if="!dialOutSupported"
type="warning"
:text="t('spreed', 'Signaling server needs to be updated to supported SIP Dial-out feature.')" />
<template v-if="dialOutEnabled">
<NcCheckboxRadioSwitch
v-model="dialOutAnonymous"
type="switch"
:disabled="loading">
{{ t('spreed', 'Do not show SIP Dial-out caller number') }}
</NcCheckboxRadioSwitch>
<p class="settings-hint">
{{ t('spreed', 'Anonymous number should appear as "unknown" or "withheld number" to call recipient') }}
</p>
</template>
<template v-if="dialOutEnabled && !dialOutAnonymous">
<label for="sip-dialout-number" class="form__label additional-top-margin">
{{ t('spreed', 'Dial-out number') }}
</label>
<NcTextField
id="sip-dialout-number"
v-model="dialOutNumber"
class="form"
name="sip-dialout-number"
:disabled="loading"
placeholder="+49123456789"
labelOutside />
<p class="settings-hint additional-top-margin">
{{ t('spreed', 'E164 formatted number used as a fallback caller number for outgoing calls') }}
</p>
<label for="sip-dialout-prefix" class="form__label additional-top-margin">
{{ t('spreed', 'Dial-out prefix') }}
</label>
<NcTextField
id="sip-dialout-prefix"
v-model="dialOutPrefix"
class="form"
name="sip-dialout-prefix"
:disabled="loading"
labelOutside />
<p class="settings-hint additional-top-margin">
{{ t('spreed', 'Prefix to configured user number for outgoing calls (default is `+`)') }}
</p>
</template>
<NcSelect
v-model="sipGroups"
inputId="sip-group-enabled"
:inputLabel="t('spreed', 'Restrict SIP configuration')"
class="form form__select"
:options="groups"
:placeholder="t('spreed', 'Enable SIP configuration')"
:disabled="loading"
:multiple="true"
:searchable="true"
:tagWidth="60"
:loading="loadingGroups"
:showNoOptions="false"
keepOpen
trackBy="id"
label="displayname"
noWrap
@search="debounceSearchGroup" />
<p class="settings-hint settings-hint--after-select">
{{ t('spreed', 'Only users of the following groups can enable SIP in conversations they moderate') }}
</p>
<label for="sip-shared-secret" class="form__label additional-top-margin">
{{ t('spreed', 'Shared secret') }}
</label>
<NcPasswordField
id="sip-shared-secret"
v-model="sharedSecret"
class="form"
name="sip-shared-secret"
asText
:disabled="loading"
:placeholder="t('spreed', 'Shared secret')"
labelOutside />
<label for="dial-in-info" class="form__label additional-top-margin">
{{ t('spreed', 'Dial-in information') }}
</label>
<NcTextArea
id="dial-in-info"
v-model="dialInInfo"
name="message"
class="form form__textarea"
rows="4"
:disabled="loading"
:placeholder="t('spreed', 'Phone number (Country)')" />
<p class="settings-hint">
{{ t('spreed', 'This information is sent in invitation emails as well as displayed in the sidebar to all participants.') }}
</p>
<NcButton
variant="primary"
class="additional-top-margin"
:disabled="loading || !isEdited"
@click="saveSIPSettings">
{{ t('spreed', 'Save changes') }}
</NcButton>
</template>
</div>
</template>
<script>
import axios from '@nextcloud/axios'
import { showSuccess } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import { generateOcsUrl } from '@nextcloud/router'
import debounce from 'debounce'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
import NcSelect from '@nextcloud/vue/components/NcSelect'
import NcTextArea from '@nextcloud/vue/components/NcTextArea'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import { EventBus } from '../../services/EventBus.ts'
import { setSIPSettings } from '../../services/settingsService.ts'
import { getWelcomeMessage } from '../../services/signalingService.js'
export default {
name: 'SIPBridge',
components: {
NcCheckboxRadioSwitch,
NcButton,
NcNoteCard,
NcSelect,
NcTextArea,
NcTextField,
NcPasswordField,
},
props: {
hasSignalingServers: {
type: Boolean,
required: true,
},
},
data() {
return {
loading: false,
loadingGroups: false,
groups: [],
sipGroups: [],
dialInInfo: '',
sharedSecret: '',
dialOutEnabled: false,
dialOutAnonymous: false,
dialOutNumber: '',
dialOutPrefix: '',
currentSetup: {},
dialOutSupported: false,
debounceSearchGroup: () => {},
}
},
computed: {
isEdited() {
return this.currentSetup.sharedSecret !== this.sharedSecret
|| this.currentSetup.dialInInfo !== this.dialInInfo
|| this.currentSetup.dialOutEnabled !== this.dialOutEnabled
|| this.currentSetup.dialOutAnonymous !== this.dialOutAnonymous
|| this.currentSetup.dialOutNumber !== this.dialOutNumber
|| this.currentSetup.dialOutPrefix !== this.dialOutPrefix
|| this.currentSetup.sipGroups !== this.sipGroups.map((group) => group.id).join('_')
},
},
mounted() {
this.debounceSearchGroup = debounce(this.searchGroup, 500)
this.loading = true
this.groups = loadState('spreed', 'sip_bridge_groups').sort(function(a, b) {
return a.displayname.localeCompare(b.displayname)
})
this.sipGroups = this.groups
this.dialInInfo = loadState('spreed', 'sip_bridge_dialin_info')
this.dialOutEnabled = loadState('spreed', 'sip_bridge_dialout')
this.dialOutAnonymous = loadState('spreed', 'sip_bridge_dialout_anonymous')
this.dialOutNumber = loadState('spreed', 'sip_bridge_dialout_number')
this.dialOutPrefix = loadState('spreed', 'sip_bridge_dialout_prefix')
this.sharedSecret = loadState('spreed', 'sip_bridge_shared_secret')
this.debounceSearchGroup('')
this.loading = false
this.saveCurrentSetup()
this.isDialoutSupported()
},
beforeUnmount() {
this.debounceSearchGroup.clear?.()
},
methods: {
t,
async searchGroup(query) {
this.loadingGroups = true
try {
const response = await axios.get(generateOcsUrl('cloud/groups/details'), {
search: query,
limit: 20,
offset: 0,
})
this.groups = response.data.ocs.data.groups.sort(function(a, b) {
return a.displayname.localeCompare(b.displayname)
})
} catch (err) {
console.error('Could not fetch groups', err)
} finally {
this.loadingGroups = false
}
},
saveCurrentSetup() {
this.currentSetup = {
sharedSecret: this.sharedSecret,
dialInInfo: this.dialInInfo,
dialOutEnabled: this.dialOutEnabled,
dialOutAnonymous: this.dialOutAnonymous,
dialOutNumber: this.dialOutNumber,
dialOutPrefix: this.dialOutPrefix,
sipGroups: this.sipGroups.map((group) => group.id).join('_'),
}
EventBus.emit('sip-settings-updated', this.currentSetup)
},
async saveSIPSettings() {
this.loading = true
this.saveLabel = t('spreed', 'Saving …')
const sipGroups = this.sipGroups.map((group) => {
return group.id
})
await setSIPSettings({
sipGroups,
sharedSecret: this.sharedSecret,
dialInInfo: this.dialInInfo,
})
if (this.currentSetup.dialOutEnabled !== this.dialOutEnabled) {
await OCP.AppConfig.setValue('spreed', 'sip_dialout', this.dialOutEnabled ? 'yes' : 'no')
}
if (this.currentSetup.dialOutAnonymous !== this.dialOutAnonymous) {
await OCP.AppConfig.setValue('spreed', 'sip_bridge_dialout_anonymous', this.dialOutAnonymous)
}
if (this.currentSetup.dialOutNumber !== this.dialOutNumber) {
await OCP.AppConfig.setValue('spreed', 'sip_bridge_dialout_number', this.dialOutNumber)
}
if (this.currentSetup.dialOutPrefix !== this.dialOutPrefix) {
await OCP.AppConfig.setValue('spreed', 'sip_bridge_dialout_prefix', this.dialOutPrefix)
}
this.loading = false
this.saveCurrentSetup()
showSuccess(t('spreed', 'SIP configuration saved!'))
},
async isDialoutSupported() {
const servers = loadState('spreed', 'signaling_servers').servers
for (let index = 0; index < servers.length; index++) {
try {
const response = await getWelcomeMessage(index)
const data = response.data.ocs.data
// At least one server has the dialout feature
if (!data.warning || (data.warning === 'UPDATE_OPTIONAL' && !(data.features?.includes('dialout')))) {
this.dialOutSupported = true
break
}
} catch (exception) {
this.dialOutSupported = false
}
}
},
},
}
</script>
<style lang="scss" scoped>
.sip-bridge {
h3 {
margin-top: 24px;
font-weight: 600;
}
.form {
width: 300px;
&__textarea {
margin-bottom: 6px;
}
&__select {
margin-bottom: 12px;
}
}
}
.settings-hint--after-select {
margin-top: 0;
}
.additional-top-margin {
margin-top: 10px;
}
</style>