mirror of
https://github.com/wekan/wekan.git
synced 2025-04-22 13:07:17 -04:00
commit
e6c7463c03
6 changed files with 115 additions and 112 deletions
|
@ -400,7 +400,11 @@ template(name="exportBoard")
|
|||
li
|
||||
a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-csv'}}
|
||||
| {{_ 'export-board-csv'}} ' , '
|
||||
li
|
||||
a(href="{{exportScsvUrl}}", download="{{exportCsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-csv'}} ' ; '
|
||||
li
|
||||
a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
|
|
|
@ -512,6 +512,21 @@ BlazeComponent.extendComponent({
|
|||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
delimiter: ',',
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/export/csv',
|
||||
params,
|
||||
queryParams,
|
||||
);
|
||||
},
|
||||
exportScsvUrl() {
|
||||
const params = {
|
||||
boardId: Session.get('currentBoard'),
|
||||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
delimiter: ';',
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/export/csv',
|
||||
|
|
|
@ -167,25 +167,38 @@ if (Meteor.isServer) {
|
|||
const exporter = new Exporter(boardId);
|
||||
if (exporter.canExport(user) || impersonateDone) {
|
||||
if (impersonateDone) {
|
||||
// TODO: Checking for CSV or TSV export type does not work:
|
||||
// let exportType = 'export' + params.query.delimiter ? 'CSV' : 'TSV';
|
||||
// So logging export to CSV:
|
||||
let exportType = 'exportCSV';
|
||||
if( params.query.delimiter == "\t" ) {
|
||||
exportType = 'exportTSV';
|
||||
}
|
||||
ImpersonatedUsers.insert({
|
||||
adminId: adminId,
|
||||
boardId: boardId,
|
||||
reason: exportType,
|
||||
});
|
||||
}
|
||||
|
||||
body = params.query.delimiter
|
||||
? exporter.buildCsv(params.query.delimiter)
|
||||
: exporter.buildCsv();
|
||||
//'Content-Length': body.length,
|
||||
res.writeHead(200, {
|
||||
'Content-Type': params.query.delimiter ? 'text/csv' : 'text/tsv',
|
||||
});
|
||||
res.write(body);
|
||||
|
||||
let userLanguage = 'en';
|
||||
if (user && user.profile) {
|
||||
userLanguage = user.profile.language
|
||||
}
|
||||
|
||||
if( params.query.delimiter == "\t" ) {
|
||||
// TSV file
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/tsv',
|
||||
});
|
||||
}
|
||||
else {
|
||||
// CSV file (comma or semicolon)
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/csv; charset=utf-8',
|
||||
});
|
||||
// Adding UTF8 BOM to quick fix MS Excel issue
|
||||
// use Uint8Array to prevent from converting bytes to string
|
||||
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
|
||||
}
|
||||
res.write(exporter.buildCsv(params.query.delimiter, userLanguage));
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(403);
|
||||
|
|
|
@ -49,8 +49,13 @@ runOnServer(function() {
|
|||
isAdmin: true,
|
||||
});
|
||||
}
|
||||
|
||||
const exporterExcel = new ExporterExcel(boardId);
|
||||
|
||||
let userLanguage = 'en';
|
||||
if(user && user.profile){
|
||||
userLanguage = user.profile.language
|
||||
}
|
||||
|
||||
const exporterExcel = new ExporterExcel(boardId, userLanguage);
|
||||
if (exporterExcel.canExport(user) || impersonateDone) {
|
||||
if (impersonateDone) {
|
||||
ImpersonatedUsers.insert({
|
||||
|
|
|
@ -197,65 +197,43 @@ export class Exporter {
|
|||
return result;
|
||||
}
|
||||
|
||||
buildCsv(delimiter = ',') {
|
||||
buildCsv(userDelimiter = ',', userLanguage='en') {
|
||||
const result = this.build();
|
||||
const columnHeaders = [];
|
||||
const cardRows = [];
|
||||
|
||||
const papaconfig = {
|
||||
delimiter, // get parameter (was: auto-detect)
|
||||
worker: true,
|
||||
};
|
||||
|
||||
/*
|
||||
newline: "", // auto-detect
|
||||
quotes: true,
|
||||
quoteChar: '"',
|
||||
escapeChar: '"',
|
||||
delimiter: userDelimiter,
|
||||
header: true,
|
||||
transformHeader: undefined,
|
||||
dynamicTyping: false,
|
||||
preview: 0,
|
||||
encoding: "",
|
||||
comments: false,
|
||||
step: undefined,
|
||||
complete: undefined,
|
||||
error: undefined,
|
||||
download: false,
|
||||
downloadRequestHeaders: undefined,
|
||||
downloadRequestBody: undefined,
|
||||
skipEmptyLines: false,
|
||||
chunk: undefined,
|
||||
chunkSize: undefined,
|
||||
fastMode: undefined,
|
||||
beforeFirstChunk: undefined,
|
||||
withCredentials: undefined,
|
||||
transform: undefined
|
||||
newline: "\r\n",
|
||||
skipEmptyLines: false,
|
||||
escapeFormulae: true,
|
||||
};
|
||||
*/
|
||||
|
||||
//delimitersToGuess: [',', '\t', '|', ';', Papa.RECORD_SEP, Papa.UNIT_SEP]
|
||||
|
||||
columnHeaders.push(
|
||||
'Title',
|
||||
'Description',
|
||||
'Status',
|
||||
'Swimlane',
|
||||
'Owner',
|
||||
'Requested by',
|
||||
'Assigned by',
|
||||
'Members',
|
||||
'Assignees',
|
||||
'Labels',
|
||||
'Start at',
|
||||
'Due at',
|
||||
'End at',
|
||||
'Over time',
|
||||
'Spent time (hours)',
|
||||
'Created at',
|
||||
'Last modified at',
|
||||
'Last activity',
|
||||
'Vote',
|
||||
'Archived',
|
||||
TAPi18n.__('title','',userLanguage),
|
||||
TAPi18n.__('description','',userLanguage),
|
||||
TAPi18n.__('list','',userLanguage),
|
||||
TAPi18n.__('swimlane','',userLanguage),
|
||||
TAPi18n.__('owner','',userLanguage),
|
||||
TAPi18n.__('requested-by','',userLanguage),
|
||||
TAPi18n.__('assigned-by','',userLanguage),
|
||||
TAPi18n.__('members','',userLanguage),
|
||||
TAPi18n.__('assignee','',userLanguage),
|
||||
TAPi18n.__('labels','',userLanguage),
|
||||
TAPi18n.__('card-start','',userLanguage),
|
||||
TAPi18n.__('card-due','',userLanguage),
|
||||
TAPi18n.__('card-end','',userLanguage),
|
||||
TAPi18n.__('overtime-hours','',userLanguage),
|
||||
TAPi18n.__('spent-time-hours','',userLanguage),
|
||||
TAPi18n.__('createdAt','',userLanguage),
|
||||
TAPi18n.__('last-modified-at','',userLanguage),
|
||||
TAPi18n.__('last-activity','',userLanguage),
|
||||
TAPi18n.__('voting','',userLanguage),
|
||||
TAPi18n.__('archived','',userLanguage),
|
||||
);
|
||||
const customFieldMap = {};
|
||||
let i = 0;
|
||||
|
@ -283,30 +261,8 @@ export class Exporter {
|
|||
}
|
||||
i++;
|
||||
});
|
||||
cardRows.push([[columnHeaders]]);
|
||||
/* TODO: Try to get translations working.
|
||||
These currently only bring English translations.
|
||||
TAPi18n.__('title'),
|
||||
TAPi18n.__('description'),
|
||||
TAPi18n.__('status'),
|
||||
TAPi18n.__('swimlane'),
|
||||
TAPi18n.__('owner'),
|
||||
TAPi18n.__('requested-by'),
|
||||
TAPi18n.__('assigned-by'),
|
||||
TAPi18n.__('members'),
|
||||
TAPi18n.__('assignee'),
|
||||
TAPi18n.__('labels'),
|
||||
TAPi18n.__('card-start'),
|
||||
TAPi18n.__('card-due'),
|
||||
TAPi18n.__('card-end'),
|
||||
TAPi18n.__('overtime-hours'),
|
||||
TAPi18n.__('spent-time-hours'),
|
||||
TAPi18n.__('createdAt'),
|
||||
TAPi18n.__('last-modified-at'),
|
||||
TAPi18n.__('last-activity'),
|
||||
TAPi18n.__('voting'),
|
||||
TAPi18n.__('archived'),
|
||||
*/
|
||||
//cardRows.push([[columnHeaders]]);
|
||||
cardRows.push(columnHeaders);
|
||||
|
||||
result.cards.forEach((card) => {
|
||||
const currentRow = [];
|
||||
|
@ -409,7 +365,8 @@ export class Exporter {
|
|||
currentRow.push(customFieldValuesToPush[valueIndex]);
|
||||
}
|
||||
}
|
||||
cardRows.push([[currentRow]]);
|
||||
//cardRows.push([[currentRow]]);
|
||||
cardRows.push(currentRow);
|
||||
});
|
||||
|
||||
return Papa.unparse(cardRows, papaconfig);
|
||||
|
|
|
@ -3,8 +3,9 @@ import { createWorkbook } from './createWorkbook';
|
|||
// exporter maybe is broken since Gridfs introduced, add fs and path
|
||||
|
||||
class ExporterExcel {
|
||||
constructor(boardId) {
|
||||
constructor(boardId, userLanguage) {
|
||||
this._boardId = boardId;
|
||||
this.userLanguage = userLanguage;
|
||||
}
|
||||
|
||||
build(res) {
|
||||
|
@ -157,8 +158,8 @@ class ExporterExcel {
|
|||
|
||||
//init exceljs workbook
|
||||
const workbook = createWorkbook();
|
||||
workbook.creator = TAPi18n.__('export-board');
|
||||
workbook.lastModifiedBy = TAPi18n.__('export-board');
|
||||
workbook.creator = TAPi18n.__('export-board','',this.userLanguage);
|
||||
workbook.lastModifiedBy = TAPi18n.__('export-board','',this.userLanguage);
|
||||
workbook.created = new Date();
|
||||
workbook.modified = new Date();
|
||||
workbook.lastPrinted = new Date();
|
||||
|
@ -367,11 +368,11 @@ class ExporterExcel {
|
|||
ws.addRow().values = ['', '', '', '', '', ''];
|
||||
//add kanban info
|
||||
ws.addRow().values = [
|
||||
TAPi18n.__('createdAt'),
|
||||
TAPi18n.__('createdAt','',this.userLanguage),
|
||||
addTZhours(result.createdAt),
|
||||
TAPi18n.__('modifiedAt'),
|
||||
TAPi18n.__('modifiedAt','',this.userLanguage),
|
||||
addTZhours(result.modifiedAt),
|
||||
TAPi18n.__('members'),
|
||||
TAPi18n.__('members','',this.userLanguage),
|
||||
jmem,
|
||||
];
|
||||
ws.getRow(3).font = {
|
||||
|
@ -388,6 +389,14 @@ class ExporterExcel {
|
|||
},
|
||||
numFmt: 'yyyy/mm/dd hh:mm:ss',
|
||||
};
|
||||
ws.getCell('D3').style = {
|
||||
font: {
|
||||
name: TAPi18n.__('excel-font'),
|
||||
size: '10',
|
||||
bold: true,
|
||||
},
|
||||
numFmt: 'yyyy/mm/dd hh:mm:ss',
|
||||
};
|
||||
//cell center
|
||||
function cellCenter(cellno) {
|
||||
ws.getCell(cellno).alignment = {
|
||||
|
@ -455,24 +464,24 @@ class ExporterExcel {
|
|||
//ws.addRow().values = ['编号', '标题', '创建人', '创建时间', '更新时间', '列表', '成员', '描述', '标签'];
|
||||
//this is where order in which the excel file generates
|
||||
ws.addRow().values = [
|
||||
TAPi18n.__('number'),
|
||||
TAPi18n.__('title'),
|
||||
TAPi18n.__('description'),
|
||||
TAPi18n.__('parent-card'),
|
||||
TAPi18n.__('owner'),
|
||||
TAPi18n.__('createdAt'),
|
||||
TAPi18n.__('last-modified-at'),
|
||||
TAPi18n.__('card-received'),
|
||||
TAPi18n.__('card-start'),
|
||||
TAPi18n.__('card-due'),
|
||||
TAPi18n.__('card-end'),
|
||||
TAPi18n.__('list'),
|
||||
TAPi18n.__('swimlane'),
|
||||
TAPi18n.__('assignee'),
|
||||
TAPi18n.__('members'),
|
||||
TAPi18n.__('labels'),
|
||||
TAPi18n.__('overtime-hours'),
|
||||
TAPi18n.__('spent-time-hours'),
|
||||
TAPi18n.__('number','',this.userLanguage),
|
||||
TAPi18n.__('title','',this.userLanguage),
|
||||
TAPi18n.__('description','',this.userLanguage),
|
||||
TAPi18n.__('parent-card','',this.userLanguage),
|
||||
TAPi18n.__('owner','',this.userLanguage),
|
||||
TAPi18n.__('createdAt','',this.userLanguage),
|
||||
TAPi18n.__('last-modified-at','',this.userLanguage),
|
||||
TAPi18n.__('card-received','',this.userLanguage),
|
||||
TAPi18n.__('card-start','',this.userLanguage),
|
||||
TAPi18n.__('card-due','',this.userLanguage),
|
||||
TAPi18n.__('card-end','',this.userLanguage),
|
||||
TAPi18n.__('list','',this.userLanguage),
|
||||
TAPi18n.__('swimlane','',this.userLanguage),
|
||||
TAPi18n.__('assignee','',this.userLanguage),
|
||||
TAPi18n.__('members','',this.userLanguage),
|
||||
TAPi18n.__('labels','',this.userLanguage),
|
||||
TAPi18n.__('overtime-hours','',this.userLanguage),
|
||||
TAPi18n.__('spent-time-hours','',this.userLanguage),
|
||||
];
|
||||
ws.getRow(5).height = 20;
|
||||
allBorder('A5');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue