ContentPlugin.jsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import React from 'react'
  16. import styled from 'styled-components'
  17. import configLoader from '../../../utils/Config'
  18. import ToggleButtonBar from '../../../components/atoms/ToggleButtonBar'
  19. import type { Field } from '../../../types/Field'
  20. import { Wrapper, Fields, FieldStyled, Row } from '../default/ContentPlugin'
  21. const ToggleButtonBarStyled = styled(ToggleButtonBar)`
  22. margin-top: 16px;
  23. `
  24. type Props = {
  25. connectionInfoSchema: Field[],
  26. validation: { valid: boolean, validation: { message: string } },
  27. invalidFields: string[],
  28. getFieldValue: (field: ?Field) => any,
  29. handleFieldChange: (field: ?Field, value: any) => void,
  30. disabled: boolean,
  31. cancelButtonText: string,
  32. validating: boolean,
  33. onRef: (contentPlugin: any) => void,
  34. onResizeUpdate: (scrollOfset: number) => void,
  35. scrollableRef: (ref: HTMLElement) => void,
  36. }
  37. type State = {
  38. useAdvancedOptions: boolean,
  39. }
  40. class ContentPlugin extends React.Component<Props, State> {
  41. state = {
  42. useAdvancedOptions: false,
  43. }
  44. previouslySelectedChoices: string[] = []
  45. get useCurrentUser(): boolean {
  46. return Boolean(this.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'openstack_use_current_user')))
  47. }
  48. componentDidMount() {
  49. this.props.onRef(this)
  50. }
  51. componentDidUpdate(prevProps: Props, prevState: State) {
  52. if (prevState.useAdvancedOptions !== this.state.useAdvancedOptions) {
  53. this.props.onResizeUpdate(0)
  54. }
  55. }
  56. componentWillUnmount() {
  57. this.props.onRef(undefined)
  58. }
  59. getApiVersion(): number {
  60. return this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === 'identity_api_version'))
  61. }
  62. getFieldValue(field: ?Field) {
  63. let fieldValue = this.props.getFieldValue(field)
  64. if (fieldValue) {
  65. return fieldValue
  66. }
  67. let getInputChoiceValue = (fieldBaseName: string): string => {
  68. let id = this.props.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === `${fieldBaseName}_id`))
  69. let previouslySelected = this.previouslySelectedChoices.find(f => f === `${fieldBaseName}_id`)
  70. if (id || previouslySelected) {
  71. if (!previouslySelected) this.previouslySelectedChoices.push(`${fieldBaseName}_id`)
  72. return `${fieldBaseName}_id`
  73. }
  74. return `${fieldBaseName}_name`
  75. }
  76. if (field && field.name === 'user_domain') {
  77. return getInputChoiceValue('user_domain')
  78. }
  79. if (field && field.name === 'project_domain') {
  80. return getInputChoiceValue('project_domain')
  81. }
  82. return fieldValue
  83. }
  84. handleAdvancedOptionsToggle(useAdvancedOptions: boolean) {
  85. this.setState({ useAdvancedOptions })
  86. }
  87. findInvalidFields = () => {
  88. let inputChoices = ['user_domain', 'project_domain']
  89. const invalidFields = this.props.connectionInfoSchema.filter(field => {
  90. if (this.isFieldRequired(field)) {
  91. let value = this.getFieldValue(field)
  92. return !value
  93. }
  94. let inputChoice = inputChoices.find(c => c === field.name)
  95. if (inputChoice && this.getApiVersion() > 2) {
  96. let selectionValue = this.getFieldValue(this.props.connectionInfoSchema.find(f => f.name === inputChoice))
  97. let itemValue = this.getFieldValue(this.props.connectionInfoSchema.find(f => f.name === selectionValue))
  98. return !itemValue
  99. }
  100. return false
  101. }).map(f => f.name)
  102. return invalidFields
  103. }
  104. filterSimpleAdvanced(): Field[] {
  105. let extraAdvancedFields = ['description', 'glance_api_version', 'identity_api_version', 'openstack_use_current_user']
  106. if (this.getApiVersion() > 2) {
  107. extraAdvancedFields = extraAdvancedFields.concat(['user_domain', 'project_domain'])
  108. }
  109. let ignoreFields = ['user_domain_id', 'project_domain_id', 'user_domain_name', 'project_domain_name']
  110. if (!configLoader.config.showOpenstackCurrentUserSwitch) {
  111. ignoreFields.push('openstack_use_current_user')
  112. }
  113. return this.props.connectionInfoSchema.filter(f => !ignoreFields.find(i => i === f.name)).filter(field => {
  114. if (this.state.useAdvancedOptions) {
  115. return true
  116. }
  117. return field.required || extraAdvancedFields.find(fieldName => field.name === fieldName)
  118. })
  119. }
  120. isFieldRequired(field: Field) {
  121. return this.useCurrentUser ? field.name === 'name' : field.required
  122. }
  123. renderSimpleAdvancedToggle() {
  124. return (
  125. <ToggleButtonBarStyled
  126. items={[{ label: 'Simple', value: 'simple' }, { label: 'Advanced', value: 'advanced' }]}
  127. selectedValue={this.state.useAdvancedOptions ? 'advanced' : 'simple'}
  128. onChange={item => { this.handleAdvancedOptionsToggle(item.value === 'advanced') }}
  129. />
  130. )
  131. }
  132. renderFields() {
  133. const rows = []
  134. let lastField
  135. let fields = this.filterSimpleAdvanced()
  136. fields.forEach((field, i) => {
  137. let disabled = this.props.disabled
  138. || (this.useCurrentUser && field.name !== 'name' && field.name !== 'description' && field.name !== 'openstack_use_current_user')
  139. let required = this.isFieldRequired(field)
  140. || (this.getApiVersion() > 2 ? field.name === 'user_domain' || field.name === 'project_domain' : false)
  141. const currentField = (
  142. <FieldStyled
  143. {...field}
  144. required={required}
  145. large
  146. disabled={disabled}
  147. password={field.name === 'password'}
  148. highlight={this.props.invalidFields.findIndex(fn => fn === field.name) > -1}
  149. value={this.getFieldValue(field)}
  150. onChange={value => { this.props.handleFieldChange(field, value) }}
  151. getFieldValue={fieldName => this.getFieldValue(this.props.connectionInfoSchema.find(n => n.name === fieldName))}
  152. onFieldChange={(fieldName, fieldValue) => { this.props.handleFieldChange(this.props.connectionInfoSchema.find(n => n.name === fieldName), fieldValue) }}
  153. />
  154. )
  155. if (i % 2 !== 0) {
  156. rows.push((
  157. <Row key={field.name}>
  158. {lastField}
  159. {currentField}
  160. </Row>
  161. ))
  162. } else if (i === fields.length - 1) {
  163. rows.push((
  164. <Row key={field.name}>
  165. {currentField}
  166. </Row>
  167. ))
  168. }
  169. lastField = currentField
  170. })
  171. return (
  172. <Fields innerRef={ref => { this.props.scrollableRef(ref) }}>
  173. {rows}
  174. </Fields>
  175. )
  176. }
  177. render() {
  178. return (
  179. <Wrapper>
  180. {this.renderSimpleAdvancedToggle()}
  181. {this.renderFields()}
  182. </Wrapper>
  183. )
  184. }
  185. }
  186. export default ContentPlugin