mirror of
https://github.com/wekan/wekan.git
synced 2025-04-24 14:08:31 -04:00
enable exporting and importing custom fields.
This commit is contained in:
parent
94e47401cb
commit
51f52fee23
2 changed files with 150 additions and 31 deletions
|
@ -50,6 +50,7 @@ export class CsvCreator {
|
||||||
*/
|
*/
|
||||||
mapHeadertoCardFieldIndex(headerRow) {
|
mapHeadertoCardFieldIndex(headerRow) {
|
||||||
const index = {};
|
const index = {};
|
||||||
|
index.customFields = [];
|
||||||
for (let i = 0; i < headerRow.length; i++) {
|
for (let i = 0; i < headerRow.length; i++) {
|
||||||
switch (headerRow[i].trim().toLowerCase()) {
|
switch (headerRow[i].trim().toLowerCase()) {
|
||||||
case 'title':
|
case 'title':
|
||||||
|
@ -98,9 +99,50 @@ export class CsvCreator {
|
||||||
index.modifiedAt = i;
|
index.modifiedAt = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (headerRow[i].toLowerCase().startsWith('customfield')) {
|
||||||
|
if (headerRow[i].split('-')[2] === 'dropdown') {
|
||||||
|
index.customFields.push({
|
||||||
|
name: headerRow[i].split('-')[1],
|
||||||
|
type: headerRow[i].split('-')[2],
|
||||||
|
options: headerRow[i].split('-')[3].split('/'),
|
||||||
|
position: i,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
index.customFields.push({
|
||||||
|
name: headerRow[i].split('-')[1],
|
||||||
|
type: headerRow[i].split('-')[2],
|
||||||
|
position: i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.fieldIndex = index;
|
this.fieldIndex = index;
|
||||||
}
|
}
|
||||||
|
createCustomFields(boardId) {
|
||||||
|
this.fieldIndex.customFields.forEach(customField => {
|
||||||
|
let settings = {};
|
||||||
|
if (customField.type === 'dropdown') {
|
||||||
|
settings = {
|
||||||
|
dropdownItems: customField.options.map(option => {
|
||||||
|
return { _id: Random.id(6), name: option };
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
settings = {};
|
||||||
|
}
|
||||||
|
const id = CustomFields.direct.insert({
|
||||||
|
name: customField.name,
|
||||||
|
type: customField.type,
|
||||||
|
settings,
|
||||||
|
showOnCard: false,
|
||||||
|
automaticallyOnCard: false,
|
||||||
|
showLabelOnMiniCard: false,
|
||||||
|
boardIds: [boardId],
|
||||||
|
});
|
||||||
|
customField.id = id;
|
||||||
|
customField.settings = settings;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
createBoard(csvData) {
|
createBoard(csvData) {
|
||||||
const boardToCreate = {
|
const boardToCreate = {
|
||||||
|
@ -228,9 +270,10 @@ export class CsvCreator {
|
||||||
const cardToCreate = {
|
const cardToCreate = {
|
||||||
archived: false,
|
archived: false,
|
||||||
boardId,
|
boardId,
|
||||||
createdAt: csvData[i][this.fieldIndex.createdAt]
|
createdAt:
|
||||||
? this._now(new Date(csvData[i][this.fieldIndex.createdAt]))
|
csvData[i][this.fieldIndex.createdAt] !== ' ' || ''
|
||||||
: null,
|
? this._now(new Date(csvData[i][this.fieldIndex.createdAt]))
|
||||||
|
: null,
|
||||||
dateLastActivity: this._now(),
|
dateLastActivity: this._now(),
|
||||||
description: csvData[i][this.fieldIndex.description],
|
description: csvData[i][this.fieldIndex.description],
|
||||||
listId: this.lists[csvData[i][this.fieldIndex.stage]],
|
listId: this.lists[csvData[i][this.fieldIndex.stage]],
|
||||||
|
@ -238,20 +281,24 @@ export class CsvCreator {
|
||||||
sort: -1,
|
sort: -1,
|
||||||
title: csvData[i][this.fieldIndex.title],
|
title: csvData[i][this.fieldIndex.title],
|
||||||
userId: this._user(),
|
userId: this._user(),
|
||||||
startAt: csvData[i][this.fieldIndex.startAt]
|
startAt:
|
||||||
? this._now(new Date(csvData[i][this.fieldIndex.startAt]))
|
csvData[i][this.fieldIndex.startAt] !== ' ' || ''
|
||||||
: null,
|
? this._now(new Date(csvData[i][this.fieldIndex.startAt]))
|
||||||
dueAt: csvData[i][this.fieldIndex.dueAt]
|
: null,
|
||||||
? this._now(new Date(csvData[i][this.fieldIndex.dueAt]))
|
dueAt:
|
||||||
: null,
|
csvData[i][this.fieldIndex.dueAt] !== ' ' || ''
|
||||||
endAt: csvData[i][this.fieldIndex.endAt]
|
? this._now(new Date(csvData[i][this.fieldIndex.dueAt]))
|
||||||
? this._now(new Date(csvData[i][this.fieldIndex.endAt]))
|
: null,
|
||||||
: null,
|
endAt:
|
||||||
|
csvData[i][this.fieldIndex.endAt] !== ' ' || ''
|
||||||
|
? this._now(new Date(csvData[i][this.fieldIndex.endAt]))
|
||||||
|
: null,
|
||||||
spentTime: null,
|
spentTime: null,
|
||||||
labelIds: [],
|
labelIds: [],
|
||||||
modifiedAt: csvData[i][this.fieldIndex.modifiedAt]
|
modifiedAt:
|
||||||
? this._now(new Date(csvData[i][this.fieldIndex.modifiedAt]))
|
csvData[i][this.fieldIndex.modifiedAt] !== ' ' || ''
|
||||||
: null,
|
? this._now(new Date(csvData[i][this.fieldIndex.modifiedAt]))
|
||||||
|
: null,
|
||||||
};
|
};
|
||||||
// add the labels
|
// add the labels
|
||||||
if (csvData[i][this.fieldIndex.labels]) {
|
if (csvData[i][this.fieldIndex.labels]) {
|
||||||
|
@ -290,7 +337,29 @@ export class CsvCreator {
|
||||||
cardToCreate.members = wekanMembers;
|
cardToCreate.members = wekanMembers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cards.direct.insert(cardToCreate);
|
// add the custom fields
|
||||||
|
if (this.fieldIndex.customFields.length > 0) {
|
||||||
|
const customFields = [];
|
||||||
|
this.fieldIndex.customFields.forEach(customField => {
|
||||||
|
if (csvData[i][customField.position] !== ' ') {
|
||||||
|
if (customField.type === 'dropdown') {
|
||||||
|
customFields.push({
|
||||||
|
_id: customField.id,
|
||||||
|
value: customField.settings.dropdownItems.find(
|
||||||
|
({ name }) => name === csvData[i][customField.position],
|
||||||
|
)._id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
customFields.push({
|
||||||
|
_id: customField.id,
|
||||||
|
value: csvData[i][customField.position],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cardToCreate.customFields = customFields;
|
||||||
|
});
|
||||||
|
Cards.direct.insert(cardToCreate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +376,7 @@ export class CsvCreator {
|
||||||
const boardId = this.createBoard(board);
|
const boardId = this.createBoard(board);
|
||||||
this.createLists(board, boardId);
|
this.createLists(board, boardId);
|
||||||
this.createSwimlanes(boardId);
|
this.createSwimlanes(boardId);
|
||||||
|
this.createCustomFields(boardId);
|
||||||
this.createCards(board, boardId);
|
this.createCards(board, boardId);
|
||||||
return boardId;
|
return boardId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ export class Exporter {
|
||||||
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
|
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
|
||||||
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
|
result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
|
||||||
result.customFields = CustomFields.find(
|
result.customFields = CustomFields.find(
|
||||||
{ boardIds: { $in: [this.boardId] } },
|
{ boardIds: this.boardId },
|
||||||
{ fields: { boardId: 0 } },
|
{ fields: { boardIds: 0 } },
|
||||||
).fetch();
|
).fetch();
|
||||||
result.comments = CardComments.find(byBoard, noBoardId).fetch();
|
result.comments = CardComments.find(byBoard, noBoardId).fetch();
|
||||||
result.activities = Activities.find(byBoard, noBoardId).fetch();
|
result.activities = Activities.find(byBoard, noBoardId).fetch();
|
||||||
|
@ -214,7 +214,29 @@ export class Exporter {
|
||||||
'Vote',
|
'Vote',
|
||||||
'Archived',
|
'Archived',
|
||||||
);
|
);
|
||||||
|
const customFieldMap = {};
|
||||||
|
let i = 0;
|
||||||
|
result.customFields.forEach(customField => {
|
||||||
|
customFieldMap[customField._id] = i;
|
||||||
|
customFieldMap[customField._id] = {
|
||||||
|
position: i,
|
||||||
|
type: customField.type,
|
||||||
|
};
|
||||||
|
if (customField.type === 'dropdown') {
|
||||||
|
let options = '';
|
||||||
|
customField.settings.dropdownItems.forEach(item => {
|
||||||
|
options = options === '' ? item.name : `/${options + item.name}`;
|
||||||
|
});
|
||||||
|
columnHeaders.push(
|
||||||
|
`CustomField-${customField.name}-${customField.type}-${options}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
columnHeaders.push(
|
||||||
|
`CustomField-${customField.name}-${customField.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
});
|
||||||
/* TODO: Try to get translations working.
|
/* TODO: Try to get translations working.
|
||||||
These currently only bring English translations.
|
These currently only bring English translations.
|
||||||
TAPi18n.__('title'),
|
TAPi18n.__('title'),
|
||||||
|
@ -290,21 +312,15 @@ export class Exporter {
|
||||||
labels = `${labels + label.name}-${label.color} `;
|
labels = `${labels + label.name}-${label.color} `;
|
||||||
});
|
});
|
||||||
currentRow.push(labels.trim());
|
currentRow.push(labels.trim());
|
||||||
currentRow.push(card.startAt ? moment(card.startAt).format('LLLL') : ' ');
|
currentRow.push(card.startAt ? moment(card.startAt).format() : ' ');
|
||||||
currentRow.push(card.dueAt ? moment(card.dueAt).format('LLLL') : ' ');
|
currentRow.push(card.dueAt ? moment(card.dueAt).format() : ' ');
|
||||||
currentRow.push(card.endAt ? moment(card.endAt).format('LLLL') : ' ');
|
currentRow.push(card.endAt ? moment(card.endAt).format() : ' ');
|
||||||
currentRow.push(card.isOvertime ? 'true' : 'false');
|
currentRow.push(card.isOvertime ? 'true' : 'false');
|
||||||
currentRow.push(card.spentTime);
|
currentRow.push(card.spentTime);
|
||||||
|
currentRow.push(card.createdAt ? moment(card.createdAt).format() : ' ');
|
||||||
|
currentRow.push(card.modifiedAt ? moment(card.modifiedAt).format() : ' ');
|
||||||
currentRow.push(
|
currentRow.push(
|
||||||
card.createdAt ? moment(card.createdAt).format('LLLL') : ' ',
|
card.dateLastActivity ? moment(card.dateLastActivity).format() : ' ',
|
||||||
);
|
|
||||||
currentRow.push(
|
|
||||||
card.modifiedAt ? moment(card.modifiedAt).format('LLLL') : ' ',
|
|
||||||
);
|
|
||||||
currentRow.push(
|
|
||||||
card.dateLastActivity
|
|
||||||
? moment(card.dateLastActivity).format('LLLL')
|
|
||||||
: ' ',
|
|
||||||
);
|
);
|
||||||
if (card.vote.question) {
|
if (card.vote.question) {
|
||||||
let positiveVoters = '';
|
let positiveVoters = '';
|
||||||
|
@ -331,6 +347,39 @@ export class Exporter {
|
||||||
currentRow.push(' ');
|
currentRow.push(' ');
|
||||||
}
|
}
|
||||||
currentRow.push(card.archived ? 'true' : 'false');
|
currentRow.push(card.archived ? 'true' : 'false');
|
||||||
|
//Custom fields
|
||||||
|
const customFieldValuesToPush = new Array(result.customFields.length);
|
||||||
|
card.customFields.forEach(field => {
|
||||||
|
if (customFieldMap[field._id].type === 'date') {
|
||||||
|
customFieldValuesToPush[customFieldMap[field._id].position] = moment(
|
||||||
|
field.value,
|
||||||
|
).format();
|
||||||
|
} else if (customFieldMap[field._id].type === 'dropdown') {
|
||||||
|
const dropdownOptions = result.customFields.find(
|
||||||
|
({ _id }) => _id === field._id,
|
||||||
|
).settings.dropdownItems;
|
||||||
|
const fieldValue = dropdownOptions.find(
|
||||||
|
({ _id }) => _id === field.value,
|
||||||
|
).name;
|
||||||
|
customFieldValuesToPush[
|
||||||
|
customFieldMap[field._id].position
|
||||||
|
] = fieldValue;
|
||||||
|
} else {
|
||||||
|
customFieldValuesToPush[customFieldMap[field._id].position] =
|
||||||
|
field.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (
|
||||||
|
let valueIndex = 0;
|
||||||
|
valueIndex < customFieldValuesToPush.length;
|
||||||
|
valueIndex++
|
||||||
|
) {
|
||||||
|
if (!(valueIndex in customFieldValuesToPush)) {
|
||||||
|
currentRow.push(' ');
|
||||||
|
} else {
|
||||||
|
currentRow.push(customFieldValuesToPush[valueIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
stringifier.write(currentRow);
|
stringifier.write(currentRow);
|
||||||
});
|
});
|
||||||
stringifier.end();
|
stringifier.end();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue