add helper to format all manifest args and to reformat a form

This commit is contained in:
Axolotle 2020-10-23 18:14:09 +02:00
parent e05b8da533
commit 1346197605
2 changed files with 154 additions and 37 deletions

View file

@ -40,3 +40,32 @@ export function objectToParams (object, { addLocale = false } = {}) {
} }
return urlParams return urlParams
} }
/**
* Check if passed value is an object literal.
*
* @return {Boolean}
*/
export function isObjectLiteral (value) {
return value !== null && value !== undefined && Object.is(value.constructor, Object)
}
/**
* Returns an flattened object literal, with all keys at first level and removing nested ones.
*
* @return {Object}
*/
export function flattenObjectLiteral (obj_) {
const flattened = {}
function flatten (obj) {
for (const key in obj) {
if (isObjectLiteral(obj[key])) {
flatten(obj[key])
} else {
flattened[key] = obj[key]
}
}
}
flatten(obj_)
return flattened
}

View file

@ -1,5 +1,7 @@
import i18n from '@/i18n' import i18n from '@/i18n'
import store from '@/store' import store from '@/store'
import * as validators from '@/helpers/validators'
import { flattenObjectLiteral } from '@/helpers/commons'
/** /**
* Tries to find a translation corresponding to the user's locale/fallback locale in a * Tries to find a translation corresponding to the user's locale/fallback locale in a
@ -15,66 +17,152 @@ export function formatI18nField (field) {
} }
/** /**
* Format app install, actions and config panel arguments into a data structure that * Format app install, actions and config panel argument into a data structure that
* will be automaticly transformed into components on screen. * will be automaticly transformed into a component on screen.
* *
* @param {Object} _arg - a yunohost arg options written by a packager. * @param {Object} arg - a yunohost arg options written by a packager.
* @return {Object} an formated argument that can be fed to the FormItemHelper component. * @return {Object} an formated argument containing formItem props, validation and base value.
*/ */
export function formatYunoHostArgument (_arg) { export function formatYunoHostArgument (arg) {
const arg = { let value = null
const validation = {}
const field = {
component: undefined, component: undefined,
label: formatI18nField(_arg.ask), label: formatI18nField(arg.ask),
props: { id: _arg.name, value: null } props: {
}
}
if (arg.type === 'boolean') {
field.id = arg.name
} else {
field.props.id = arg.name
} }
// Some apps has an argument type `string` as type but expect a select since it has `choices` // Some apps has an argument type `string` as type but expect a select since it has `choices`
if (_arg.choices !== undefined) { if (arg.choices !== undefined) {
arg.component = 'SelectItem' field.component = 'SelectItem'
arg.props.choices = _arg.choices field.props.choices = arg.choices
// Input // Input
} else if ([undefined, 'string', 'number', 'password', 'email'].includes(_arg.type)) { } else if ([undefined, 'string', 'number', 'password', 'email'].includes(arg.type)) {
arg.component = 'InputItem' field.component = 'InputItem'
if (![undefined, 'string'].includes(_arg.type)) { if (![undefined, 'string'].includes(arg.type)) {
arg.props.type = _arg.type field.props.type = arg.type
if (_arg.type === 'password') { if (arg.type === 'password') {
arg.description = i18n.t('good_practices_about_admin_password') field.description = i18n.t('good_practices_about_admin_password')
field.placeholder = '••••••••'
validation.passwordLenght = validators.minLength(8)
} }
} }
// Checkbox // Checkbox
} else if (_arg.type === 'boolean') { } else if (arg.type === 'boolean') {
arg.component = 'CheckboxItem' field.component = 'CheckboxItem'
arg.props.value = _arg.default || false value = arg.default || false
console.log('check', value)
// Special (store related) // Special (store related)
} else if (['user', 'domain'].includes(_arg.type)) { } else if (['user', 'domain'].includes(arg.type)) {
arg.component = 'SelectItem' field.component = 'SelectItem'
arg.link = { name: _arg.type + '-list', text: i18n.t(`manage_${_arg.type}s`) } field.link = { name: arg.type + '-list', text: i18n.t(`manage_${arg.type}s`) }
arg.props = { ...arg.props, ...store.getters[_arg.type + 'sAsOptions'] } field.props.choices = store.getters[arg.type + 'sAsChoices']
value = arg.type === 'domain' ? store.getters.mainDomain : field.props.choices[0].value
// Unknown from the specs, try to display it as an input[text] // Unknown from the specs, try to display it as an input[text]
// FIXME throw an error instead ? // FIXME throw an error instead ?
} else { } else {
arg.component = 'InputItem' field.component = 'InputItem'
} }
// Required for inputs (no need for checkbox and select, their values can't be null) // Required (no need for checkbox its value can't be null)
if (arg.component === 'InputItem') { if (field.component !== 'CheckboxItem' && arg.optional !== true) {
arg.props.required = _arg.optional !== true validation.required = validators.required
} }
// Default value // Default value
if (_arg.default) { if (arg.default) {
arg.props.value = _arg.default value = arg.default
} }
// Help message // Help message
if (_arg.help) { if (arg.help) {
arg.description = formatI18nField(_arg.help) field.description = formatI18nField(arg.help)
} }
// Example // Example
if (_arg.example) { if (arg.example) {
arg.example = _arg.example field.example = arg.example
if (arg.component === 'InputItem') { if (field.component === 'InputItem') {
arg.props.placeholder = arg.example field.props.placeholder = field.example
} }
} }
return arg return {
value,
field,
// Return null instead of empty object if there's no validation
validation: Object.keys(validation).length === 0 ? null : validation
}
}
/**
* Format app install, actions and config panel manifest args into a form that can be used
* as v-model values, fields that can be passed to a FormField component and validations.
*
* @param {Array} args - a yunohost arg array written by a packager.
* @param {String} name - (temp) an app name to build a label field in case of manifest install args
* @return {Object} an object containing all parsed values to be used in vue views.
*/
export function formatYunoHostArguments (args, name = null) {
let disclaimer = null
const form = {}
const fields = {}
const validations = {}
// FIXME yunohost should add the label field by default
if (name) {
args.unshift({
ask: i18n.t('label_for_manifestname', { name }),
default: name,
name: 'label'
})
}
for (const arg of args) {
if (arg.type === 'display_text') {
disclaimer = formatI18nField(arg.ask)
} else {
const { value, field, validation } = formatYunoHostArgument(arg)
fields[arg.name] = field
form[arg.name] = value
if (validation) validations[arg.name] = validation
}
}
return { form, fields, validations, disclaimer }
}
/**
* Format a form produced by a vue view to be sent to the server.
*
* @param {Object} formData - a object literal containing form values.
* @param {Object} extraParams - optionnal params
* @param {Array} extraParams.extract - an array of keys that should be extracted from the form.
* @param {Boolean} extraParams.flatten - flattens or not the passed formData.
* @return {Object} the parsed data to be sent to the server, with extracted values if specified.
*/
export function formatFormData (formData, { extract = null, flatten = false } = {}) {
if (flatten) {
formData = flattenObjectLiteral(formData)
}
const data = {}
const extracted = {}
for (const [key, value] of Object.entries(formData)) {
if (extract && extract.includes(key)) {
extracted[key] = value
} else {
if (typeof value === 'boolean') {
data[key] = value ? 1 : 0
} else {
data[key] = value
}
}
}
return extract ? { data, ...extracted } : data
} }