mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Relocate export urls to /download/
Summary: Moves CSV and XLSX export urls under /download/, and removes the document title query parameter which is now retrieved from the backend. Test Plan: No new tests. Existing tests that verify endpoints still function. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3010
This commit is contained in:
		
							parent
							
								
									cecebded1f
								
							
						
					
					
						commit
						0717ee627e
					
				@ -46,7 +46,7 @@ import {isSchemaAction} from 'app/common/DocActions';
 | 
			
		||||
import {OpenLocalDocResult} from 'app/common/DocListAPI';
 | 
			
		||||
import {HashLink, IDocPage} from 'app/common/gristUrls';
 | 
			
		||||
import {RecalcWhen} from 'app/common/gristTypes';
 | 
			
		||||
import {encodeQueryParams, undef, waitObs} from 'app/common/gutil';
 | 
			
		||||
import {undef, waitObs} from 'app/common/gutil';
 | 
			
		||||
import {LocalPlugin} from "app/common/plugin";
 | 
			
		||||
import {StringUnion} from 'app/common/StringUnion';
 | 
			
		||||
import {TableData} from 'app/common/TableData';
 | 
			
		||||
@ -625,29 +625,20 @@ export class GristDoc extends DisposableWithEvents {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getXlsxLink() {
 | 
			
		||||
    const baseUrl = this.docPageModel.appModel.api.getDocAPI(this.docId()).getGenerateXlsxUrl();
 | 
			
		||||
    const params = {
 | 
			
		||||
      title: this.docPageModel.currentDocTitle.get(),
 | 
			
		||||
    };
 | 
			
		||||
    return baseUrl + '?' + encodeQueryParams(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getCsvLink() {
 | 
			
		||||
    const filters = this.viewModel.activeSection.peek().filteredFields.get().map(field=> ({
 | 
			
		||||
      colRef : field.colRef.peek(),
 | 
			
		||||
      filter : field.activeFilter.peek()
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    const baseUrl = this.docPageModel.appModel.api.getDocAPI(this.docId()).getGenerateCsvUrl();
 | 
			
		||||
    const params = {
 | 
			
		||||
      title: this.docPageModel.currentDocTitle.get(),
 | 
			
		||||
      viewSection: this.viewModel.activeSectionId(),
 | 
			
		||||
      tableId: this.viewModel.activeSection().table().tableId(),
 | 
			
		||||
      activeSortSpec: JSON.stringify(this.viewModel.activeSection().activeSortSpec()),
 | 
			
		||||
      filters : JSON.stringify(filters),
 | 
			
		||||
    };
 | 
			
		||||
    return baseUrl + '?' + encodeQueryParams(params);
 | 
			
		||||
 | 
			
		||||
    return this.docPageModel.appModel.api.getDocAPI(this.docId()).getDownloadCsvUrl(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public hasGranularAccessRules(): boolean {
 | 
			
		||||
 | 
			
		||||
@ -223,8 +223,10 @@ function menuExports(doc: Document, pageModel: DocPageModel) {
 | 
			
		||||
    ),
 | 
			
		||||
    menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
 | 
			
		||||
      menuIcon('Download'), 'Export CSV', testId('tb-share-option')),
 | 
			
		||||
    menuItemLink({ href: gristDoc.getXlsxLink(), target: '_blank', download: ''},
 | 
			
		||||
      menuIcon('Download'), 'Export XLSX', testId('tb-share-option')),
 | 
			
		||||
    menuItemLink({
 | 
			
		||||
      href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadXlsxUrl(),
 | 
			
		||||
      target: '_blank', download: ''
 | 
			
		||||
    }, menuIcon('Download'), 'Export XLSX', testId('tb-share-option')),
 | 
			
		||||
    menuItem(() => sendToDrive(doc, pageModel),
 | 
			
		||||
      menuIcon('Download'), 'Send to Google Drive', testId('tb-share-option')),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import {FullUser} from 'app/common/LoginSessionAPI';
 | 
			
		||||
import {OrgPrefs, UserOrgPrefs, UserPrefs} from 'app/common/Prefs';
 | 
			
		||||
import * as roles from 'app/common/roles';
 | 
			
		||||
import {addCurrentOrgToPath} from 'app/common/urlUtils';
 | 
			
		||||
import {encodeQueryParams} from 'app/common/gutil';
 | 
			
		||||
 | 
			
		||||
export {FullUser} from 'app/common/LoginSessionAPI';
 | 
			
		||||
 | 
			
		||||
@ -320,6 +321,16 @@ export interface UserAPI {
 | 
			
		||||
  forRemoved(): UserAPI; // Get a version of the API that works on removed resources.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parameters for the download CSV endpoint (/download/csv).
 | 
			
		||||
 */
 | 
			
		||||
 export interface DownloadCsvParams {
 | 
			
		||||
  tableId: string;
 | 
			
		||||
  viewSection?: number;
 | 
			
		||||
  activeSortSpec?: string;
 | 
			
		||||
  filters?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Collect endpoints related to the content of a single document that we've been thinking
 | 
			
		||||
 * of as the (restful) "Doc API".  A few endpoints that could be here are not, for historical
 | 
			
		||||
@ -347,8 +358,8 @@ export interface DocAPI {
 | 
			
		||||
  // is HEAD, the result will contain a copy of any rows added or updated.
 | 
			
		||||
  compareVersion(leftHash: string, rightHash: string): Promise<DocStateComparison>;
 | 
			
		||||
  getDownloadUrl(template?: boolean): string;
 | 
			
		||||
  getGenerateXlsxUrl(): string;
 | 
			
		||||
  getGenerateCsvUrl(): string;
 | 
			
		||||
  getDownloadXlsxUrl(): string;
 | 
			
		||||
  getDownloadCsvUrl(params: DownloadCsvParams): string;
 | 
			
		||||
  /**
 | 
			
		||||
   * Exports current document to the Google Drive as a spreadsheet file. To invoke this method, first
 | 
			
		||||
   * acquire "code" via Google Auth Endpoint (see ShareMenu.ts for an example).
 | 
			
		||||
@ -792,12 +803,13 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
 | 
			
		||||
    return this._url + `/download?template=${Number(template)}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getGenerateXlsxUrl() {
 | 
			
		||||
    return this._url + '/gen-xlsx';
 | 
			
		||||
  public getDownloadXlsxUrl() {
 | 
			
		||||
    return this._url + '/download/xlsx';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getGenerateCsvUrl() {
 | 
			
		||||
    return this._url + '/gen-csv';
 | 
			
		||||
  public getDownloadCsvUrl(params: DownloadCsvParams) {
 | 
			
		||||
    // We spread `params` to work around TypeScript being overly cautious.
 | 
			
		||||
    return this._url + '/download/csv?' + encodeQueryParams({...params});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async sendToDrive(code: string, title: string): Promise<{url: string}> {
 | 
			
		||||
 | 
			
		||||
@ -42,8 +42,6 @@ export class DocApiForwarder {
 | 
			
		||||
    app.use('/api/docs/:docId/remove', withDoc);
 | 
			
		||||
    app.delete('/api/docs/:docId', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/download', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/gen-csv', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/gen-xlsx', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/send-to-drive', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/fork', withDoc);
 | 
			
		||||
    app.use('/api/docs/:docId/create-fork', withDoc);
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,9 @@ import { googleAuthTokenMiddleware } from "app/server/lib/GoogleAuth";
 | 
			
		||||
import * as _ from "lodash";
 | 
			
		||||
import {isRaisedException} from "app/common/gristTypes";
 | 
			
		||||
import {localeFromRequest} from "app/server/lib/ServerLocale";
 | 
			
		||||
import { generateCSV, generateXLSX } from "app/server/serverMethods";
 | 
			
		||||
import { downloadCSV, DownloadCSVOptions } from "app/server/lib/ExportCSV";
 | 
			
		||||
import { downloadXLSX, DownloadXLSXOptions } from "app/server/lib/ExportXLSX";
 | 
			
		||||
import { parseExportParameters } from "app/server/lib/Export";
 | 
			
		||||
 | 
			
		||||
// Cap on the number of requests that can be outstanding on a single document via the
 | 
			
		||||
// rest doc api.  When this limit is exceeded, incoming requests receive an immediate
 | 
			
		||||
@ -566,9 +568,31 @@ export class DocWorkerApi {
 | 
			
		||||
      res.json(result);
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    this._app.get('/api/docs/:docId/gen-csv', canView, withDoc(generateCSV));
 | 
			
		||||
    this._app.get('/api/docs/:docId/download/csv', canView, withDoc(async (activeDoc, req, res) => {
 | 
			
		||||
      // Query DB for doc metadata to get the doc title.
 | 
			
		||||
      const {name: docTitle} =
 | 
			
		||||
        await this._dbManager.getDoc({userId: getUserId(req), org: req.org, urlId: getDocId(req)});
 | 
			
		||||
 | 
			
		||||
    this._app.get('/api/docs/:docId/gen-xlsx', canView, withDoc(generateXLSX));
 | 
			
		||||
      const params = parseExportParameters(req);
 | 
			
		||||
      const filename = docTitle + (params.tableId === docTitle ? '' : '-' + params.tableId);
 | 
			
		||||
 | 
			
		||||
      const options: DownloadCSVOptions = {
 | 
			
		||||
        ...params,
 | 
			
		||||
        filename,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await downloadCSV(activeDoc, req, res, options);
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    this._app.get('/api/docs/:docId/download/xlsx', canView, withDoc(async (activeDoc, req, res) => {
 | 
			
		||||
      // Query DB for doc metadata to get the doc title (to use as the filename).
 | 
			
		||||
      const {name: filename} =
 | 
			
		||||
        await this._dbManager.getDoc({userId: getUserId(req), org: req.org, urlId: getDocId(req)});
 | 
			
		||||
 | 
			
		||||
      const options: DownloadXLSXOptions = {filename};
 | 
			
		||||
 | 
			
		||||
      await downloadXLSX(activeDoc, req, res, options);
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    this._app.get('/api/docs/:docId/send-to-drive', canView, decodeGoogleToken, withDoc(exportToDrive));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ import {DocumentSettings} from 'app/common/DocumentSettings';
 | 
			
		||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
 | 
			
		||||
import {RequestWithLogin} from 'app/server/lib/Authorizer';
 | 
			
		||||
import {docSessionFromRequest} from 'app/server/lib/DocSession';
 | 
			
		||||
import { integerParam, optJsonParam, stringParam} from 'app/server/lib/requestUtils';
 | 
			
		||||
import {optIntegerParam, optJsonParam, stringParam} from 'app/server/lib/requestUtils';
 | 
			
		||||
import {ServerColumnGetters} from 'app/server/lib/ServerColumnGetters';
 | 
			
		||||
import * as express from 'express';
 | 
			
		||||
import * as _ from 'underscore';
 | 
			
		||||
@ -72,7 +72,7 @@ export interface ExportData {
 | 
			
		||||
 */
 | 
			
		||||
export interface ExportParameters {
 | 
			
		||||
  tableId: string;
 | 
			
		||||
  viewSectionId: number;
 | 
			
		||||
  viewSectionId: number | undefined;
 | 
			
		||||
  sortOrder: number[];
 | 
			
		||||
  filters: Filter[];
 | 
			
		||||
}
 | 
			
		||||
@ -82,7 +82,7 @@ export interface ExportParameters {
 | 
			
		||||
 */
 | 
			
		||||
export function parseExportParameters(req: express.Request): ExportParameters {
 | 
			
		||||
  const tableId = stringParam(req.query.tableId);
 | 
			
		||||
  const viewSectionId = integerParam(req.query.viewSection);
 | 
			
		||||
  const viewSectionId = optIntegerParam(req.query.viewSection);
 | 
			
		||||
  const sortOrder = optJsonParam(req.query.activeSortSpec, []) as number[];
 | 
			
		||||
  const filters: Filter[] = optJsonParam(req.query.filters, []);
 | 
			
		||||
 | 
			
		||||
@ -94,20 +94,6 @@ export function parseExportParameters(req: express.Request): ExportParameters {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates the file name (without an extension) for exported table.
 | 
			
		||||
 * @param activeDoc ActiveDoc
 | 
			
		||||
 * @param req Request (with export params)
 | 
			
		||||
 */
 | 
			
		||||
export function parseExportFileName(activeDoc: ActiveDoc, req: express.Request) {
 | 
			
		||||
  const title = req.query.title;
 | 
			
		||||
  const tableId = req.query.tableId;
 | 
			
		||||
  const docName = title || activeDoc.docName;
 | 
			
		||||
  const name = docName +
 | 
			
		||||
    (tableId === docName ? '' : '-' + tableId);
 | 
			
		||||
  return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Makes assertion that value does exists or throws an error
 | 
			
		||||
function safe<T>(value: T, msg: string) {
 | 
			
		||||
  if (!value) { throw new Error(msg); }
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,54 @@
 | 
			
		||||
import {createFormatter} from 'app/common/ValueFormatter';
 | 
			
		||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
 | 
			
		||||
import {ExportData, exportSection, Filter} from 'app/server/lib/Export';
 | 
			
		||||
import {ExportData, exportSection, exportTable, Filter} from 'app/server/lib/Export';
 | 
			
		||||
import * as bluebird from 'bluebird';
 | 
			
		||||
import * as csv from 'csv';
 | 
			
		||||
import * as express from 'express';
 | 
			
		||||
import * as log from 'app/server/lib/log';
 | 
			
		||||
import * as contentDisposition from 'content-disposition';
 | 
			
		||||
 | 
			
		||||
export interface DownloadCSVOptions {
 | 
			
		||||
  filename: string;
 | 
			
		||||
  tableId: string;
 | 
			
		||||
  viewSectionId: number | undefined;
 | 
			
		||||
  filters: Filter[];
 | 
			
		||||
  sortOrder: number[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// promisify csv
 | 
			
		||||
bluebird.promisifyAll(csv);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns a csv stream that can be transformed or parsed.  See https://github.com/wdavidw/node-csv
 | 
			
		||||
 * for API details.
 | 
			
		||||
 * Converts `activeDoc` to a CSV and sends the converted data through `res`.
 | 
			
		||||
 */
 | 
			
		||||
export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
 | 
			
		||||
                                  res: express.Response, options: DownloadCSVOptions) {
 | 
			
		||||
  log.info('Generating .csv file...');
 | 
			
		||||
  const {filename, tableId, viewSectionId, filters, sortOrder} = options;
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const data = viewSectionId ?
 | 
			
		||||
      await makeCSVFromViewSection(activeDoc, viewSectionId, sortOrder, filters, req) :
 | 
			
		||||
      await makeCSVFromTable(activeDoc, tableId, req);
 | 
			
		||||
    res.set('Content-Type', 'text/csv');
 | 
			
		||||
    res.setHeader('Content-Disposition', contentDisposition(filename + '.csv'));
 | 
			
		||||
    res.send(data);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    log.error("Exporting to CSV has failed. Request url: %s", req.url, err);
 | 
			
		||||
    const errHtml =
 | 
			
		||||
      `<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
  <body>There was an unexpected error while generating a csv file.</body>
 | 
			
		||||
</html>
 | 
			
		||||
`;
 | 
			
		||||
    res.status(400).send(errHtml);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns a csv stream of a view section that can be transformed or parsed.
 | 
			
		||||
 *
 | 
			
		||||
 * See https://github.com/wdavidw/node-csv for API details.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
 | 
			
		||||
 * @param {Integer} viewSectionId - id of the viewsection to export.
 | 
			
		||||
@ -18,7 +56,7 @@ bluebird.promisifyAll(csv);
 | 
			
		||||
 * @param {Filter[]} filters (optional) - filters defined from ui.
 | 
			
		||||
 * @return {Promise<string>} Promise for the resulting CSV.
 | 
			
		||||
 */
 | 
			
		||||
export async function makeCSV(
 | 
			
		||||
export async function makeCSVFromViewSection(
 | 
			
		||||
  activeDoc: ActiveDoc,
 | 
			
		||||
  viewSectionId: number,
 | 
			
		||||
  sortOrder: number[],
 | 
			
		||||
@ -30,6 +68,31 @@ export async function makeCSV(
 | 
			
		||||
  return file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns a csv stream of a table that can be transformed or parsed.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
 | 
			
		||||
 * @param {Integer} tableId - id of the table to export.
 | 
			
		||||
 * @return {Promise<string>} Promise for the resulting CSV.
 | 
			
		||||
 */
 | 
			
		||||
export async function makeCSVFromTable(
 | 
			
		||||
  activeDoc: ActiveDoc,
 | 
			
		||||
  tableId: string,
 | 
			
		||||
  req: express.Request) {
 | 
			
		||||
 | 
			
		||||
  if (!activeDoc.docData) {
 | 
			
		||||
    throw new Error('No docData in active document');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Look up the table to make a CSV from.
 | 
			
		||||
  const tables = activeDoc.docData.getTable('_grist_Tables')!;
 | 
			
		||||
  const tableRef = tables.findRow('tableId', tableId);
 | 
			
		||||
 | 
			
		||||
  const data = await exportTable(activeDoc, tableRef, req);
 | 
			
		||||
  const file = convertToCsv(data);
 | 
			
		||||
  return file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertToCsv({
 | 
			
		||||
  rowIds,
 | 
			
		||||
  access,
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,37 @@ import {createExcelFormatter} from 'app/server/lib/ExcelFormatter';
 | 
			
		||||
import {ExportData, exportDoc} from 'app/server/lib/Export';
 | 
			
		||||
import {Alignment, Border, Fill, Workbook} from 'exceljs';
 | 
			
		||||
import * as express from 'express';
 | 
			
		||||
import * as log from 'app/server/lib/log';
 | 
			
		||||
import * as contentDisposition from 'content-disposition';
 | 
			
		||||
 | 
			
		||||
export interface DownloadXLSXOptions {
 | 
			
		||||
  filename: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts `activeDoc` to CSV and sends the converted data through `res`.
 | 
			
		||||
 */
 | 
			
		||||
export async function downloadXLSX(activeDoc: ActiveDoc, req: express.Request,
 | 
			
		||||
                                   res: express.Response, {filename}: DownloadXLSXOptions) {
 | 
			
		||||
  log.debug(`Generating .xlsx file`);
 | 
			
		||||
  try {
 | 
			
		||||
    const data = await makeXLSX(activeDoc, req);
 | 
			
		||||
    res.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
 | 
			
		||||
    res.setHeader('Content-Disposition', contentDisposition(filename + '.xlsx'));
 | 
			
		||||
    res.send(data);
 | 
			
		||||
    log.debug('XLSX file generated');
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    log.error("Exporting to XLSX has failed. Request url: %s", req.url, err);
 | 
			
		||||
    // send a generic information to client
 | 
			
		||||
    const errHtml =
 | 
			
		||||
      `<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
  <body>There was an unexpected error while generating a xlsx file.</body>
 | 
			
		||||
</html>
 | 
			
		||||
`;
 | 
			
		||||
    res.status(400).send(errHtml);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates excel document with all tables from an active Grist document.
 | 
			
		||||
 | 
			
		||||
@ -1,55 +0,0 @@
 | 
			
		||||
import {parseExportFileName, parseExportParameters} from 'app/server/lib/Export';
 | 
			
		||||
import {makeCSV} from 'app/server/lib/ExportCSV';
 | 
			
		||||
import {makeXLSX} from 'app/server/lib/ExportXLSX';
 | 
			
		||||
import * as log from 'app/server/lib/log';
 | 
			
		||||
import * as contentDisposition from 'content-disposition';
 | 
			
		||||
import * as express from 'express';
 | 
			
		||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
 | 
			
		||||
 | 
			
		||||
export async function generateCSV(activeDoc: ActiveDoc, req: express.Request, res: express.Response) {
 | 
			
		||||
  log.info('Generating .csv file...');
 | 
			
		||||
  const {
 | 
			
		||||
    viewSectionId,
 | 
			
		||||
    filters,
 | 
			
		||||
    sortOrder
 | 
			
		||||
  } = parseExportParameters(req);
 | 
			
		||||
 | 
			
		||||
  // Generate a decent name for the exported file.
 | 
			
		||||
  const name = parseExportFileName(activeDoc, req);
 | 
			
		||||
  try {
 | 
			
		||||
    const data = await makeCSV(activeDoc, viewSectionId, sortOrder, filters, req);
 | 
			
		||||
    res.set('Content-Type', 'text/csv');
 | 
			
		||||
    res.setHeader('Content-Disposition', contentDisposition(name + '.csv'));
 | 
			
		||||
    res.send(data);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    log.error("Exporting to CSV has failed. Request url: %s", req.url, err);
 | 
			
		||||
    const errHtml =
 | 
			
		||||
      `<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
  <body>There was an unexpected error while generating a csv file.</body>
 | 
			
		||||
</html>
 | 
			
		||||
`;
 | 
			
		||||
    res.status(400).send(errHtml);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function generateXLSX(activeDoc: ActiveDoc, req: express.Request, res: express.Response) {
 | 
			
		||||
  log.debug(`Generating .xlsx file`);
 | 
			
		||||
  try {
 | 
			
		||||
    const data = await makeXLSX(activeDoc, req);
 | 
			
		||||
    res.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
 | 
			
		||||
    res.setHeader('Content-Disposition', contentDisposition((req.query.title || activeDoc.docName) + '.xlsx'));
 | 
			
		||||
    res.send(data);
 | 
			
		||||
    log.debug('XLSX file generated');
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    log.error("Exporting to XLSX has failed. Request url: %s", req.url, err);
 | 
			
		||||
    // send a generic information to client
 | 
			
		||||
    const errHtml =
 | 
			
		||||
      `<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
  <body>There was an unexpected error while generating a xlsx file.</body>
 | 
			
		||||
</html>
 | 
			
		||||
`;
 | 
			
		||||
    res.status(400).send(errHtml);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user