mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	Merge pull request #3712 from strapi/ctm/edit-formadata-upgrade
Ctm/edit formadata upgrade
This commit is contained in:
		
						commit
						00f0a536a3
					
				@ -1,6 +1,6 @@
 | 
			
		||||
import React, { memo, useEffect, useState, useReducer } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { cloneDeep, get, isEmpty } from 'lodash';
 | 
			
		||||
import { cloneDeep, get } from 'lodash';
 | 
			
		||||
import {
 | 
			
		||||
  BackHeader,
 | 
			
		||||
  getQueryParameters,
 | 
			
		||||
@ -24,7 +24,7 @@ import createYupSchema from './utils/schema';
 | 
			
		||||
import {
 | 
			
		||||
  getMediaAttributes,
 | 
			
		||||
  cleanData,
 | 
			
		||||
  associateFilesToData,
 | 
			
		||||
  mapDataKeysToFilesToUpload,
 | 
			
		||||
} from './utils/formatData';
 | 
			
		||||
 | 
			
		||||
const getRequestUrl = path => `/${pluginId}/explorer/${path}`;
 | 
			
		||||
@ -289,121 +289,46 @@ function EditView({
 | 
			
		||||
      emitEvent('willSaveEntry');
 | 
			
		||||
      // Create an object containing all the paths of the media fields
 | 
			
		||||
      const filesMap = getMediaAttributes(layout, groupLayoutsData);
 | 
			
		||||
      // Create the formdata to upload all the files
 | 
			
		||||
      const formDatas = Object.keys(filesMap).reduce((acc, current) => {
 | 
			
		||||
        const keys = current.split('.');
 | 
			
		||||
        const isMultiple = get(filesMap, [current, 'multiple'], false);
 | 
			
		||||
        const isGroup = get(filesMap, [current, 'isGroup'], false);
 | 
			
		||||
        const isRepeatable = get(filesMap, [current, 'repeatable'], false);
 | 
			
		||||
 | 
			
		||||
        const getFilesToUpload = path => {
 | 
			
		||||
          const value = get(modifiedData, path, []) || [];
 | 
			
		||||
 | 
			
		||||
          return value.filter(file => {
 | 
			
		||||
            return file instanceof File;
 | 
			
		||||
          });
 | 
			
		||||
        };
 | 
			
		||||
        const getFileToUpload = path => {
 | 
			
		||||
          const file = get(modifiedData, [...path, 0], '');
 | 
			
		||||
          if (file instanceof File) {
 | 
			
		||||
            return [file];
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return [];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!isRepeatable) {
 | 
			
		||||
          const currentFilesToUpload = isMultiple
 | 
			
		||||
            ? getFilesToUpload(keys)
 | 
			
		||||
            : getFileToUpload([...keys]);
 | 
			
		||||
 | 
			
		||||
          if (!isEmpty(currentFilesToUpload)) {
 | 
			
		||||
            acc[current] = currentFilesToUpload.reduce((acc2, curr) => {
 | 
			
		||||
              acc2.append('files', curr);
 | 
			
		||||
 | 
			
		||||
              return acc2;
 | 
			
		||||
            }, new FormData());
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isGroup && isRepeatable) {
 | 
			
		||||
          const [key, targetKey] = current.split('.');
 | 
			
		||||
          const groupData = get(modifiedData, [key], []);
 | 
			
		||||
          const groupFiles = groupData.reduce((acc1, current, index) => {
 | 
			
		||||
            const files = isMultiple
 | 
			
		||||
              ? getFileToUpload([key, index, targetKey])
 | 
			
		||||
              : getFileToUpload([key, index, targetKey]);
 | 
			
		||||
 | 
			
		||||
            if (!isEmpty(files)) {
 | 
			
		||||
              const toFormData = files.reduce((acc2, curr) => {
 | 
			
		||||
                acc2.append('files', curr);
 | 
			
		||||
 | 
			
		||||
                return acc2;
 | 
			
		||||
              }, new FormData());
 | 
			
		||||
 | 
			
		||||
              acc1[`${key}.${index}.${targetKey}`] = toFormData;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return acc1;
 | 
			
		||||
          }, {});
 | 
			
		||||
 | 
			
		||||
          return { ...acc, ...groupFiles };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return acc;
 | 
			
		||||
      }, {});
 | 
			
		||||
      // Change the request helper default headers so we can pass a FormData
 | 
			
		||||
      const headers = { 'X-Forwarded-Host': 'strapi' };
 | 
			
		||||
      // Upload the files
 | 
			
		||||
      const mapUploadedFiles = Object.keys(formDatas).reduce(
 | 
			
		||||
        async (acc, current) => {
 | 
			
		||||
          const collection = await acc;
 | 
			
		||||
          try {
 | 
			
		||||
            const uploadedFiles = await request(
 | 
			
		||||
              '/upload',
 | 
			
		||||
              {
 | 
			
		||||
                method: 'POST',
 | 
			
		||||
                body: formDatas[current],
 | 
			
		||||
                headers,
 | 
			
		||||
                signal: submitSignal,
 | 
			
		||||
              },
 | 
			
		||||
              false,
 | 
			
		||||
              false
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            collection[current] = uploadedFiles;
 | 
			
		||||
 | 
			
		||||
            return collection;
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            console.log('upload error', err);
 | 
			
		||||
            strapi.notification.error(`${pluginId}.notification.upload.error`);
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        Promise.resolve({})
 | 
			
		||||
      );
 | 
			
		||||
      // Create an object that maps the keys with the related files to upload
 | 
			
		||||
      const filesToUpload = mapDataKeysToFilesToUpload(filesMap, modifiedData);
 | 
			
		||||
 | 
			
		||||
      const cleanedData = cleanData(
 | 
			
		||||
        cloneDeep(modifiedData),
 | 
			
		||||
        layout,
 | 
			
		||||
        groupLayoutsData
 | 
			
		||||
      );
 | 
			
		||||
      const cleanedDataWithUploadedFiles = associateFilesToData(
 | 
			
		||||
        cleanedData,
 | 
			
		||||
        filesMap,
 | 
			
		||||
        await mapUploadedFiles
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const formData = new FormData();
 | 
			
		||||
 | 
			
		||||
      formData.append('data', JSON.stringify(cleanedData));
 | 
			
		||||
 | 
			
		||||
      Object.keys(filesToUpload).forEach(key => {
 | 
			
		||||
        const files = filesToUpload[key];
 | 
			
		||||
 | 
			
		||||
        files.forEach(file => {
 | 
			
		||||
          formData.append(`files.${key}`, file);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Change the request helper default headers so we can pass a FormData
 | 
			
		||||
      const headers = { 'X-Forwarded-Host': 'strapi' };
 | 
			
		||||
      const method = isCreatingEntry ? 'POST' : 'PUT';
 | 
			
		||||
      const endPoint = isCreatingEntry ? slug : `${slug}/${id}`;
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        // Time to actually send the data
 | 
			
		||||
        await request(getRequestUrl(endPoint), {
 | 
			
		||||
          method,
 | 
			
		||||
          params: { source },
 | 
			
		||||
          body: cleanedDataWithUploadedFiles,
 | 
			
		||||
          signal: submitSignal,
 | 
			
		||||
        });
 | 
			
		||||
        await request(
 | 
			
		||||
          getRequestUrl(endPoint),
 | 
			
		||||
          {
 | 
			
		||||
            method,
 | 
			
		||||
            headers,
 | 
			
		||||
            params: { source },
 | 
			
		||||
            body: formData,
 | 
			
		||||
            signal: submitSignal,
 | 
			
		||||
          },
 | 
			
		||||
          false,
 | 
			
		||||
          false
 | 
			
		||||
        );
 | 
			
		||||
        emitEvent('didSaveEntry');
 | 
			
		||||
        redirectToPreviousPage();
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
@ -546,7 +471,7 @@ function EditView({
 | 
			
		||||
                        }}
 | 
			
		||||
                        groupValue={groupValue}
 | 
			
		||||
                        key={key}
 | 
			
		||||
                        isRepeatable={group.repeatable}
 | 
			
		||||
                        isRepeatable={group.repeatable || false}
 | 
			
		||||
                        name={name}
 | 
			
		||||
                        modifiedData={modifiedData}
 | 
			
		||||
                        moveGroupField={(dragIndex, overIndex, name) => {
 | 
			
		||||
 | 
			
		||||
@ -65,43 +65,47 @@ function reducer(state, action) {
 | 
			
		||||
        .update('initialData', () => fromJS(action.data))
 | 
			
		||||
        .update('modifiedData', () => fromJS(action.data))
 | 
			
		||||
        .update('isLoading', () => false);
 | 
			
		||||
    case 'GET_GROUP_LAYOUTS_SUCCEEDED':
 | 
			
		||||
    case 'GET_GROUP_LAYOUTS_SUCCEEDED': {
 | 
			
		||||
      const addTempIdToGroupData = obj => {
 | 
			
		||||
        const { defaultGroupValues } = action;
 | 
			
		||||
 | 
			
		||||
        if (action.isCreatingEntry === true) {
 | 
			
		||||
          return obj.keySeq().reduce((acc, current) => {
 | 
			
		||||
            if (defaultGroupValues[current]) {
 | 
			
		||||
              return acc.set(
 | 
			
		||||
                current,
 | 
			
		||||
                fromJS(defaultGroupValues[current].toSet)
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            return acc;
 | 
			
		||||
          }, obj);
 | 
			
		||||
        } else {
 | 
			
		||||
          return obj.keySeq().reduce((acc, current) => {
 | 
			
		||||
            if (defaultGroupValues[current] && List.isList(obj.get(current))) {
 | 
			
		||||
              const formatted = obj.get(current).reduce((acc2, curr, index) => {
 | 
			
		||||
                return acc2.set(index, curr.set('_temp__id', index));
 | 
			
		||||
              }, List([]));
 | 
			
		||||
 | 
			
		||||
              return acc.set(current, formatted);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return acc;
 | 
			
		||||
          }, obj);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      return state
 | 
			
		||||
        .update('groupLayoutsData', () => fromJS(action.groupLayouts))
 | 
			
		||||
        .update('defaultGroupValues', () => fromJS(action.defaultGroupValues))
 | 
			
		||||
        .update('modifiedData', obj => {
 | 
			
		||||
          const { defaultGroupValues } = action;
 | 
			
		||||
 | 
			
		||||
          if (action.isCreatingEntry === true) {
 | 
			
		||||
            return obj.keySeq().reduce((acc, current) => {
 | 
			
		||||
              if (defaultGroupValues[current]) {
 | 
			
		||||
                return acc.set(
 | 
			
		||||
                  current,
 | 
			
		||||
                  fromJS(defaultGroupValues[current].toSet)
 | 
			
		||||
                );
 | 
			
		||||
              }
 | 
			
		||||
              return acc;
 | 
			
		||||
            }, obj);
 | 
			
		||||
          } else {
 | 
			
		||||
            return obj.keySeq().reduce((acc, current) => {
 | 
			
		||||
              if (
 | 
			
		||||
                defaultGroupValues[current] &&
 | 
			
		||||
                List.isList(obj.get(current))
 | 
			
		||||
              ) {
 | 
			
		||||
                const formatted = obj
 | 
			
		||||
                  .get(current)
 | 
			
		||||
                  .reduce((acc2, curr, index) => {
 | 
			
		||||
                    return acc2.set(index, curr.set('_temp__id', index));
 | 
			
		||||
                  }, List([]));
 | 
			
		||||
 | 
			
		||||
                return acc.set(current, formatted);
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              return acc;
 | 
			
		||||
            }, obj);
 | 
			
		||||
          }
 | 
			
		||||
          return addTempIdToGroupData(obj);
 | 
			
		||||
        })
 | 
			
		||||
        .update('initialData', obj => {
 | 
			
		||||
          return addTempIdToGroupData(obj);
 | 
			
		||||
        })
 | 
			
		||||
        .update('isLoadingForLayouts', () => false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'INIT':
 | 
			
		||||
      return initialState
 | 
			
		||||
        .set('initialData', fromJS(action.data))
 | 
			
		||||
@ -112,11 +116,24 @@ function reducer(state, action) {
 | 
			
		||||
          .delete(action.dragIndex)
 | 
			
		||||
          .insert(action.overIndex, list.get(action.dragIndex));
 | 
			
		||||
      });
 | 
			
		||||
    case 'ON_CHANGE':
 | 
			
		||||
      return state.updateIn(
 | 
			
		||||
    case 'ON_CHANGE': {
 | 
			
		||||
      let newState = state;
 | 
			
		||||
      const [nonRepeatableGroupKey] = action.keys;
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        action.keys.length === 2 &&
 | 
			
		||||
        state.getIn(['modifiedData', nonRepeatableGroupKey]) === null
 | 
			
		||||
      ) {
 | 
			
		||||
        newState = state.updateIn(['modifiedData', nonRepeatableGroupKey], () =>
 | 
			
		||||
          fromJS({})
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return newState.updateIn(
 | 
			
		||||
        ['modifiedData', ...action.keys],
 | 
			
		||||
        () => action.value
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    case 'ON_REMOVE_FIELD':
 | 
			
		||||
      return state
 | 
			
		||||
        .removeIn(['modifiedData', ...action.keys])
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,126 @@
 | 
			
		||||
const ctLayout = {
 | 
			
		||||
  schema: {
 | 
			
		||||
    attributes: {
 | 
			
		||||
      bool: { type: 'boolean' },
 | 
			
		||||
      content: { type: 'richtext' },
 | 
			
		||||
      created_at: { type: 'timestamp' },
 | 
			
		||||
      date: { type: 'date' },
 | 
			
		||||
      enum: { type: 'enumeration', enum: ['un', 'deux'] },
 | 
			
		||||
      fb_cta: {
 | 
			
		||||
        required: true,
 | 
			
		||||
        type: 'group',
 | 
			
		||||
        group: 'cta_facebook',
 | 
			
		||||
        repeatable: false,
 | 
			
		||||
      },
 | 
			
		||||
      id: { type: 'integer' },
 | 
			
		||||
      ingredients: {
 | 
			
		||||
        type: 'group',
 | 
			
		||||
        group: 'ingredients',
 | 
			
		||||
        repeatable: true,
 | 
			
		||||
        min: 1,
 | 
			
		||||
        max: 10,
 | 
			
		||||
      },
 | 
			
		||||
      json: { type: 'json' },
 | 
			
		||||
      linkedTags: {
 | 
			
		||||
        attribute: 'tag',
 | 
			
		||||
        collection: 'tag',
 | 
			
		||||
        column: 'id',
 | 
			
		||||
        isVirtual: true,
 | 
			
		||||
        relationType: 'manyWay',
 | 
			
		||||
        targetModel: 'tag',
 | 
			
		||||
        type: 'relation',
 | 
			
		||||
      },
 | 
			
		||||
      mainIngredient: {
 | 
			
		||||
        type: 'group',
 | 
			
		||||
        group: 'ingredients',
 | 
			
		||||
        repeatable: false,
 | 
			
		||||
      },
 | 
			
		||||
      mainTag: {
 | 
			
		||||
        model: 'tag',
 | 
			
		||||
        type: 'relation',
 | 
			
		||||
        targetModel: 'tag',
 | 
			
		||||
        relationType: 'oneWay',
 | 
			
		||||
      },
 | 
			
		||||
      manyTags: {
 | 
			
		||||
        attribute: 'tag',
 | 
			
		||||
        collection: 'tag',
 | 
			
		||||
        column: 'id',
 | 
			
		||||
        dominant: true,
 | 
			
		||||
        isVirtual: true,
 | 
			
		||||
        relationType: 'manyToMany',
 | 
			
		||||
        targetModel: 'tag',
 | 
			
		||||
        type: 'relation',
 | 
			
		||||
        via: 'linkedArticles',
 | 
			
		||||
      },
 | 
			
		||||
      number: { type: 'integer' },
 | 
			
		||||
      pic: { type: 'media', multiple: false, required: false },
 | 
			
		||||
      pictures: { type: 'media', multiple: true, required: false },
 | 
			
		||||
      published: { type: 'boolean' },
 | 
			
		||||
      title: {
 | 
			
		||||
        type: 'string',
 | 
			
		||||
        default: 'test',
 | 
			
		||||
        required: true,
 | 
			
		||||
        unique: true,
 | 
			
		||||
      },
 | 
			
		||||
      updated_at: { type: 'timestampUpdate' },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const groupLayouts = {
 | 
			
		||||
  cta_facebook: {
 | 
			
		||||
    schema: {
 | 
			
		||||
      attributes: {
 | 
			
		||||
        description: { type: 'text' },
 | 
			
		||||
        id: { type: 'integer' },
 | 
			
		||||
        title: { type: 'string' },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  ingredients: {
 | 
			
		||||
    schema: {
 | 
			
		||||
      attributes: {
 | 
			
		||||
        testMultiple: { type: 'media', multiple: true },
 | 
			
		||||
        test: { type: 'media', multiple: false },
 | 
			
		||||
        id: { type: 'integer' },
 | 
			
		||||
        name: { type: 'string' },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const simpleCtLayout = {
 | 
			
		||||
  uid: 'simple',
 | 
			
		||||
  schema: {
 | 
			
		||||
    attributes: {
 | 
			
		||||
      title: {
 | 
			
		||||
        type: 'string',
 | 
			
		||||
        default: 'test',
 | 
			
		||||
      },
 | 
			
		||||
      article: {
 | 
			
		||||
        type: 'relation',
 | 
			
		||||
        relationType: 'oneToOne',
 | 
			
		||||
        targetModel: 'article',
 | 
			
		||||
      },
 | 
			
		||||
      articles: {
 | 
			
		||||
        type: 'relation',
 | 
			
		||||
        relationType: 'manyToMany',
 | 
			
		||||
        targetModel: 'article',
 | 
			
		||||
      },
 | 
			
		||||
      picture: {
 | 
			
		||||
        type: 'media',
 | 
			
		||||
        multiple: false,
 | 
			
		||||
      },
 | 
			
		||||
      pictures: {
 | 
			
		||||
        type: 'media',
 | 
			
		||||
        multiple: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  // We don't need this key for the test
 | 
			
		||||
  layouts: {},
 | 
			
		||||
  // We don't need this key for the test
 | 
			
		||||
  settings: {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { ctLayout, groupLayouts, simpleCtLayout };
 | 
			
		||||
@ -1,100 +1,212 @@
 | 
			
		||||
import { getMediaAttributes } from '../utils/formatData';
 | 
			
		||||
import {
 | 
			
		||||
  cleanData,
 | 
			
		||||
  getMediaAttributes,
 | 
			
		||||
  helperCleanData,
 | 
			
		||||
} from '../utils/formatData';
 | 
			
		||||
import { ctLayout, groupLayouts, simpleCtLayout } from './data';
 | 
			
		||||
 | 
			
		||||
describe('getMediasAttributes util', () => {
 | 
			
		||||
  let ctLayout;
 | 
			
		||||
  let groupLayouts;
 | 
			
		||||
describe('Content Manager | EditView | utils | cleanData', () => {
 | 
			
		||||
  let simpleContentTypeLayout;
 | 
			
		||||
  let contentTypeLayout;
 | 
			
		||||
  let grpLayouts;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    ctLayout = {
 | 
			
		||||
      schema: {
 | 
			
		||||
        attributes: {
 | 
			
		||||
          bool: { type: 'boolean' },
 | 
			
		||||
          content: { type: 'richtext' },
 | 
			
		||||
          created_at: { type: 'timestamp' },
 | 
			
		||||
          date: { type: 'date' },
 | 
			
		||||
          enum: { type: 'enumeration', enum: Array(2) },
 | 
			
		||||
          fb_cta: {
 | 
			
		||||
            required: true,
 | 
			
		||||
            type: 'group',
 | 
			
		||||
            group: 'cta_facebook',
 | 
			
		||||
            repeatable: false,
 | 
			
		||||
          },
 | 
			
		||||
          id: { type: 'integer' },
 | 
			
		||||
          ingredients: {
 | 
			
		||||
            type: 'group',
 | 
			
		||||
            group: 'ingredients',
 | 
			
		||||
            repeatable: true,
 | 
			
		||||
            min: 1,
 | 
			
		||||
            max: 10,
 | 
			
		||||
          },
 | 
			
		||||
          json: { type: 'json' },
 | 
			
		||||
          linkedTags: {
 | 
			
		||||
            attribute: 'tag',
 | 
			
		||||
            collection: 'tag',
 | 
			
		||||
            column: 'id',
 | 
			
		||||
            isVirtual: true,
 | 
			
		||||
            relationType: 'manyWay',
 | 
			
		||||
            targetModel: 'tag',
 | 
			
		||||
            type: 'relation',
 | 
			
		||||
          },
 | 
			
		||||
          mainIngredient: {
 | 
			
		||||
            type: 'group',
 | 
			
		||||
            group: 'ingredients',
 | 
			
		||||
            repeatable: false,
 | 
			
		||||
          },
 | 
			
		||||
          mainTag: {
 | 
			
		||||
            model: 'tag',
 | 
			
		||||
            type: 'relation',
 | 
			
		||||
            targetModel: 'tag',
 | 
			
		||||
            relationType: 'oneWay',
 | 
			
		||||
          },
 | 
			
		||||
          manyTags: {
 | 
			
		||||
            attribute: 'tag',
 | 
			
		||||
            collection: 'tag',
 | 
			
		||||
            column: 'id',
 | 
			
		||||
            dominant: true,
 | 
			
		||||
            isVirtual: true,
 | 
			
		||||
            relationType: 'manyToMany',
 | 
			
		||||
            targetModel: 'tag',
 | 
			
		||||
            type: 'relation',
 | 
			
		||||
            via: 'linkedArticles',
 | 
			
		||||
          },
 | 
			
		||||
          number: { type: 'integer' },
 | 
			
		||||
          pic: { type: 'media', multiple: false, required: false },
 | 
			
		||||
          pictures: { type: 'media', multiple: true, required: false },
 | 
			
		||||
          published: { type: 'boolean' },
 | 
			
		||||
          title: {
 | 
			
		||||
            type: 'string',
 | 
			
		||||
            default: 'soupette',
 | 
			
		||||
            required: true,
 | 
			
		||||
            unique: true,
 | 
			
		||||
          },
 | 
			
		||||
          updated_at: { type: 'timestampUpdate' },
 | 
			
		||||
        },
 | 
			
		||||
    simpleContentTypeLayout = simpleCtLayout;
 | 
			
		||||
    contentTypeLayout = ctLayout;
 | 
			
		||||
    grpLayouts = groupLayouts;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should format de data correctly if the content type has no group and no file has been added', () => {
 | 
			
		||||
    const data = {
 | 
			
		||||
      title: 'test',
 | 
			
		||||
      article: {
 | 
			
		||||
        id: 1,
 | 
			
		||||
        name: 'test',
 | 
			
		||||
      },
 | 
			
		||||
      articles: [
 | 
			
		||||
        {
 | 
			
		||||
          id: 1,
 | 
			
		||||
          name: 'test',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          id: 2,
 | 
			
		||||
          name: 'test1',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      picture: {
 | 
			
		||||
        id: 4,
 | 
			
		||||
        url: '/something-test',
 | 
			
		||||
        ext: 'unknown',
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      pictures: [
 | 
			
		||||
        {
 | 
			
		||||
          id: 1,
 | 
			
		||||
          url: '/something',
 | 
			
		||||
          ext: 'jpg',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          id: 2,
 | 
			
		||||
          url: '/something-else',
 | 
			
		||||
          ext: 'png',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
    const expected = {
 | 
			
		||||
      title: 'test',
 | 
			
		||||
      article: 1,
 | 
			
		||||
      articles: [1, 2],
 | 
			
		||||
      picture: 4,
 | 
			
		||||
      pictures: [1, 2],
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    groupLayouts = {
 | 
			
		||||
      cta_facebook: {
 | 
			
		||||
        schema: {
 | 
			
		||||
          attributes: {
 | 
			
		||||
            description: { type: 'text' },
 | 
			
		||||
            id: { type: 'integer' },
 | 
			
		||||
            title: { type: 'string' },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
    expect(cleanData(data, simpleContentTypeLayout, grpLayouts)).toEqual(
 | 
			
		||||
      expected
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should format the datac correctly when there is a repeatable and a non repeatable field', () => {
 | 
			
		||||
    const data = {
 | 
			
		||||
      bool: 'test',
 | 
			
		||||
      content: 'test',
 | 
			
		||||
      date: null,
 | 
			
		||||
      enum: 'un',
 | 
			
		||||
      fb_cta: {
 | 
			
		||||
        description: 'something cool',
 | 
			
		||||
        title: 'test',
 | 
			
		||||
      },
 | 
			
		||||
      ingredients: {
 | 
			
		||||
        schema: {
 | 
			
		||||
          attributes: {
 | 
			
		||||
            testMultiple: { type: 'media', multiple: true },
 | 
			
		||||
            test: { type: 'media', multiple: false },
 | 
			
		||||
            id: { type: 'integer' },
 | 
			
		||||
            name: { type: 'string' },
 | 
			
		||||
          },
 | 
			
		||||
      ingredients: [
 | 
			
		||||
        {
 | 
			
		||||
          testMultiple: [
 | 
			
		||||
            {
 | 
			
		||||
              id: 3,
 | 
			
		||||
              url: '/test-test',
 | 
			
		||||
            },
 | 
			
		||||
            new File([''], 'test', { type: 'text/html' }),
 | 
			
		||||
          ],
 | 
			
		||||
          test: null,
 | 
			
		||||
          name: 'Super name',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      linkedTags: [
 | 
			
		||||
        {
 | 
			
		||||
          name: 'test',
 | 
			
		||||
          id: 1,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      mainIngredient: {
 | 
			
		||||
        name: 'another name',
 | 
			
		||||
      },
 | 
			
		||||
      mainTag: {
 | 
			
		||||
        name: 'test1',
 | 
			
		||||
        id: 2,
 | 
			
		||||
      },
 | 
			
		||||
      manyTags: [
 | 
			
		||||
        {
 | 
			
		||||
          name: 'test4',
 | 
			
		||||
          id: 4,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      number: 1,
 | 
			
		||||
      pic: new File([''], 'test1', { type: 'text/html' }),
 | 
			
		||||
      pictures: [
 | 
			
		||||
        {
 | 
			
		||||
          id: 1,
 | 
			
		||||
          url: '/test',
 | 
			
		||||
        },
 | 
			
		||||
        new File([''], 'test2', { type: 'text/html' }),
 | 
			
		||||
      ],
 | 
			
		||||
      published: true,
 | 
			
		||||
      title: 'test',
 | 
			
		||||
    };
 | 
			
		||||
    const expected = {
 | 
			
		||||
      bool: 'test',
 | 
			
		||||
      content: 'test',
 | 
			
		||||
      date: null,
 | 
			
		||||
      enum: 'un',
 | 
			
		||||
      fb_cta: {
 | 
			
		||||
        description: 'something cool',
 | 
			
		||||
        title: 'test',
 | 
			
		||||
      },
 | 
			
		||||
      ingredients: [
 | 
			
		||||
        {
 | 
			
		||||
          testMultiple: [3],
 | 
			
		||||
          test: null,
 | 
			
		||||
          name: 'Super name',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      linkedTags: [1],
 | 
			
		||||
      mainIngredient: {
 | 
			
		||||
        name: 'another name',
 | 
			
		||||
      },
 | 
			
		||||
      mainTag: 2,
 | 
			
		||||
      manyTags: [4],
 | 
			
		||||
      number: 1,
 | 
			
		||||
      pic: null,
 | 
			
		||||
      pictures: [1],
 | 
			
		||||
      published: true,
 | 
			
		||||
      title: 'test',
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    expect(cleanData(data, contentTypeLayout, groupLayouts)).toEqual(expected);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('Content Manager | EditView | utils | helperCleanData', () => {
 | 
			
		||||
  let data;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    data = {
 | 
			
		||||
      test: 'something',
 | 
			
		||||
      object: {
 | 
			
		||||
        id: 1,
 | 
			
		||||
        test: 'test',
 | 
			
		||||
        other: 'test',
 | 
			
		||||
      },
 | 
			
		||||
      array: [
 | 
			
		||||
        {
 | 
			
		||||
          id: 2,
 | 
			
		||||
          test: 'test',
 | 
			
		||||
          other: 'test',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          id: 3,
 | 
			
		||||
          test: 'test1',
 | 
			
		||||
          other: 'test1',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          id: 4,
 | 
			
		||||
          test: 'test2',
 | 
			
		||||
          other: 'test2',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      other: 'super cool',
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
  it('should return the value if it is not an object', () => {
 | 
			
		||||
    expect(helperCleanData(data.test, 'id')).toEqual('something');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return the id of an object', () => {
 | 
			
		||||
    expect(helperCleanData(data.object, 'id')).toEqual(1);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return an array with the ids of each elements if an array of objects is given', () => {
 | 
			
		||||
    expect(helperCleanData(data.array, 'id')).toEqual([2, 3, 4]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return an array with the objects if the key does not exist', () => {
 | 
			
		||||
    expect(helperCleanData(data.array, 'something')).toEqual(data.array);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('Content Manager | EditView | utils | getMediasAttributes', () => {
 | 
			
		||||
  let contentTypeLayout;
 | 
			
		||||
  let grpLayouts;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    contentTypeLayout = ctLayout;
 | 
			
		||||
    grpLayouts = groupLayouts;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should return an array containing the paths of all the medias attributes', () => {
 | 
			
		||||
@ -119,6 +231,8 @@ describe('getMediasAttributes util', () => {
 | 
			
		||||
      pictures: { multiple: true, isGroup: false, repeatable: false },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    expect(getMediaAttributes(ctLayout, groupLayouts)).toMatchObject(expected);
 | 
			
		||||
    expect(getMediaAttributes(contentTypeLayout, grpLayouts)).toMatchObject(
 | 
			
		||||
      expected
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,74 @@
 | 
			
		||||
import { cloneDeep, get, isArray, isObject, set, unset } from 'lodash';
 | 
			
		||||
import { get, isArray, isEmpty, isObject } from 'lodash';
 | 
			
		||||
 | 
			
		||||
export const cleanData = (retrievedData, ctLayout, groupLayouts) => {
 | 
			
		||||
  const getType = (schema, attrName) =>
 | 
			
		||||
    get(schema, ['attributes', attrName, 'type'], '');
 | 
			
		||||
  const getOtherInfos = (schema, arr) =>
 | 
			
		||||
    get(schema, ['attributes', ...arr], '');
 | 
			
		||||
 | 
			
		||||
  const recursiveCleanData = (data, layout) => {
 | 
			
		||||
    return Object.keys(data).reduce((acc, current) => {
 | 
			
		||||
      const attrType = getType(layout.schema, current);
 | 
			
		||||
      const value = get(data, current);
 | 
			
		||||
      const group = getOtherInfos(layout.schema, [current, 'group']);
 | 
			
		||||
      const isRepeatable = getOtherInfos(layout.schema, [
 | 
			
		||||
        current,
 | 
			
		||||
        'repeatable',
 | 
			
		||||
      ]);
 | 
			
		||||
      let cleanedData;
 | 
			
		||||
 | 
			
		||||
      switch (attrType) {
 | 
			
		||||
        case 'json':
 | 
			
		||||
          cleanedData = value;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'date':
 | 
			
		||||
          cleanedData =
 | 
			
		||||
            value && value._isAMomentObject === true
 | 
			
		||||
              ? value.toISOString()
 | 
			
		||||
              : value;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'media':
 | 
			
		||||
          if (getOtherInfos(layout.schema, [current, 'multiple']) === true) {
 | 
			
		||||
            cleanedData = value
 | 
			
		||||
              ? helperCleanData(
 | 
			
		||||
                  value.filter(file => !(file instanceof File)),
 | 
			
		||||
                  'id'
 | 
			
		||||
                )
 | 
			
		||||
              : null;
 | 
			
		||||
          } else {
 | 
			
		||||
            cleanedData =
 | 
			
		||||
              get(value, 0) instanceof File ? null : get(value, 'id', null);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case 'group':
 | 
			
		||||
          if (isRepeatable) {
 | 
			
		||||
            cleanedData = value
 | 
			
		||||
              ? value.map(data => {
 | 
			
		||||
                  delete data._temp__id;
 | 
			
		||||
                  const subCleanedData = recursiveCleanData(
 | 
			
		||||
                    data,
 | 
			
		||||
                    groupLayouts[group]
 | 
			
		||||
                  );
 | 
			
		||||
 | 
			
		||||
                  return subCleanedData;
 | 
			
		||||
                })
 | 
			
		||||
              : value;
 | 
			
		||||
          } else {
 | 
			
		||||
            cleanedData = recursiveCleanData(value, groupLayouts[group]);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          cleanedData = helperCleanData(value, 'id');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      acc[current] = cleanedData;
 | 
			
		||||
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, {});
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return recursiveCleanData(retrievedData, ctLayout);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getMediaAttributes = (ctLayout, groupLayouts) => {
 | 
			
		||||
  const getMedia = (
 | 
			
		||||
@ -37,23 +107,7 @@ export const getMediaAttributes = (ctLayout, groupLayouts) => {
 | 
			
		||||
  return getMedia(ctLayout);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getFilesToUpload = (data, prefix = '') => {
 | 
			
		||||
  return Object.keys(data).reduce((acc, current) => {
 | 
			
		||||
    if (isObject(data[current]) && !isArray(data[current])) {
 | 
			
		||||
      return getFilesToUpload(data[current], current);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (get(data, [current]) instanceof File) {
 | 
			
		||||
      const path = prefix !== '' ? `${prefix}.${current}` : current;
 | 
			
		||||
 | 
			
		||||
      acc[path] = data[current];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return acc;
 | 
			
		||||
  }, {});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const helperCleanData = (value, key) => {
 | 
			
		||||
export const helperCleanData = (value, key) => {
 | 
			
		||||
  if (isArray(value)) {
 | 
			
		||||
    return value.map(obj => (obj[key] ? obj[key] : obj));
 | 
			
		||||
  } else if (isObject(value)) {
 | 
			
		||||
@ -63,95 +117,57 @@ const helperCleanData = (value, key) => {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const cleanData = (retrievedData, ctLayout, groupLayouts) => {
 | 
			
		||||
  const getType = (schema, attrName) =>
 | 
			
		||||
    get(schema, ['attributes', attrName, 'type'], '');
 | 
			
		||||
  const getOtherInfos = (schema, arr) =>
 | 
			
		||||
    get(schema, ['attributes', ...arr], '');
 | 
			
		||||
export const mapDataKeysToFilesToUpload = (filesMap, data) => {
 | 
			
		||||
  return Object.keys(filesMap).reduce((acc, current) => {
 | 
			
		||||
    const keys = current.split('.');
 | 
			
		||||
    const isMultiple = get(filesMap, [current, 'multiple'], false);
 | 
			
		||||
    const isGroup = get(filesMap, [current, 'isGroup'], false);
 | 
			
		||||
    const isRepeatable = get(filesMap, [current, 'repeatable'], false);
 | 
			
		||||
 | 
			
		||||
  const recursiveCleanData = (data, layout) => {
 | 
			
		||||
    return Object.keys(data).reduce((acc, current) => {
 | 
			
		||||
      const attrType = getType(layout.schema, current);
 | 
			
		||||
      const value = get(data, current);
 | 
			
		||||
      const group = getOtherInfos(layout.schema, [current, 'group']);
 | 
			
		||||
      const isRepeatable = getOtherInfos(layout.schema, [
 | 
			
		||||
        current,
 | 
			
		||||
        'repeatable',
 | 
			
		||||
      ]);
 | 
			
		||||
      let cleanedData;
 | 
			
		||||
    const getFilesToUpload = path => {
 | 
			
		||||
      const value = get(data, path, []) || [];
 | 
			
		||||
 | 
			
		||||
      switch (attrType) {
 | 
			
		||||
        case 'json':
 | 
			
		||||
          cleanedData = value;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'date':
 | 
			
		||||
          cleanedData =
 | 
			
		||||
            value && value._isAMomentObject === true
 | 
			
		||||
              ? value.toISOString()
 | 
			
		||||
              : value;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'media':
 | 
			
		||||
          if (getOtherInfos(layout.schema, [current, 'multiple'])) {
 | 
			
		||||
            cleanedData = value
 | 
			
		||||
              ? value.filter(file => !(file instanceof File))
 | 
			
		||||
              : null;
 | 
			
		||||
          } else {
 | 
			
		||||
            cleanedData =
 | 
			
		||||
              get(value, 0) instanceof File ? '' : get(value, 'id', null);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case 'group':
 | 
			
		||||
          if (isRepeatable) {
 | 
			
		||||
            cleanedData = value
 | 
			
		||||
              ? value.map(data => {
 | 
			
		||||
                  delete data._temp__id;
 | 
			
		||||
                  const subCleanedData = recursiveCleanData(
 | 
			
		||||
                    data,
 | 
			
		||||
                    groupLayouts[group]
 | 
			
		||||
                  );
 | 
			
		||||
 | 
			
		||||
                  return subCleanedData;
 | 
			
		||||
                })
 | 
			
		||||
              : value;
 | 
			
		||||
          } else {
 | 
			
		||||
            cleanedData = recursiveCleanData(value, groupLayouts[group]);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          cleanedData = helperCleanData(value, 'id');
 | 
			
		||||
      return value.filter(file => {
 | 
			
		||||
        return file instanceof File;
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    const getFileToUpload = path => {
 | 
			
		||||
      const file = get(data, [...path, 0], '');
 | 
			
		||||
      if (file instanceof File) {
 | 
			
		||||
        return [file];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      acc[current] = cleanedData;
 | 
			
		||||
      return [];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, {});
 | 
			
		||||
  };
 | 
			
		||||
    if (!isRepeatable) {
 | 
			
		||||
      const currentFilesToUpload = isMultiple
 | 
			
		||||
        ? getFilesToUpload(keys)
 | 
			
		||||
        : getFileToUpload([...keys]);
 | 
			
		||||
 | 
			
		||||
  return recursiveCleanData(retrievedData, ctLayout);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const associateFilesToData = (data, filesMap, uploadedFiles) => {
 | 
			
		||||
  const ret = cloneDeep(data);
 | 
			
		||||
 | 
			
		||||
  Object.keys(uploadedFiles).forEach(key => {
 | 
			
		||||
    const keys = key.split('.');
 | 
			
		||||
    const filesMapKey =
 | 
			
		||||
      keys.length > 2
 | 
			
		||||
        ? [keys[0], keys[2]]
 | 
			
		||||
        : [keys[0], keys[1]].filter(k => !!k);
 | 
			
		||||
    const isMultiple = get(filesMap, [...filesMapKey, 'multiple'], false);
 | 
			
		||||
    const cleanedValue = get(uploadedFiles, key, []).map(v =>
 | 
			
		||||
      helperCleanData(v, 'id')
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (isMultiple) {
 | 
			
		||||
      const previousFiles = get(data, key, []);
 | 
			
		||||
      set(ret, key, [...previousFiles, ...cleanedValue]);
 | 
			
		||||
    } else {
 | 
			
		||||
      unset(ret, key);
 | 
			
		||||
      set(ret, key, cleanedValue[0] || null);
 | 
			
		||||
      if (!isEmpty(currentFilesToUpload)) {
 | 
			
		||||
        acc[current] = currentFilesToUpload;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
    if (isGroup && isRepeatable) {
 | 
			
		||||
      const [key, targetKey] = current.split('.');
 | 
			
		||||
      const groupData = get(data, [key], []);
 | 
			
		||||
      const groupFiles = groupData.reduce((acc1, current, index) => {
 | 
			
		||||
        const files = isMultiple
 | 
			
		||||
          ? getFileToUpload([key, index, targetKey])
 | 
			
		||||
          : getFileToUpload([key, index, targetKey]);
 | 
			
		||||
 | 
			
		||||
        if (!isEmpty(files)) {
 | 
			
		||||
          acc1[`${key}.${index}.${targetKey}`] = files;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return acc1;
 | 
			
		||||
      }, {});
 | 
			
		||||
 | 
			
		||||
      return { ...acc, ...groupFiles };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return acc;
 | 
			
		||||
  }, {});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,15 +0,0 @@
 | 
			
		||||
import { isPlainObject, isFunction } from 'lodash';
 | 
			
		||||
 | 
			
		||||
export const bindLayout = function (object) {
 | 
			
		||||
  return Object.keys(object).reduce((acc, current) => {
 | 
			
		||||
    if (isPlainObject(object[current])) {
 | 
			
		||||
      acc[current] = bindLayout.call(this, object[current]);
 | 
			
		||||
    } else if (isFunction(object[current])) {
 | 
			
		||||
      acc[current] = object[current].bind(this);
 | 
			
		||||
    } else {
 | 
			
		||||
      acc[current] = object[current];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return acc;
 | 
			
		||||
  }, {});
 | 
			
		||||
};
 | 
			
		||||
@ -1,130 +0,0 @@
 | 
			
		||||
import {
 | 
			
		||||
  findIndex,
 | 
			
		||||
  forEach,
 | 
			
		||||
  includes,
 | 
			
		||||
  isArray,
 | 
			
		||||
  isBoolean,
 | 
			
		||||
  isEmpty,
 | 
			
		||||
  isNaN,
 | 
			
		||||
  isNull,
 | 
			
		||||
  isNumber,
 | 
			
		||||
  isObject,
 | 
			
		||||
  isUndefined,
 | 
			
		||||
  map,
 | 
			
		||||
  mapKeys,
 | 
			
		||||
  reject,
 | 
			
		||||
} from 'lodash';
 | 
			
		||||
 | 
			
		||||
/* eslint-disable consistent-return */
 | 
			
		||||
export function getValidationsFromForm(form, formValidations) {
 | 
			
		||||
  map(form, (value, key) => {
 | 
			
		||||
    // Check if the object
 | 
			
		||||
    if (isObject(value) && !isArray(value)) {
 | 
			
		||||
      forEach(value, subValue => {
 | 
			
		||||
        // Check if it has nestedInputs
 | 
			
		||||
        if (isArray(subValue) && value.type !== 'select') {
 | 
			
		||||
          return getValidationsFromForm(subValue, formValidations);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isArray(value) && value.type !== 'select') {
 | 
			
		||||
      return getValidationsFromForm(form[key], formValidations);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Push the target and the validation
 | 
			
		||||
    if (value.name) {
 | 
			
		||||
      formValidations.push({
 | 
			
		||||
        name: value.name,
 | 
			
		||||
        validations: value.validations,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return formValidations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function checkFormValidity(formData, formValidations) {
 | 
			
		||||
  const errors = [];
 | 
			
		||||
 | 
			
		||||
  forEach(formData, (value, key) => {
 | 
			
		||||
    const validationValue =
 | 
			
		||||
      formValidations[findIndex(formValidations, ['name', key])];
 | 
			
		||||
 | 
			
		||||
    if (!isUndefined(validationValue)) {
 | 
			
		||||
      const inputErrors = validate(value, validationValue.validations);
 | 
			
		||||
 | 
			
		||||
      if (!isEmpty(inputErrors)) {
 | 
			
		||||
        errors.push({ name: key, errors: inputErrors });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return errors;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function validate(value, validations) {
 | 
			
		||||
  let errors = [];
 | 
			
		||||
  // Handle i18n
 | 
			
		||||
  const requiredError = { id: 'content-manager.error.validation.required' };
 | 
			
		||||
  mapKeys(validations, (validationValue, validationKey) => {
 | 
			
		||||
    switch (validationKey) {
 | 
			
		||||
      case 'max':
 | 
			
		||||
        if (parseInt(value, 10) > validationValue) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.max' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'min':
 | 
			
		||||
        if (parseInt(value, 10) < validationValue) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.min' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'maxLength':
 | 
			
		||||
        if (value && value.length > validationValue) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.maxLength' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'minLength':
 | 
			
		||||
        if (value && value.length < validationValue) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.minLength' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'required':
 | 
			
		||||
        if (validationValue === true && value.length === 0) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.required' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'regex':
 | 
			
		||||
        if (!new RegExp(validationValue).test(value)) {
 | 
			
		||||
          errors.push({ id: 'content-manager.error.validation.regex' });
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'type':
 | 
			
		||||
        if (validationValue === 'json') {
 | 
			
		||||
          try {
 | 
			
		||||
            if (
 | 
			
		||||
              isObject(value) ||
 | 
			
		||||
              isBoolean(value) ||
 | 
			
		||||
              isNumber(value) ||
 | 
			
		||||
              isArray(value) ||
 | 
			
		||||
              isNaN(value) ||
 | 
			
		||||
              isNull(value)
 | 
			
		||||
            ) {
 | 
			
		||||
              value = JSON.parse(JSON.stringify(value));
 | 
			
		||||
            } else {
 | 
			
		||||
              errors.push({ id: 'content-manager.error.validation.json' });
 | 
			
		||||
            }
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            errors.push({ id: 'content-manager.error.validation.json' });
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (includes(errors, requiredError)) {
 | 
			
		||||
    errors = reject(errors, error => error !== requiredError);
 | 
			
		||||
  }
 | 
			
		||||
  return errors;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user