feat(xo-web/setting/remotes): ability to change the type of a remote (#3207)
This commit is contained in:
parent
0ed1df3af6
commit
b95fc86667
@ -8,6 +8,7 @@
|
|||||||
- Search syntax support wildcard (`*`) and regular expressions [#3190](https://github.com/vatesfr/xen-orchestra/issues/3190) (PRs [#3198](https://github.com/vatesfr/xen-orchestra/pull/3198) & [#3199](https://github.com/vatesfr/xen-orchestra/pull/3199))
|
- Search syntax support wildcard (`*`) and regular expressions [#3190](https://github.com/vatesfr/xen-orchestra/issues/3190) (PRs [#3198](https://github.com/vatesfr/xen-orchestra/pull/3198) & [#3199](https://github.com/vatesfr/xen-orchestra/pull/3199))
|
||||||
- Import VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3216](https://github.com/vatesfr/xen-orchestra/pull/3216))
|
- Import VDI content [#2432](https://github.com/vatesfr/xen-orchestra/issues/2432) (PR [#3216](https://github.com/vatesfr/xen-orchestra/pull/3216))
|
||||||
- [Backup NG form] Ability to edit a schedule's name [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) [#3071](https://github.com/vatesfr/xen-orchestra/issues/3071) (PR [#3143](https://github.com/vatesfr/xen-orchestra/pull/3143))
|
- [Backup NG form] Ability to edit a schedule's name [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) [#3071](https://github.com/vatesfr/xen-orchestra/issues/3071) (PR [#3143](https://github.com/vatesfr/xen-orchestra/pull/3143))
|
||||||
|
- [Remotes] Ability to change the type of a remote [#2423](https://github.com/vatesfr/xen-orchestra/issues/2423) (PR [#3207](https://github.com/vatesfr/xen-orchestra/pull/3207))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
15
packages/xo-web/src/common/freactal-utils.js
Normal file
15
packages/xo-web/src/common/freactal-utils.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// TODO: remove these functions once the PR: https://github.com/julien-f/freactal/pull/5 has been merged
|
||||||
|
// It only supports native inputs
|
||||||
|
export const linkState = (_, { target }) => state => ({
|
||||||
|
...state,
|
||||||
|
[target.name]:
|
||||||
|
target.nodeName.toLowerCase() === 'input' &&
|
||||||
|
target.type.toLowerCase() === 'checkbox'
|
||||||
|
? target.checked
|
||||||
|
: target.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const toggleState = (_, { target: { name } }) => state => ({
|
||||||
|
...state,
|
||||||
|
[name]: !state[name],
|
||||||
|
})
|
@ -1,22 +1,18 @@
|
|||||||
import _, { messages } from 'intl'
|
import _, { messages } from 'intl'
|
||||||
import ActionButton from 'action-button'
|
|
||||||
import Component from 'base-component'
|
|
||||||
import Icon from 'icon'
|
import Icon from 'icon'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import SortedTable from 'sorted-table'
|
import SortedTable from 'sorted-table'
|
||||||
import StateButton from 'state-button'
|
import StateButton from 'state-button'
|
||||||
import Tooltip from 'tooltip'
|
import Tooltip from 'tooltip'
|
||||||
import { addSubscriptions } from 'utils'
|
import { addSubscriptions, generateRandomId } from 'utils'
|
||||||
import { alert, confirm } from 'modal'
|
import { alert } from 'modal'
|
||||||
import { error } from 'notification'
|
|
||||||
import { format, parse } from 'xo-remote-parser'
|
import { format, parse } from 'xo-remote-parser'
|
||||||
import { groupBy, map, isEmpty, some } from 'lodash'
|
import { groupBy, map, isEmpty } from 'lodash'
|
||||||
import { injectIntl } from 'react-intl'
|
import { injectIntl } from 'react-intl'
|
||||||
import { Number as InputNumber } from 'form'
|
import { injectState, provideState } from '@julien-f/freactal'
|
||||||
import { Number, Password, Text } from 'editable'
|
import { Number, Password, Text } from 'editable'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createRemote,
|
|
||||||
deleteRemote,
|
deleteRemote,
|
||||||
deleteRemotes,
|
deleteRemotes,
|
||||||
disableRemote,
|
disableRemote,
|
||||||
@ -26,11 +22,8 @@ import {
|
|||||||
testRemote,
|
testRemote,
|
||||||
} from 'xo'
|
} from 'xo'
|
||||||
|
|
||||||
const remoteTypes = {
|
import Remote from './remote'
|
||||||
file: 'remoteTypeLocal',
|
|
||||||
nfs: 'remoteTypeNfs',
|
|
||||||
smb: 'remoteTypeSmb',
|
|
||||||
}
|
|
||||||
const _changeUrlElement = (value, { remote, element }) =>
|
const _changeUrlElement = (value, { remote, element }) =>
|
||||||
editRemote(remote, {
|
editRemote(remote, {
|
||||||
url: format({ ...remote, [element]: value === null ? undefined : value }),
|
url: format({ ...remote, [element]: value === null ? undefined : value }),
|
||||||
@ -230,6 +223,12 @@ const INDIVIDUAL_ACTIONS = [
|
|||||||
label: _('remoteTestTip'),
|
label: _('remoteTestTip'),
|
||||||
level: 'primary',
|
level: 'primary',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
handler: (remote, { editRemote }) => editRemote(remote),
|
||||||
|
icon: 'edit',
|
||||||
|
label: _('formEdit'),
|
||||||
|
level: 'primary',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: deleteRemote,
|
handler: deleteRemote,
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
@ -242,317 +241,94 @@ const FILTERS = {
|
|||||||
filterRemotesOnlyDisconnected: '!enabled?',
|
filterRemotesOnlyDisconnected: '!enabled?',
|
||||||
}
|
}
|
||||||
|
|
||||||
@addSubscriptions({
|
export default [
|
||||||
remotes: cb =>
|
addSubscriptions({
|
||||||
subscribeRemotes(remotes => {
|
remotes: cb =>
|
||||||
cb(
|
subscribeRemotes(remotes => {
|
||||||
groupBy(
|
cb(
|
||||||
map(remotes, remote => {
|
groupBy(
|
||||||
try {
|
map(remotes, remote => {
|
||||||
return {
|
try {
|
||||||
...remote,
|
return {
|
||||||
...parse(remote.url),
|
...remote,
|
||||||
|
...parse(remote.url),
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Remote parsing error:', remote, '\n', err)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
}).filter(r => r !== undefined),
|
||||||
console.error('Remote parsing error:', remote, '\n', err)
|
'type'
|
||||||
}
|
)
|
||||||
}).filter(r => r !== undefined),
|
|
||||||
'type'
|
|
||||||
)
|
)
|
||||||
)
|
}),
|
||||||
|
}),
|
||||||
|
injectIntl,
|
||||||
|
provideState({
|
||||||
|
initialState: () => ({
|
||||||
|
formId: generateRandomId(),
|
||||||
|
remote: undefined,
|
||||||
}),
|
}),
|
||||||
})
|
effects: {
|
||||||
@injectIntl
|
reset: () => () => ({
|
||||||
export default class Remotes extends Component {
|
formId: generateRandomId(),
|
||||||
constructor (props) {
|
remote: undefined,
|
||||||
super(props)
|
}),
|
||||||
this.state = {
|
editRemote: (_, remote) => () => ({
|
||||||
domain: '',
|
remote,
|
||||||
host: '',
|
}),
|
||||||
name: '',
|
},
|
||||||
password: '',
|
}),
|
||||||
path: '',
|
injectState,
|
||||||
port: undefined,
|
({ state, effects, remotes = {}, intl: { formatMessage } }) => (
|
||||||
type: 'nfs',
|
<div>
|
||||||
username: '',
|
{!isEmpty(remotes.file) && (
|
||||||
}
|
<div>
|
||||||
}
|
<h2>{_('remoteTypeLocal')}</h2>
|
||||||
|
<SortedTable
|
||||||
|
collection={remotes.file}
|
||||||
|
columns={COLUMNS_LOCAL_REMOTE}
|
||||||
|
data-editRemote={effects.editRemote}
|
||||||
|
data-formatMessage={formatMessage}
|
||||||
|
filters={FILTERS}
|
||||||
|
groupedActions={GROUPED_ACTIONS}
|
||||||
|
individualActions={INDIVIDUAL_ACTIONS}
|
||||||
|
stateUrlParam='l'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
_checkNameExists = () =>
|
{!isEmpty(remotes.nfs) && (
|
||||||
some(this.props.remotes, values => some(values, ['name', this.state.name]))
|
<div>
|
||||||
? alert(
|
<h2>{_('remoteTypeNfs')}</h2>
|
||||||
<span>
|
<SortedTable
|
||||||
<Icon icon='error' /> {_('remoteTestName')}
|
collection={remotes.nfs}
|
||||||
</span>,
|
columns={COLUMNS_NFS_REMOTE}
|
||||||
<p>{_('remoteTestNameFailure')}</p>
|
data-editRemote={effects.editRemote}
|
||||||
)
|
data-formatMessage={formatMessage}
|
||||||
: this._createRemote()
|
filters={FILTERS}
|
||||||
|
groupedActions={GROUPED_ACTIONS}
|
||||||
|
individualActions={INDIVIDUAL_ACTIONS}
|
||||||
|
stateUrlParam='nfs'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
_createRemote = async () => {
|
{!isEmpty(remotes.smb) && (
|
||||||
const {
|
<div>
|
||||||
domain,
|
<h2>{_('remoteTypeSmb')}</h2>
|
||||||
host,
|
<SortedTable
|
||||||
name,
|
collection={remotes.smb}
|
||||||
password,
|
columns={COLUMNS_SMB_REMOTE}
|
||||||
path,
|
data-editRemote={effects.editRemote}
|
||||||
port,
|
data-formatMessage={formatMessage}
|
||||||
type,
|
filters={FILTERS}
|
||||||
username,
|
groupedActions={GROUPED_ACTIONS}
|
||||||
} = this.state
|
individualActions={INDIVIDUAL_ACTIONS}
|
||||||
|
stateUrlParam='smb'
|
||||||
const urlParams = {
|
/>
|
||||||
host,
|
</div>
|
||||||
path,
|
)}
|
||||||
port,
|
<Remote formatMessage={formatMessage} key={state.formId} />
|
||||||
type,
|
</div>
|
||||||
}
|
),
|
||||||
username && (urlParams.username = username)
|
].reduceRight((value, decorator) => decorator(value))
|
||||||
password && (urlParams.password = password)
|
|
||||||
domain && (urlParams.domain = domain)
|
|
||||||
|
|
||||||
if (type === 'file') {
|
|
||||||
await confirm({
|
|
||||||
title: _('localRemoteWarningTitle'),
|
|
||||||
body: _('localRemoteWarningMessage'),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = format(urlParams)
|
|
||||||
return createRemote(name, url).then(
|
|
||||||
() => {
|
|
||||||
this.setState({
|
|
||||||
domain: '',
|
|
||||||
host: '',
|
|
||||||
name: '',
|
|
||||||
password: '',
|
|
||||||
path: '',
|
|
||||||
port: undefined,
|
|
||||||
type: 'nfs',
|
|
||||||
username: '',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
err => error('Create Remote', err.message || String(err))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const {
|
|
||||||
intl: { formatMessage },
|
|
||||||
remotes = {},
|
|
||||||
} = this.props
|
|
||||||
const {
|
|
||||||
domain,
|
|
||||||
host,
|
|
||||||
name,
|
|
||||||
password,
|
|
||||||
path,
|
|
||||||
port,
|
|
||||||
type,
|
|
||||||
username,
|
|
||||||
} = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!isEmpty(remotes.file) && (
|
|
||||||
<div>
|
|
||||||
<h2>{_('remoteTypeLocal')}</h2>
|
|
||||||
<SortedTable
|
|
||||||
collection={remotes.file}
|
|
||||||
columns={COLUMNS_LOCAL_REMOTE}
|
|
||||||
data-formatMessage={formatMessage}
|
|
||||||
filters={FILTERS}
|
|
||||||
groupedActions={GROUPED_ACTIONS}
|
|
||||||
individualActions={INDIVIDUAL_ACTIONS}
|
|
||||||
stateUrlParam='l'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isEmpty(remotes.nfs) && (
|
|
||||||
<div>
|
|
||||||
<h2>{_('remoteTypeNfs')}</h2>
|
|
||||||
<SortedTable
|
|
||||||
collection={remotes.nfs}
|
|
||||||
columns={COLUMNS_NFS_REMOTE}
|
|
||||||
data-formatMessage={formatMessage}
|
|
||||||
filters={FILTERS}
|
|
||||||
groupedActions={GROUPED_ACTIONS}
|
|
||||||
individualActions={INDIVIDUAL_ACTIONS}
|
|
||||||
stateUrlParam='nfs'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isEmpty(remotes.smb) && (
|
|
||||||
<div>
|
|
||||||
<h2>{_('remoteTypeSmb')}</h2>
|
|
||||||
<SortedTable
|
|
||||||
collection={remotes.smb}
|
|
||||||
columns={COLUMNS_SMB_REMOTE}
|
|
||||||
data-formatMessage={formatMessage}
|
|
||||||
filters={FILTERS}
|
|
||||||
groupedActions={GROUPED_ACTIONS}
|
|
||||||
individualActions={INDIVIDUAL_ACTIONS}
|
|
||||||
stateUrlParam='smb'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<h2>{_('newRemote')}</h2>
|
|
||||||
<form id='newRemoteForm'>
|
|
||||||
<div className='form-group'>
|
|
||||||
<label htmlFor='newRemoteType'>{_('remoteType')}</label>
|
|
||||||
<select
|
|
||||||
id='newRemoteType'
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('type')}
|
|
||||||
required
|
|
||||||
value={type}
|
|
||||||
>
|
|
||||||
{map(remoteTypes, (label, key) =>
|
|
||||||
_({ key }, label, message => (
|
|
||||||
<option value={key}>{message}</option>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</select>
|
|
||||||
{type === 'smb' && (
|
|
||||||
<em className='text-warning'>{_('remoteSmbWarningMessage')}</em>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className='form-group'>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('name')}
|
|
||||||
placeholder={formatMessage(messages.remoteMyNamePlaceHolder)}
|
|
||||||
required
|
|
||||||
type='text'
|
|
||||||
value={name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{type === 'file' && (
|
|
||||||
<fieldset className='form-group'>
|
|
||||||
<div className='input-group'>
|
|
||||||
<span className='input-group-addon'>/</span>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('path')}
|
|
||||||
pattern='^(([^/]+)+(/[^/]+)*)?$'
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteLocalPlaceHolderPath
|
|
||||||
)}
|
|
||||||
type='text'
|
|
||||||
value={path}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
)}
|
|
||||||
{type === 'nfs' && (
|
|
||||||
<fieldset>
|
|
||||||
<div className='form-group'>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('host')}
|
|
||||||
placeholder={formatMessage(messages.remoteNfsPlaceHolderHost)}
|
|
||||||
type='text'
|
|
||||||
value={host}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<InputNumber
|
|
||||||
onChange={this.linkState('port')}
|
|
||||||
placeholder={formatMessage(messages.remoteNfsPlaceHolderPort)}
|
|
||||||
value={port}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='input-group form-group'>
|
|
||||||
<span className='input-group-addon'>/</span>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('path')}
|
|
||||||
pattern='^(([^/]+)+(/[^/]+)*)?$'
|
|
||||||
placeholder={formatMessage(messages.remoteNfsPlaceHolderPath)}
|
|
||||||
type='text'
|
|
||||||
value={path}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
)}
|
|
||||||
{type === 'smb' && (
|
|
||||||
<fieldset>
|
|
||||||
<div className='input-group form-group'>
|
|
||||||
<span className='input-group-addon'>\\</span>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('host')}
|
|
||||||
pattern='^([^\\/]+)\\([^\\/]+)$'
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteSmbPlaceHolderAddressShare
|
|
||||||
)}
|
|
||||||
type='text'
|
|
||||||
value={host}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<span className='input-group-addon'>\</span>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('path')}
|
|
||||||
pattern='^(([^\\/]+)+(\\[^\\/]+)*)?$'
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteSmbPlaceHolderRemotePath
|
|
||||||
)}
|
|
||||||
type='text'
|
|
||||||
value={path}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-group'>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('username')}
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteSmbPlaceHolderUsername
|
|
||||||
)}
|
|
||||||
type='text'
|
|
||||||
value={username}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-group'>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('password')}
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteSmbPlaceHolderPassword
|
|
||||||
)}
|
|
||||||
type='text'
|
|
||||||
value={password}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='form-group'>
|
|
||||||
<input
|
|
||||||
className='form-control'
|
|
||||||
onChange={this.linkState('domain')}
|
|
||||||
placeholder={formatMessage(
|
|
||||||
messages.remoteSmbPlaceHolderDomain
|
|
||||||
)}
|
|
||||||
required
|
|
||||||
type='text'
|
|
||||||
value={domain}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
)}
|
|
||||||
<div className='form-group'>
|
|
||||||
<ActionButton
|
|
||||||
type='submit'
|
|
||||||
form='newRemoteForm'
|
|
||||||
icon='save'
|
|
||||||
btnStyle='primary'
|
|
||||||
handler={this._checkNameExists}
|
|
||||||
>
|
|
||||||
{_('savePluginConfiguration')}
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
315
packages/xo-web/src/xo-app/settings/remotes/remote.js
Normal file
315
packages/xo-web/src/xo-app/settings/remotes/remote.js
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
import _, { messages } from 'intl'
|
||||||
|
import ActionButton from 'action-button'
|
||||||
|
import Icon from 'icon'
|
||||||
|
import React from 'react'
|
||||||
|
import { addSubscriptions, generateRandomId } from 'utils'
|
||||||
|
import { alert, confirm } from 'modal'
|
||||||
|
import { createRemote, editRemote, subscribeRemotes } from 'xo'
|
||||||
|
import { error } from 'notification'
|
||||||
|
import { format } from 'xo-remote-parser'
|
||||||
|
import { injectState, provideState } from '@julien-f/freactal'
|
||||||
|
import { linkState } from 'freactal-utils'
|
||||||
|
import { map, some, trimStart } from 'lodash'
|
||||||
|
import { Number } from 'form'
|
||||||
|
|
||||||
|
const remoteTypes = {
|
||||||
|
file: 'remoteTypeLocal',
|
||||||
|
nfs: 'remoteTypeNfs',
|
||||||
|
smb: 'remoteTypeSmb',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
addSubscriptions({
|
||||||
|
remotes: subscribeRemotes,
|
||||||
|
}),
|
||||||
|
provideState({
|
||||||
|
initialState: () => ({
|
||||||
|
domain: undefined,
|
||||||
|
host: undefined,
|
||||||
|
inputTypeId: generateRandomId(),
|
||||||
|
name: undefined,
|
||||||
|
password: undefined,
|
||||||
|
path: undefined,
|
||||||
|
port: undefined,
|
||||||
|
type: undefined,
|
||||||
|
username: undefined,
|
||||||
|
}),
|
||||||
|
effects: {
|
||||||
|
linkState,
|
||||||
|
setPort: (_, port) => state => ({
|
||||||
|
port: port === undefined && state.remote !== undefined ? '' : port,
|
||||||
|
}),
|
||||||
|
editRemote: ({ reset }) => state => {
|
||||||
|
const {
|
||||||
|
remote,
|
||||||
|
domain = remote.domain,
|
||||||
|
host = remote.host,
|
||||||
|
name,
|
||||||
|
password = remote.password,
|
||||||
|
path = remote.path,
|
||||||
|
port = remote.port,
|
||||||
|
type = remote.type,
|
||||||
|
username = remote.username,
|
||||||
|
} = state
|
||||||
|
return editRemote(remote, {
|
||||||
|
name,
|
||||||
|
url: format({
|
||||||
|
domain,
|
||||||
|
host,
|
||||||
|
password,
|
||||||
|
path,
|
||||||
|
port: port || undefined,
|
||||||
|
type,
|
||||||
|
username,
|
||||||
|
}),
|
||||||
|
}).then(reset)
|
||||||
|
},
|
||||||
|
createRemote: ({ reset }) => async (state, { remotes }) => {
|
||||||
|
if (some(remotes, { name: state.name })) {
|
||||||
|
return alert(
|
||||||
|
<span>
|
||||||
|
<Icon icon='error' /> {_('remoteTestName')}
|
||||||
|
</span>,
|
||||||
|
<p>{_('remoteTestNameFailure')}</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
domain,
|
||||||
|
host,
|
||||||
|
name,
|
||||||
|
password,
|
||||||
|
path,
|
||||||
|
port,
|
||||||
|
type = 'nfs',
|
||||||
|
username,
|
||||||
|
} = state
|
||||||
|
|
||||||
|
const urlParams = {
|
||||||
|
host,
|
||||||
|
path,
|
||||||
|
port,
|
||||||
|
type,
|
||||||
|
}
|
||||||
|
username && (urlParams.username = username)
|
||||||
|
password && (urlParams.password = password)
|
||||||
|
domain && (urlParams.domain = domain)
|
||||||
|
|
||||||
|
if (type === 'file') {
|
||||||
|
await confirm({
|
||||||
|
title: _('localRemoteWarningTitle'),
|
||||||
|
body: _('localRemoteWarningMessage'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = format(urlParams)
|
||||||
|
return createRemote(name, url)
|
||||||
|
.then(reset)
|
||||||
|
.catch(err => error('Create Remote', err.message || String(err)))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
parsedPath: ({ remote }) => remote && trimStart(remote.path, '/'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
injectState,
|
||||||
|
({ state, effects, formatMessage }) => {
|
||||||
|
const {
|
||||||
|
remote = {},
|
||||||
|
domain = remote.domain || '',
|
||||||
|
host = remote.host || '',
|
||||||
|
name = remote.name || '',
|
||||||
|
password = remote.password || '',
|
||||||
|
parsedPath,
|
||||||
|
path = parsedPath || '',
|
||||||
|
port = remote.port,
|
||||||
|
type = remote.type || 'nfs',
|
||||||
|
username = remote.username || '',
|
||||||
|
} = state
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>{_('newRemote')}</h2>
|
||||||
|
<form id={state.formId}>
|
||||||
|
<div className='form-group'>
|
||||||
|
<label htmlFor={state.inputTypeId}>{_('remoteType')}</label>
|
||||||
|
<select
|
||||||
|
className='form-control'
|
||||||
|
id={state.inputTypeId}
|
||||||
|
name='type'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
required
|
||||||
|
value={type}
|
||||||
|
>
|
||||||
|
{map(remoteTypes, (label, key) =>
|
||||||
|
_({ key }, label, message => (
|
||||||
|
<option value={key}>{message}</option>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
{type === 'smb' && (
|
||||||
|
<em className='text-warning'>{_('remoteSmbWarningMessage')}</em>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='form-group'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='name'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
placeholder={formatMessage(messages.remoteMyNamePlaceHolder)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{type === 'file' && (
|
||||||
|
<fieldset className='form-group'>
|
||||||
|
<div className='input-group'>
|
||||||
|
<span className='input-group-addon'>/</span>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='path'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
pattern='^(([^/]+)+(/[^/]+)*)?$'
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteLocalPlaceHolderPath
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={path}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
|
{type === 'nfs' && (
|
||||||
|
<fieldset>
|
||||||
|
<div className='form-group'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='host'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
placeholder={formatMessage(messages.remoteNfsPlaceHolderHost)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={host}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<Number
|
||||||
|
onChange={effects.setPort}
|
||||||
|
placeholder={formatMessage(messages.remoteNfsPlaceHolderPort)}
|
||||||
|
value={port}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='input-group form-group'>
|
||||||
|
<span className='input-group-addon'>/</span>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='path'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
pattern='^(([^/]+)+(/[^/]+)*)?$'
|
||||||
|
placeholder={formatMessage(messages.remoteNfsPlaceHolderPath)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={path}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
|
{type === 'smb' && (
|
||||||
|
<fieldset>
|
||||||
|
<div className='input-group form-group'>
|
||||||
|
<span className='input-group-addon'>\\</span>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='host'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
pattern='^([^\\/]+)\\([^\\/]+)$'
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteSmbPlaceHolderAddressShare
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={host}
|
||||||
|
/>
|
||||||
|
<span className='input-group-addon'>\</span>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='path'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
pattern='^(([^\\/]+)+(\\[^\\/]+)*)?$'
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteSmbPlaceHolderRemotePath
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={path}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-group'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='username'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteSmbPlaceHolderUsername
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={username}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-group'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
name='password'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteSmbPlaceHolderPassword
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={password}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='form-group'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
onChange={effects.linkState}
|
||||||
|
name='domain'
|
||||||
|
placeholder={formatMessage(
|
||||||
|
messages.remoteSmbPlaceHolderDomain
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
type='text'
|
||||||
|
value={domain}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
|
<div className='form-group'>
|
||||||
|
<ActionButton
|
||||||
|
btnStyle='primary'
|
||||||
|
form={state.formId}
|
||||||
|
handler={
|
||||||
|
state.remote === undefined
|
||||||
|
? effects.createRemote
|
||||||
|
: effects.editRemote
|
||||||
|
}
|
||||||
|
icon='save'
|
||||||
|
type='submit'
|
||||||
|
>
|
||||||
|
{_('savePluginConfiguration')}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
className='pull-right'
|
||||||
|
handler={effects.reset}
|
||||||
|
icon='reset'
|
||||||
|
type='reset'
|
||||||
|
>
|
||||||
|
{_('formReset')}
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
].reduceRight((value, decorator) => decorator(value))
|
Loading…
Reference in New Issue
Block a user