mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) updates from grist-core
This commit is contained in:
commit
f32563e8fb
@ -85,7 +85,7 @@ import {AccessTokenOptions, AccessTokenResult, GristDocAPI, UIRowId} from 'app/p
|
||||
import {compileAclFormula} from 'app/server/lib/ACLFormula';
|
||||
import {AssistanceSchemaPromptV1Context} from 'app/server/lib/Assistance';
|
||||
import {AssistanceContext} from 'app/common/AssistancePrompts';
|
||||
import {Authorizer} from 'app/server/lib/Authorizer';
|
||||
import {Authorizer, RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||
import {checksumFile} from 'app/server/lib/checksumFile';
|
||||
import {Client} from 'app/server/lib/Client';
|
||||
import {DEFAULT_CACHE_TTL, DocManager} from 'app/server/lib/DocManager';
|
||||
@ -141,6 +141,7 @@ import remove = require('lodash/remove');
|
||||
import sum = require('lodash/sum');
|
||||
import without = require('lodash/without');
|
||||
import zipObject = require('lodash/zipObject');
|
||||
import { getMetaTables } from './DocApi';
|
||||
|
||||
bluebird.promisifyAll(tmp);
|
||||
|
||||
@ -2791,7 +2792,6 @@ export function tableIdToRef(metaTables: { [p: string]: TableDataAction }, table
|
||||
|
||||
// Helper that converts a Grist column colId to a ref given the corresponding table.
|
||||
export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId: string, colId: string) {
|
||||
|
||||
const tableRef = tableIdToRef(metaTables, tableId);
|
||||
|
||||
const [, , colRefs, columnData] = metaTables._grist_Tables_column;
|
||||
@ -2804,6 +2804,31 @@ export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId:
|
||||
return colRefs[colRowIndex];
|
||||
}
|
||||
|
||||
// Helper that check if tableRef is used instead of tableId and return real tableId
|
||||
// If metaTables is not define, activeDoc and req allow it to be created
|
||||
interface MetaTables {
|
||||
metaTables: { [p: string]: TableDataAction }
|
||||
}
|
||||
interface ActiveDocAndReq {
|
||||
activeDoc: ActiveDoc, req: RequestWithLogin
|
||||
}
|
||||
export async function getRealTableId(
|
||||
tableId: string,
|
||||
options: MetaTables | ActiveDocAndReq
|
||||
): Promise<string> {
|
||||
if (parseInt(tableId)) {
|
||||
const metaTables = "metaTables" in options
|
||||
? options.metaTables
|
||||
: await getMetaTables(options.activeDoc, options.req);
|
||||
const [, , tableRefs, tableData] = metaTables._grist_Tables;
|
||||
if (tableRefs.indexOf(parseInt(tableId)) >= 0) {
|
||||
const tableRowIndex = tableRefs.indexOf(parseInt(tableId));
|
||||
return tableData.tableId[tableRowIndex]!.toString();
|
||||
}
|
||||
}
|
||||
return tableId;
|
||||
}
|
||||
|
||||
export function sanitizeApplyUAOptions(options?: ApplyUAOptions): ApplyUAOptions {
|
||||
return pick(options||{}, ['desc', 'otherId', 'linkId', 'parseStrings']);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import {
|
||||
TableOperationsImpl,
|
||||
TableOperationsPlatform
|
||||
} from 'app/plugin/TableOperationsImpl';
|
||||
import {ActiveDoc, colIdToRef as colIdToReference, tableIdToRef} from "app/server/lib/ActiveDoc";
|
||||
import {ActiveDoc, colIdToRef as colIdToReference, getRealTableId, tableIdToRef} from "app/server/lib/ActiveDoc";
|
||||
import {appSettings} from "app/server/lib/AppSettings";
|
||||
import {sendForCompletion} from 'app/server/lib/Assistance';
|
||||
import {
|
||||
@ -201,7 +201,7 @@ export class DocWorkerApi {
|
||||
if (!Object.keys(filters).every(col => Array.isArray(filters[col]))) {
|
||||
throw new ApiError("Invalid query: filter values must be arrays", 400);
|
||||
}
|
||||
const tableId = optTableId || req.params.tableId;
|
||||
const tableId = await getRealTableId(optTableId || req.params.tableId, {activeDoc, req});
|
||||
const session = docSessionFromRequest(req);
|
||||
const {tableData} = await handleSandboxError(tableId, [], activeDoc.fetchQuery(
|
||||
session, {tableId, filters}, !immediate));
|
||||
@ -262,11 +262,6 @@ export class DocWorkerApi {
|
||||
})
|
||||
);
|
||||
|
||||
async function getMetaTables(activeDoc: ActiveDoc, req: RequestWithLogin) {
|
||||
return await handleSandboxError("", [],
|
||||
activeDoc.fetchMetaTables(docSessionFromRequest(req)));
|
||||
}
|
||||
|
||||
const registerWebhook = async (activeDoc: ActiveDoc, req: RequestWithLogin, webhook: WebhookFields) => {
|
||||
const {fields, url} = await getWebhookSettings(activeDoc, req, null, webhook);
|
||||
if (!fields.eventTypes?.length) {
|
||||
@ -337,7 +332,8 @@ export class DocWorkerApi {
|
||||
const trigger = webhookId ? activeDoc.triggers.getWebhookTriggerRecord(webhookId) : undefined;
|
||||
let currentTableId = trigger ? tablesTable.getValue(trigger.tableRef, 'tableId')! : undefined;
|
||||
const {url, eventTypes, isReadyColumn, name} = webhook;
|
||||
const tableId = req.params.tableId || webhook.tableId;
|
||||
const tableId = await getRealTableId(req.params.tableId || webhook.tableId, {metaTables});
|
||||
|
||||
const fields: Partial<SchemaTypes['_grist_Triggers']> = {};
|
||||
|
||||
if (url && !isUrlAllowed(url)) {
|
||||
@ -387,7 +383,7 @@ export class DocWorkerApi {
|
||||
// Get the columns of the specified table in recordish format
|
||||
this._app.get('/api/docs/:docId/tables/:tableId/columns', canView,
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tableId = req.params.tableId;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const includeHidden = isAffirmative(req.query.hidden);
|
||||
const columns = await handleSandboxError('', [],
|
||||
activeDoc.getTableCols(docSessionFromRequest(req), tableId, includeHidden));
|
||||
@ -498,7 +494,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const colValues = req.body as BulkColValues;
|
||||
const count = colValues[Object.keys(colValues)[0]].length;
|
||||
const op = getTableOperations(req, activeDoc);
|
||||
const op = await getTableOperations(req, activeDoc);
|
||||
const ids = await op.addRecords(count, colValues);
|
||||
res.json(ids);
|
||||
})
|
||||
@ -527,7 +523,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
}
|
||||
validateCore(RecordsPost, req, body);
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
const records = await ops.create(body.records);
|
||||
res.json({records});
|
||||
})
|
||||
@ -558,7 +554,7 @@ export class DocWorkerApi {
|
||||
this._app.post('/api/docs/:docId/tables/:tableId/columns', canEdit, validate(ColumnsPost),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const body = req.body as Types.ColumnsPost;
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const actions = body.columns.map(({fields, id: colId}) =>
|
||||
// AddVisibleColumn adds the column to all widgets of the table.
|
||||
// This isn't necessarily what the user wants, but it seems like a good default.
|
||||
@ -590,7 +586,7 @@ export class DocWorkerApi {
|
||||
|
||||
this._app.post('/api/docs/:docId/tables/:tableId/data/delete', canEdit, withDoc(async (activeDoc, req, res) => {
|
||||
const rowIds = req.body;
|
||||
const op = getTableOperations(req, activeDoc);
|
||||
const op = await getTableOperations(req, activeDoc);
|
||||
await op.destroy(rowIds);
|
||||
res.json(null);
|
||||
}));
|
||||
@ -659,7 +655,7 @@ export class DocWorkerApi {
|
||||
const rowIds = columnValues.id;
|
||||
// sandbox expects no id column
|
||||
delete columnValues.id;
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
await ops.updateRecords(columnValues, rowIds);
|
||||
res.json(null);
|
||||
})
|
||||
@ -669,7 +665,7 @@ export class DocWorkerApi {
|
||||
this._app.patch('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPatch),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const body = req.body as Types.RecordsPatch;
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
await ops.update(body.records);
|
||||
res.json(null);
|
||||
})
|
||||
@ -680,7 +676,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
||||
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const tableRef = tablesTable.findMatchingRowId({tableId});
|
||||
if (!tableRef) {
|
||||
throw new ApiError(`Table not found "${tableId}"`, 404);
|
||||
@ -693,7 +689,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
return {...col, id};
|
||||
});
|
||||
const ops = getTableOperations(req, activeDoc, "_grist_Tables_column");
|
||||
const ops = await getTableOperations(req, activeDoc, "_grist_Tables_column");
|
||||
await ops.update(columns);
|
||||
res.json(null);
|
||||
})
|
||||
@ -711,7 +707,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
return {...table, id};
|
||||
});
|
||||
const ops = getTableOperations(req, activeDoc, "_grist_Tables");
|
||||
const ops = await getTableOperations(req, activeDoc, "_grist_Tables");
|
||||
await ops.update(tables);
|
||||
res.json(null);
|
||||
})
|
||||
@ -720,7 +716,7 @@ export class DocWorkerApi {
|
||||
// Add or update records given in records format
|
||||
this._app.put('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPut),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
const body = req.body as Types.RecordsPut;
|
||||
const options = {
|
||||
add: !isAffirmative(req.query.noadd),
|
||||
@ -740,7 +736,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
||||
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const tableRef = tablesTable.findMatchingRowId({tableId});
|
||||
if (!tableRef) {
|
||||
throw new ApiError(`Table not found "${tableId}"`, 404);
|
||||
@ -785,7 +781,8 @@ export class DocWorkerApi {
|
||||
|
||||
this._app.delete('/api/docs/:docId/tables/:tableId/columns/:colId', canEdit,
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const {tableId, colId} = req.params;
|
||||
const {colId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const actions = [ [ 'RemoveColumn', tableId, colId ] ];
|
||||
await handleSandboxError(tableId, [colId],
|
||||
activeDoc.applyUserActions(docSessionFromRequest(req), actions)
|
||||
@ -1205,12 +1202,13 @@ export class DocWorkerApi {
|
||||
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: docTitle} = await this._dbManager.getDoc(req);
|
||||
const options = !_.isEmpty(req.query) ? this._getDownloadOptions(req, docTitle) : {
|
||||
const options: DownloadOptions = !_.isEmpty(req.query) ? this._getDownloadOptions(req, docTitle) : {
|
||||
filename: docTitle,
|
||||
tableId: '',
|
||||
viewSectionId: undefined,
|
||||
filters: [],
|
||||
sortOrder: [],
|
||||
header: 'label'
|
||||
};
|
||||
await downloadXLSX(activeDoc, req, res, options);
|
||||
}));
|
||||
@ -1941,12 +1939,21 @@ function getErrorPlatform(tableId: string): TableOperationsPlatform {
|
||||
};
|
||||
}
|
||||
|
||||
function getTableOperations(req: RequestWithLogin, activeDoc: ActiveDoc, tableId?: string): TableOperationsImpl {
|
||||
export async function getMetaTables(activeDoc: ActiveDoc, req: RequestWithLogin) {
|
||||
return await handleSandboxError("", [],
|
||||
activeDoc.fetchMetaTables(docSessionFromRequest(req)));
|
||||
}
|
||||
|
||||
async function getTableOperations(
|
||||
req: RequestWithLogin,
|
||||
activeDoc: ActiveDoc,
|
||||
tableId?: string): Promise<TableOperationsImpl> {
|
||||
const options: OpOptions = {
|
||||
parseStrings: !isAffirmative(req.query.noparse)
|
||||
};
|
||||
const realTableId = await getRealTableId(tableId ?? req.params.tableId, {activeDoc, req});
|
||||
const platform: TableOperationsPlatform = {
|
||||
...getErrorPlatform(tableId ?? req.params.tableId),
|
||||
...getErrorPlatform(realTableId),
|
||||
applyUserActions(actions, opts) {
|
||||
if (!activeDoc) { throw new Error('no document'); }
|
||||
return activeDoc.applyUserActions(
|
||||
|
@ -17,7 +17,7 @@ import {BaseFormatter, createFullFormatterFromDocData} from 'app/common/ValueFor
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||
import {docSessionFromRequest} from 'app/server/lib/DocSession';
|
||||
import {optIntegerParam, optJsonParam, stringParam} from 'app/server/lib/requestUtils';
|
||||
import {optIntegerParam, optJsonParam, optStringParam, stringParam} from 'app/server/lib/requestUtils';
|
||||
import {ServerColumnGetters} from 'app/server/lib/ServerColumnGetters';
|
||||
import * as express from 'express';
|
||||
import * as _ from 'underscore';
|
||||
@ -90,6 +90,8 @@ export interface ExportData {
|
||||
docSettings: DocumentSettings;
|
||||
}
|
||||
|
||||
export type ExportHeader = 'colId' | 'label';
|
||||
|
||||
/**
|
||||
* Export parameters that identifies a section, filters, sort order.
|
||||
*/
|
||||
@ -99,6 +101,7 @@ export interface ExportParameters {
|
||||
sortOrder?: number[];
|
||||
filters?: Filter[];
|
||||
linkingFilter?: FilterColValues;
|
||||
header?: ExportHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,6 +120,7 @@ export function parseExportParameters(req: express.Request): ExportParameters {
|
||||
const sortOrder = optJsonParam(req.query.activeSortSpec, []) as number[];
|
||||
const filters: Filter[] = optJsonParam(req.query.filters, []);
|
||||
const linkingFilter: FilterColValues = optJsonParam(req.query.linkingFilter, null);
|
||||
const header = optStringParam(req.query.header, 'header', {allowed: ['label', 'colId']}) as ExportHeader | undefined;
|
||||
|
||||
return {
|
||||
tableId,
|
||||
@ -124,6 +128,7 @@ export function parseExportParameters(req: express.Request): ExportParameters {
|
||||
sortOrder,
|
||||
filters,
|
||||
linkingFilter,
|
||||
header,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {ApiError} from 'app/common/ApiError';
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {FilterColValues} from "app/common/ActiveDocAPI";
|
||||
import {DownloadOptions, ExportData, exportSection, exportTable, Filter} from 'app/server/lib/Export';
|
||||
import {DownloadOptions, ExportData, ExportHeader, exportSection, exportTable, Filter} from 'app/server/lib/Export';
|
||||
import log from 'app/server/lib/log';
|
||||
import * as bluebird from 'bluebird';
|
||||
import contentDisposition from 'content-disposition';
|
||||
@ -17,11 +17,13 @@ bluebird.promisifyAll(csv);
|
||||
export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
|
||||
res: express.Response, options: DownloadOptions) {
|
||||
log.info('Generating .csv file...');
|
||||
const {filename, tableId, viewSectionId, filters, sortOrder, linkingFilter} = options;
|
||||
const {filename, tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
|
||||
const data = viewSectionId ?
|
||||
await makeCSVFromViewSection(
|
||||
activeDoc, viewSectionId, sortOrder || null, filters || null, linkingFilter || null, req) :
|
||||
await makeCSVFromTable(activeDoc, tableId, req);
|
||||
await makeCSVFromViewSection({
|
||||
activeDoc, viewSectionId, sortOrder: sortOrder || null, filters: filters || null,
|
||||
linkingFilter: linkingFilter || null, header, req
|
||||
}) :
|
||||
await makeCSVFromTable({activeDoc, tableId, header, req});
|
||||
res.set('Content-Type', 'text/csv');
|
||||
res.setHeader('Content-Disposition', contentDisposition(filename + '.csv'));
|
||||
res.send(data);
|
||||
@ -32,36 +34,51 @@ export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
|
||||
*
|
||||
* 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.
|
||||
* @param {Integer[]} activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} filters (optional) - filters defined from ui.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} options.activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} options.filters (optional) - filters defined from ui.
|
||||
* @param {FilterColValues} options.linkingFilter (optional) - linking filter defined from ui.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
* @param {express.Request} options.req - the request object.
|
||||
*
|
||||
* @return {Promise<string>} Promise for the resulting CSV.
|
||||
*/
|
||||
export async function makeCSVFromViewSection(
|
||||
export async function makeCSVFromViewSection({
|
||||
activeDoc, viewSectionId, sortOrder = null, filters = null, linkingFilter = null, header, req
|
||||
}: {
|
||||
activeDoc: ActiveDoc,
|
||||
viewSectionId: number,
|
||||
sortOrder: number[] | null,
|
||||
filters: Filter[] | null,
|
||||
linkingFilter: FilterColValues | null,
|
||||
req: express.Request) {
|
||||
header?: ExportHeader,
|
||||
req: express.Request
|
||||
}) {
|
||||
|
||||
const data = await exportSection(activeDoc, viewSectionId, sortOrder, filters, linkingFilter, req);
|
||||
const file = convertToCsv(data);
|
||||
const file = convertToCsv(data, { header });
|
||||
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.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.tableId - id of the table to export.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
* @param {express.Request} options.req - the request object.
|
||||
*
|
||||
* @return {Promise<string>} Promise for the resulting CSV.
|
||||
*/
|
||||
export async function makeCSVFromTable(
|
||||
export async function makeCSVFromTable({ activeDoc, tableId, header, req }: {
|
||||
activeDoc: ActiveDoc,
|
||||
tableId: string,
|
||||
req: express.Request) {
|
||||
header?: ExportHeader,
|
||||
req: express.Request
|
||||
}) {
|
||||
|
||||
if (!activeDoc.docData) {
|
||||
throw new Error('No docData in active document');
|
||||
@ -76,7 +93,7 @@ export async function makeCSVFromTable(
|
||||
}
|
||||
|
||||
const data = await exportTable(activeDoc, tableRef, req);
|
||||
const file = convertToCsv(data);
|
||||
const file = convertToCsv(data, { header });
|
||||
return file;
|
||||
}
|
||||
|
||||
@ -84,13 +101,13 @@ function convertToCsv({
|
||||
rowIds,
|
||||
access,
|
||||
columns: viewColumns,
|
||||
docSettings
|
||||
}: ExportData) {
|
||||
}: ExportData, options: { header?: ExportHeader }) {
|
||||
|
||||
// create formatters for columns
|
||||
const formatters = viewColumns.map(col => col.formatter);
|
||||
// Arrange the data into a row-indexed matrix, starting with column headers.
|
||||
const csvMatrix = [viewColumns.map(col => col.label)];
|
||||
const colPropertyAsHeader = options.header ?? 'label';
|
||||
const csvMatrix = [viewColumns.map(col => col[colPropertyAsHeader])];
|
||||
// populate all the rows with values as strings
|
||||
rowIds.forEach(row => {
|
||||
csvMatrix.push(access.map((getter, c) => formatters[c].formatAny(getter(row))));
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {PassThrough} from 'stream';
|
||||
import {FilterColValues} from "app/common/ActiveDocAPI";
|
||||
import {ActiveDocSource, doExportDoc, doExportSection, doExportTable,
|
||||
ExportData, ExportParameters, Filter} from 'app/server/lib/Export';
|
||||
ExportData, ExportHeader, ExportParameters, Filter} from 'app/server/lib/Export';
|
||||
import {createExcelFormatter} from 'app/server/lib/ExcelFormatter';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {Alignment, Border, Buffer as ExcelBuffer, stream as ExcelWriteStream,
|
||||
@ -79,26 +79,34 @@ export async function doMakeXLSXFromOptions(
|
||||
stream: Stream,
|
||||
options: ExportParameters
|
||||
) {
|
||||
const {tableId, viewSectionId, filters, sortOrder, linkingFilter} = options;
|
||||
const {tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
|
||||
if (viewSectionId) {
|
||||
return doMakeXLSXFromViewSection(activeDocSource, testDates, stream, viewSectionId,
|
||||
sortOrder || null, filters || null, linkingFilter || null);
|
||||
return doMakeXLSXFromViewSection({activeDocSource, testDates, stream, viewSectionId, header,
|
||||
sortOrder: sortOrder || null, filters: filters || null, linkingFilter: linkingFilter || null});
|
||||
} else if (tableId) {
|
||||
return doMakeXLSXFromTable(activeDocSource, testDates, stream, tableId);
|
||||
return doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header});
|
||||
} else {
|
||||
return doMakeXLSX(activeDocSource, testDates, stream);
|
||||
return doMakeXLSX({activeDocSource, testDates, stream, header});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Returns a XLSX stream of a view section that can be transformed or parsed.
|
||||
*
|
||||
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} filters (optional) - filters defined from ui.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDocSource - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} options.activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} options.filters (optional) - filters defined from ui.
|
||||
* @param {FilterColValues} options.linkingFilter (optional)
|
||||
* @param {Stream} options.stream - the stream to write to.
|
||||
* @param {boolean} options.testDates - whether to use static dates for testing.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
*/
|
||||
async function doMakeXLSXFromViewSection(
|
||||
async function doMakeXLSXFromViewSection({
|
||||
activeDocSource, testDates, stream, viewSectionId, sortOrder, filters, linkingFilter, header
|
||||
}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
@ -106,27 +114,35 @@ async function doMakeXLSXFromViewSection(
|
||||
sortOrder: number[] | null,
|
||||
filters: Filter[] | null,
|
||||
linkingFilter: FilterColValues | null,
|
||||
) {
|
||||
header?: ExportHeader,
|
||||
}) {
|
||||
const data = await doExportSection(activeDocSource, viewSectionId, sortOrder, filters, linkingFilter);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
exportTable(data);
|
||||
return end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Returns a XLSX 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.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDocSource - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.tableId - id of the table to export.
|
||||
* @param {Stream} options.stream - the stream to write to.
|
||||
* @param {boolean} options.testDates - whether to use static dates for testing.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
*
|
||||
*/
|
||||
async function doMakeXLSXFromTable(
|
||||
async function doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
tableId: string,
|
||||
) {
|
||||
header?: ExportHeader,
|
||||
}) {
|
||||
const data = await doExportTable(activeDocSource, {tableId});
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
exportTable(data);
|
||||
return end();
|
||||
}
|
||||
@ -134,12 +150,13 @@ async function doMakeXLSXFromTable(
|
||||
/**
|
||||
* Creates excel document with all tables from an active Grist document.
|
||||
*/
|
||||
async function doMakeXLSX(
|
||||
async function doMakeXLSX({activeDocSource, testDates, stream, header}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
): Promise<void|ExcelBuffer> {
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
header?: ExportHeader,
|
||||
}): Promise<void|ExcelBuffer> {
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
await doExportDoc(activeDocSource, async (table: ExportData) => exportTable(table));
|
||||
return end();
|
||||
}
|
||||
@ -152,7 +169,7 @@ async function doMakeXLSX(
|
||||
* (The second option is for grist-static; at the time of writing
|
||||
* WorkbookWriter doesn't appear to be available in a browser context).
|
||||
*/
|
||||
function convertToExcel(stream: Stream|undefined, testDates: boolean): {
|
||||
function convertToExcel(stream: Stream|undefined, testDates: boolean, options: { header?: ExportHeader }): {
|
||||
exportTable: (table: ExportData) => void,
|
||||
end: () => Promise<void|ExcelBuffer>,
|
||||
} {
|
||||
@ -206,7 +223,8 @@ function convertToExcel(stream: Stream|undefined, testDates: boolean): {
|
||||
const formatters = columns.map(col => createExcelFormatter(col.formatter.type, col.formatter.widgetOpts));
|
||||
// Generate headers for all columns with correct styles for whole column.
|
||||
// Actual header style for a first row will be overwritten later.
|
||||
ws.columns = columns.map((col, c) => ({ header: col.label, style: formatters[c].style() }));
|
||||
const colHeader = options.header ?? 'label';
|
||||
ws.columns = columns.map((col, c) => ({ header: col[colHeader], style: formatters[c].style() }));
|
||||
// style up the header row
|
||||
for (let i = 1; i <= columns.length; i++) {
|
||||
// apply to all rows (including header)
|
||||
|
@ -207,7 +207,9 @@
|
||||
"{{wrongTypeCount}} non-{{columnType}} column is not shown": "{{wrongTypeCount}} nicht{{columnType}} Spalte wird nicht angezeigt",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown": "{{wrongTypeCount}} nicht{{columnType}} Spalte wird nicht angezeigt",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "Spalte {{wrongTypeCount}} Nicht-{{columnType}} wird nicht angezeigt",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "Spalten {{wrongTypeCount}} Nicht-{{columnType}} werden nicht angezeigt"
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "Spalten {{wrongTypeCount}} Nicht-{{columnType}} werden nicht angezeigt",
|
||||
"No {{columnType}} columns in table.": "Keine {{columnType}} Spalten in der Tabelle.",
|
||||
"Clear selection": "Auswahl löschen"
|
||||
},
|
||||
"DataTables": {
|
||||
"Click to copy": "Zum Kopieren anklicken",
|
||||
|
@ -199,7 +199,9 @@
|
||||
"Widget needs to {{read}} the current table.": "Widget needs to {{read}} the current table.",
|
||||
"Widget needs {{fullAccess}} to this document.": "Widget needs {{fullAccess}} to this document.",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} non-{{columnType}} column is not shown",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} non-{{columnType}} columns are not shown"
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} non-{{columnType}} columns are not shown",
|
||||
"Clear selection": "Clear selection",
|
||||
"No {{columnType}} columns in table.": "No {{columnType}} columns in table."
|
||||
},
|
||||
"DataTables": {
|
||||
"Click to copy": "Click to copy",
|
||||
|
@ -175,7 +175,9 @@
|
||||
"{{wrongTypeCount}} non-{{columnType}} column is not shown": "{{wrongTypeCount}} columna no {{columnType}} no se muestra",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown": "{{wrongTypeCount}} columnas no {{columnType}} no se muestran",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} no se muestra la columna {{columnType}}",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} no se muestran las columnas {{columnType}}"
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} no se muestran las columnas {{columnType}}",
|
||||
"No {{columnType}} columns in table.": "No hay columnas {{columnType}} en la tabla.",
|
||||
"Clear selection": "Borrar la selección"
|
||||
},
|
||||
"DocHistory": {
|
||||
"Activity": "Actividad",
|
||||
|
@ -74,8 +74,8 @@
|
||||
"Accounts": "Comptes",
|
||||
"Add Account": "Ajouter un compte",
|
||||
"Sign Out": "Se déconnecter",
|
||||
"Upgrade Plan": "Version Premium",
|
||||
"Support Grist": "Centre d'aide Grist",
|
||||
"Upgrade Plan": "Changer d'offre",
|
||||
"Support Grist": "Soutenir Grist",
|
||||
"Billing Account": "Facturation",
|
||||
"Activation": "Activer",
|
||||
"Sign In": "Se connecter",
|
||||
@ -86,7 +86,8 @@
|
||||
"Action Log failed to load": "Impossible de charger le journal des actions",
|
||||
"Table {{tableId}} was subsequently removed in action #{{actionNum}}": "La table {{tableId}} a été ensuite supprimée dans l'action #{{actionNum}}",
|
||||
"This row was subsequently removed in action {{action.actionNum}}": "Cette ligne a été ensuite supprimée dans l'action {{action.actionNum}}",
|
||||
"Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "La colonne {{colId}} a ensuite été supprimée dans l'action #{{action.actionNum}}"
|
||||
"Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "La colonne {{colId}} a ensuite été supprimée dans l'action #{{action.actionNum}}",
|
||||
"All tables": "Toutes les tables"
|
||||
},
|
||||
"AddNewButton": {
|
||||
"Add New": "Nouveau"
|
||||
@ -194,7 +195,9 @@
|
||||
"Widget needs to {{read}} the current table.": "Le widget a besoin de {{read}} la table actuelle.",
|
||||
"Widget does not require any permissions.": "La vue ne nécessite aucune autorisation.",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} colonnes non-{{columnType}} masquées",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} colonnes de type non-{{columnType}} masquées"
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} colonnes de type non-{{columnType}} masquées",
|
||||
"No {{columnType}} columns in table.": "Pas de colonne de type {{columnType}} dans la table.",
|
||||
"Clear selection": "Tout désélectionner"
|
||||
},
|
||||
"DataTables": {
|
||||
"Raw Data Tables": "Données sources",
|
||||
@ -416,7 +419,7 @@
|
||||
"Get started by exploring templates, or creating your first Grist document.": "Commencez par explorer des modèles ou créez votre premier document Grist.",
|
||||
"Welcome to Grist!": "Bienvenue sur Grist !",
|
||||
"Help Center": "Centre d'aide",
|
||||
"Invite Team Members": "Inviter un nouveau membre",
|
||||
"Invite Team Members": "Inviter un nouveau membre à l'espace d'équipe",
|
||||
"Browse Templates": "Parcourir les modèles",
|
||||
"Create Empty Document": "Créer un document vide",
|
||||
"Import Document": "Importer un Fichier",
|
||||
@ -521,7 +524,7 @@
|
||||
"Select Widget": "Choisir la vue",
|
||||
"Select Data": "Choisir les données source",
|
||||
"Group by": "Grouper par",
|
||||
"Add to Page": "Ajouter à la page"
|
||||
"Add to Page": "Ajouter à la Page"
|
||||
},
|
||||
"Pages": {
|
||||
"The following tables will no longer be visible_one": "La donnée source ne sera plus visible",
|
||||
@ -628,11 +631,11 @@
|
||||
"Use choice position": "Utiliser l'ordre des choix",
|
||||
"Natural sort": "Trier",
|
||||
"Empty values last": "Valeurs vides en dernier",
|
||||
"Search Columns": "Rechercher"
|
||||
"Search Columns": "Rechercher dans les colonnes"
|
||||
},
|
||||
"SortFilterConfig": {
|
||||
"Save": "Enregistrer",
|
||||
"Revert": "Restaurer",
|
||||
"Revert": "Retour",
|
||||
"Sort": "TRI",
|
||||
"Filter": "FILTRE",
|
||||
"Update Sort & Filter settings": "Mettre à jour le tri et le filtre"
|
||||
@ -649,7 +652,7 @@
|
||||
"Code View": "Vue du code",
|
||||
"How-to Tutorial": "Tutoriel pratique",
|
||||
"Tour of this Document": "Découvrir le document",
|
||||
"Delete document tour?": "Delete document tour?",
|
||||
"Delete document tour?": "Supprimer la visite guidée du document ?",
|
||||
"Delete": "Supprimer",
|
||||
"Return to viewing as yourself": "Revenir à une vue en propre",
|
||||
"Raw Data": "Données source",
|
||||
@ -690,10 +693,10 @@
|
||||
"Update formula (Shift+Enter)": "Mettre à jour la formule (Maj+Entrée)"
|
||||
},
|
||||
"ViewConfigTab": {
|
||||
"Unmark On-Demand": "Unmark On-Demand",
|
||||
"Unmark On-Demand": "Ne plus marquer comme \"à la demande\"",
|
||||
"Make On-Demand": "Rendre dynamique",
|
||||
"Advanced settings": "Paramètres avancés",
|
||||
"Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.": "Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.",
|
||||
"Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.": "Les grosses tables peuvent être marquées comme \"à la demande\" pour éviter de les charger dans le moteur de calcul.",
|
||||
"Form": "Formulaire",
|
||||
"Compact": "Compact",
|
||||
"Blocks": "Blocs",
|
||||
@ -720,7 +723,7 @@
|
||||
"ViewSectionMenu": {
|
||||
"Update Sort&Filter settings": "Mettre à jour le tri et le filtre",
|
||||
"Save": "Enregistrer",
|
||||
"Revert": "Restaurer",
|
||||
"Revert": "Retour",
|
||||
"(customized)": "(personnalisé)",
|
||||
"(modified)": "(modifié)",
|
||||
"(empty)": "(vide)",
|
||||
@ -791,7 +794,10 @@
|
||||
"Contact support": "Contacter le support",
|
||||
"Something went wrong": "Une erreur s’est produite",
|
||||
"There was an error: {{message}}": "Une erreur s’est produite : {{message}}",
|
||||
"There was an unknown error.": "Une erreur inconnue s’est produite."
|
||||
"There was an unknown error.": "Une erreur inconnue s’est produite.",
|
||||
"Account deleted{{suffix}}": "Compte supprimé {{suffix}}",
|
||||
"Your account has been deleted.": "Votre compte a été supprimé.",
|
||||
"Sign up": "S'inscrire"
|
||||
},
|
||||
"menus": {
|
||||
"Select fields": "Sélectionner les champs",
|
||||
@ -847,7 +853,7 @@
|
||||
"Open row styles": "Ouvrir les styles de ligne",
|
||||
"Cell Style": "Style de cellule",
|
||||
"CELL STYLE": "STYLE de CELLULE",
|
||||
"Default cell style": "Style par défaut",
|
||||
"Default cell style": "Style par défaut de la cellule",
|
||||
"Mixed style": "Style composite",
|
||||
"Header Style": "Style de l'entête",
|
||||
"Default header style": "Style par défaut",
|
||||
@ -916,7 +922,7 @@
|
||||
"Update formula (Shift+Enter)": "Modifier la formule (MAJ+Entrée)"
|
||||
},
|
||||
"ColumnEditor": {
|
||||
"COLUMN DESCRIPTION": "DESCRIPTION",
|
||||
"COLUMN DESCRIPTION": "DESCRIPTION DE LA COLONNE",
|
||||
"COLUMN LABEL": "LIBELLÉ"
|
||||
},
|
||||
"ACLUsers": {
|
||||
@ -938,7 +944,7 @@
|
||||
"CHOICES": "CHOIX"
|
||||
},
|
||||
"ColumnInfo": {
|
||||
"COLUMN DESCRIPTION": "DESCRIPTION",
|
||||
"COLUMN DESCRIPTION": "DESCRIPTION DE LA COLONNE",
|
||||
"COLUMN ID: ": "Identifiant de la colonne : ",
|
||||
"COLUMN LABEL": "LIBELLÉ",
|
||||
"Cancel": "Annuler",
|
||||
@ -1013,7 +1019,7 @@
|
||||
"relational": "relationnelles",
|
||||
"Access Rules": "Règles d'accès",
|
||||
"Learn more.": "En savoir plus.",
|
||||
"They allow for one record to point (or refer) to another.": "Ils permettent à un enregistrement de pointer (ou de faire référence) vers un autre.",
|
||||
"They allow for one record to point (or refer) to another.": "Ils permettent à une ligne de pointer (ou de faire référence) vers une autre.",
|
||||
"Add New": "Nouveau",
|
||||
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Les règles d'accès vous donnent le pouvoir de créer des règles nuancées pour déterminer qui peut voir ou modifier quelles parties de votre document.",
|
||||
"Use the 𝚺 icon to create summary (or pivot) tables, for totals or subtotals.": "Utilisez l'icône 𝚺 pour créer des tables récapitulatives (ou tables croisées dynamiques), pour les totaux ou les sous-totaux.",
|
||||
@ -1021,7 +1027,12 @@
|
||||
"Anchor Links": "Ancres",
|
||||
"Custom Widgets": "Vues personnalisées",
|
||||
"To make an anchor link that takes the user to a specific cell, click on a row and press {{shortcut}}.": "Pour créer un lien d'ancrage qui amène l'utilisateur à une cellule spécifique, cliquez sur une ligne et appuyez sur {{shortcut}}.",
|
||||
"You can choose one of our pre-made widgets or embed your own by providing its full URL.": "Vous pouvez choisir l'une de nos vues prédéfinis ou intégrer la vôtre en indiquant son URL complète."
|
||||
"You can choose one of our pre-made widgets or embed your own by providing its full URL.": "Vous pouvez choisir l'une de nos vues prédéfinies ou intégrer la vôtre en indiquant son URL complète.",
|
||||
"To configure your calendar, select columns for start": {
|
||||
"end dates and event titles. Note each column's type.": "Pour configurer votre calendrier, sélectionnez les colonnes pour les dates de début/fin et le nom de l'évènement. Notez le type de chaque colonne."
|
||||
},
|
||||
"Calendar": "Calendrier",
|
||||
"Can't find the right columns? Click 'Change Widget' to select the table with events data.": "Impossible de trouver les bonnes colonnes ? Cliquez sur \"Changer de vue\" pour sélectionner la table contenant les évènements."
|
||||
},
|
||||
"ColumnTitle": {
|
||||
"Add description": "Ajouter une description",
|
||||
@ -1030,7 +1041,7 @@
|
||||
"COLUMN ID: ": "Identifiant de la column : ",
|
||||
"Column description": "Description de la colonne",
|
||||
"Column label": "Libellé de la colonne",
|
||||
"Provide a column label": "Renommer la colonne",
|
||||
"Provide a column label": "Donner un nom à la colonne",
|
||||
"Save": "Sauvegarder",
|
||||
"Close": "Fermer"
|
||||
},
|
||||
@ -1074,8 +1085,8 @@
|
||||
"AI Assistant": "Assistant IA",
|
||||
"Apply": "Appliquer",
|
||||
"Cancel": "Annuler",
|
||||
"Hi, I'm the Grist Formula AI Assistant.": "Bonjour, je suis l'assistant IA de Grist pour les formules",
|
||||
"I can only help with formulas. I cannot build tables, columns, and views, or write access rules.": "Je peux aider seulement pour les formules, je ne peut pas créer de tables, de colonnes, de vue ou gérer les droits d'accès.",
|
||||
"Hi, I'm the Grist Formula AI Assistant.": "Bonjour, je suis l'assistant IA de Grist pour les formules.",
|
||||
"I can only help with formulas. I cannot build tables, columns, and views, or write access rules.": "Je ne peux aider que pour les formules, je ne peut pas créer de tables, de colonnes, de vue ou gérer les droits d'accès.",
|
||||
"Learn more": "En apprendre plus",
|
||||
"Clear Conversation": "Effacer la conversation",
|
||||
"Code View": "Vue du code",
|
||||
@ -1100,7 +1111,9 @@
|
||||
"Close": "Fermer",
|
||||
"Contribute": "Contribuer",
|
||||
"Support Grist": "Support Grist",
|
||||
"Opt in to Telemetry": "S'inscrire à Telemetry"
|
||||
"Opt in to Telemetry": "S'inscrire à l'envoi de données de télémétrie",
|
||||
"Opted In": "Accepté",
|
||||
"Support Grist page": "Soutenir Grist"
|
||||
},
|
||||
"GridView": {
|
||||
"Click to insert": "Cliquer pour insérer"
|
||||
@ -1112,12 +1125,16 @@
|
||||
"Sponsor Grist Labs on GitHub": "Sponsoriser Grist Labs sur GitHub",
|
||||
"GitHub Sponsors page": "Page de sponsors GitHub",
|
||||
"Manage Sponsorship": "Gérer le parrainage",
|
||||
"Opt in to Telemetry": "S'inscrire à Telemetry",
|
||||
"Opt out of Telemetry": "S'inscrire à Telemetry",
|
||||
"Opt in to Telemetry": "S'inscrire à l'envoi de données de télémétrie",
|
||||
"Opt out of Telemetry": "Se désinscrire de l'envoi de données de télémétrie",
|
||||
"Support Grist": "Support Grist",
|
||||
"Telemetry": "Telemetry",
|
||||
"This instance is opted in to telemetry. Only the site administrator has permission to change this.": "Cette instance est autorisée à utiliser la télémétrie. Seul l'administrateur de l'espace est autorisé à modifier ce paramètre.",
|
||||
"This instance is opted out of telemetry. Only the site administrator has permission to change this.": "Cette instance est autorisée à utiliser la télémétrie. Seul l'administrateur de l'espace est autorisé à modifier ce paramètre."
|
||||
"Telemetry": "Télémétrie",
|
||||
"This instance is opted in to telemetry. Only the site administrator has permission to change this.": "Cette instance accepte l'envoi de données de télémétrie. Seul l'administrateur de l'espace est autorisé à modifier ce paramètre.",
|
||||
"This instance is opted out of telemetry. Only the site administrator has permission to change this.": "Cette instance est autorisée à utiliser la télémétrie. Seul l'administrateur de l'espace est autorisé à modifier ce paramètre.",
|
||||
"You have opted out of telemetry.": "Vous avez choisi de ne pas envoyer de données de télémétrie.",
|
||||
"We only collect usage statistics, as detailed in our {{link}}, never document contents.": "Nous ne collectons que des statistiques d'usage, comme détaillé dans notre {{link}}, jamais le contenu des documents.",
|
||||
"You can opt out of telemetry at any time from this page.": "Vous pouvez vous désinscrire de la télémétrie à tout moment depuis cette page.",
|
||||
"You have opted in to telemetry. Thank you!": "Merci de vous être inscrit à la télémétrie !"
|
||||
},
|
||||
"buildViewSectionDom": {
|
||||
"No data": "Aucune donnée",
|
||||
@ -1129,7 +1146,7 @@
|
||||
"Anyone with link ": "Toute personne possédant le lien ",
|
||||
"Cancel": "Annuler",
|
||||
"Close": "Fermer",
|
||||
"Add {{member}} to your team": "Ajouter des {{member}} à votre équipe",
|
||||
"Add {{member}} to your team": "Ajouter {{member}} à votre équipe",
|
||||
"Confirm": "Confirmer",
|
||||
"member": "membre",
|
||||
"{{collaborator}} limit exceeded": "la limite de {{collaborator}} a été atteinte",
|
||||
@ -1139,39 +1156,39 @@
|
||||
"Collaborator": "Collaborateur",
|
||||
"Copy Link": "Copier le lien",
|
||||
"Create a team to share with more people": "Créer une équipe pour partager avec plus de personnes",
|
||||
"Grist support": "Support Grist",
|
||||
"Grist support": "Support utilisateur Grist",
|
||||
"Guest": "Invité",
|
||||
"Invite multiple": "Invitation multiple",
|
||||
"Invite people to {{resourceType}}": "Inviter des personnes à {{resourceType}}",
|
||||
"Link copied to clipboard": "Lien copié dans le presse-papiers",
|
||||
"Manage members of team site": "Gérer les membres de l'espace d'équipe",
|
||||
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "L'absence d'accès par défaut permet d'accorder l'accès à des documents ou à des espaces de travail spécifiques, plutôt qu'à l'ensemble de l'espace d'équipe.",
|
||||
"Off": "Off",
|
||||
"On": "On",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{name}}.": "Une fois que vous avez supprimé votre propre accès, vous ne pourrez pas le récupérer sans l'aide d'une autre personne disposant d'un accès suffisant au {{name}}.",
|
||||
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "L'absence d'accès par défaut permet d' accorder l'accès à des documents ou à des espaces de travail spécifiques, plutôt qu'à l'ensemble de l'espace d'équipe.",
|
||||
"Off": "Désactivé",
|
||||
"On": "Activé",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{name}}.": "Une fois que vous avez supprimé votre propre accès, vous ne pourrez pas le récupérer sans l'aide d'une autre personne disposant d'un accès suffisant à {{name}}.",
|
||||
"Open Access Rules": "Ouvrir les règles d'accès",
|
||||
"Outside collaborator": "Collaborateur externe",
|
||||
"Public Access": "Accès public",
|
||||
"Public access": "Accès public",
|
||||
"Public access inherited from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "L'accès public hérite de {{parent}}. Pour le supprimer, changer l'option 'Accès hérité' à 'Aucun'",
|
||||
"Public access inherited from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "L'accès public est hérité de {{parent}}. Pour le supprimer, changer l'option 'Accès hérité' à 'Aucun'.",
|
||||
"Public access: ": "Accès public : ",
|
||||
"Remove my access": "Supprimer mon accès",
|
||||
"Save & ": "Sauvegarder & ",
|
||||
"Team member": "Membres",
|
||||
"User may not modify their own access.": "L'utilisateur ne peut pas modifier son propre accès.",
|
||||
"Team member": "Membres de l'espace d'équipe",
|
||||
"User may not modify their own access.": "L'utilisateur ne peut pas modifier ses propres accès.",
|
||||
"Your role for this team site": "Votre rôle pour cet espace d'équipe",
|
||||
"Your role for this {{resourceType}}": "Votre rôle pour cet {{resourceType}}",
|
||||
"free collaborator": "Collaborateur gratuit",
|
||||
"free collaborator": "collaborateur gratuit",
|
||||
"guest": "invité",
|
||||
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "L'absence d'accès par défaut permet d'accorder l'accès à des documents ou à des espaces de travail spécifiques, plutôt qu'à l'ensemble de l'espace d'équipe.",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{resourceType}}.": "Une fois que vous avez supprimé votre propre accès, vous ne pourrez pas le récupérer sans l'aide d'une autre personne disposant d'un accès suffisant au {{resourceType}}.",
|
||||
"User has view access to {{resource}} resulting from manually-set access to resources inside. If removed here, this user will lose access to resources inside.": "L'utilisateur a un accès visuel à {{resource}} résultant d'un accès manuel aux ressources internes. S'il est supprimé ici, cet utilisateur perdra l'accès aux ressources internes.",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{resourceType}}.": "Une fois que vous avez supprimé vos propres accès, vous ne pourrez pas les récupérer sans l'aide d'une autre personne disposant d'un accès suffisant au {{resourceType}}.",
|
||||
"User has view access to {{resource}} resulting from manually-set access to resources inside. If removed here, this user will lose access to resources inside.": "L'utilisateur a un accès en lecture seule à {{resource}} résultant d'un accès des ressources à l'intérieur. S'il est supprimé ici, cet utilisateur perdra l'accès aux ressources à l'intérieur.",
|
||||
"You are about to remove your own access to this {{resourceType}}": "Vous êtes sur le point de supprimer votre propre accès à {{resourceType}}",
|
||||
"User inherits permissions from {{parent})}. To remove, set 'Inherit access' option to 'None'.": "L'utilisateur hérite ses permissions de {{parent})}. Pour supprimer cela, paramétrez 'Héritage d'accès à 'Aucun'."
|
||||
},
|
||||
"SearchModel": {
|
||||
"Search all tables": "Rechercher toutes les tables",
|
||||
"Search all pages": "Rechercher toutes les pages"
|
||||
"Search all pages": "Rechercher dans toutes les pages"
|
||||
},
|
||||
"searchDropdown": {
|
||||
"Search": "Chercher"
|
||||
|
@ -28,7 +28,14 @@
|
||||
"Sign Out": "Logg ut",
|
||||
"Sign in": "Logg inn",
|
||||
"Switch Accounts": "Bytt konto",
|
||||
"Toggle Mobile Mode": "Slå av/på mobilmodus"
|
||||
"Toggle Mobile Mode": "Slå av/på mobilmodus",
|
||||
"Activation": "Aktivering",
|
||||
"Support Grist": "Støtt Grist",
|
||||
"Upgrade Plan": "Oppgrader plan",
|
||||
"Use This Template": "Bruk denne malen",
|
||||
"Billing Account": "Faktureringskonto",
|
||||
"Sign In": "Logg inn",
|
||||
"Sign Up": "Registrering"
|
||||
},
|
||||
"AddNewButton": {
|
||||
"Add New": "Legg til ny"
|
||||
@ -53,7 +60,8 @@
|
||||
"Home Page": "Hjemmeside",
|
||||
"Personal Site": "Personlig side",
|
||||
"Team Site": "Lagside",
|
||||
"Legacy": "Foreldet"
|
||||
"Legacy": "Foreldet",
|
||||
"Grist Templates": "Grist-maler"
|
||||
},
|
||||
"CellContextMenu": {
|
||||
"Clear cell": "Tøm celle",
|
||||
@ -84,7 +92,11 @@
|
||||
"Duplicate rows_one": "Dupliser rad",
|
||||
"Reset {{count}} columns_one": "Tilbakestill kolonne",
|
||||
"Reset {{count}} entire columns_other": "Tilbakestill {{count}} hele kolonner",
|
||||
"Reset {{count}} columns_other": "Tilbakestill {{count}} kolonner"
|
||||
"Reset {{count}} columns_other": "Tilbakestill {{count}} kolonner",
|
||||
"Copy": "Kopier",
|
||||
"Comment": "Kommentar",
|
||||
"Cut": "Klipp ut",
|
||||
"Paste": "Lim inn"
|
||||
},
|
||||
"ColumnFilterMenu": {
|
||||
"All": "Alle",
|
||||
@ -121,7 +133,9 @@
|
||||
"Widget needs to {{read}} the current table.": "Miniprogrammet må {{read}} nåværende tabell.",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} ikke-{{columnType}}-kolonner er ikke vist.",
|
||||
"Widget needs {{fullAccess}} to this document.": "Miniprogrammet trenger {{fullAccess}} til dette dokumentet.",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} ikke-{{columnType}}-kolonne er ikke vist."
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} ikke-{{columnType}}-kolonne er ikke vist.",
|
||||
"No {{columnType}} columns in table.": "Ingen {{columnType}}-kolonner i tabell.",
|
||||
"Clear selection": "Tøm utvalg"
|
||||
},
|
||||
"DocHistory": {
|
||||
"Activity": "Aktivitet",
|
||||
@ -186,7 +200,8 @@
|
||||
"Trash": "Papirkurv",
|
||||
"Workspace will be moved to Trash.": "Arbeidsområdet vil bli flyttet til papirkurven.",
|
||||
"Workspaces": "Arbeidsområder",
|
||||
"Rename": "Gi nytt navn"
|
||||
"Rename": "Gi nytt navn",
|
||||
"Tutorial": "Veiledning"
|
||||
},
|
||||
"MakeCopyMenu": {
|
||||
"It will be overwritten, losing any content not in this document.": "Den vil bli overskrevet, noe som forkaster alt innholdet som ikke er i dokumentet.",
|
||||
@ -211,7 +226,10 @@
|
||||
"Overwrite": "Overskriv",
|
||||
"Replacing the original requires editing rights on the original document.": "Erstatting av originalen krever redigeringsrettigheter til originaldokumentet.",
|
||||
"Be careful, the original has changes not in this document. Those changes will be overwritten.": "Vær forsiktig, originalen har endringer som ikke finnes i dette dokumentet. De endringene vil bli overskrevet.",
|
||||
"However, it appears to be already identical.": "Dog later det til at det allerede er identisk."
|
||||
"However, it appears to be already identical.": "Dog later det til at det allerede er identisk.",
|
||||
"Remove all data but keep the structure to use as a template": "Fjern all data, men behold strukturen til bruk som mal",
|
||||
"Remove document history (can significantly reduce file size)": "Fjern dokumenthistorikk (kan redusere filstørrelse drastisk)",
|
||||
"Download full document and history": "Last ned hele dokumentet og historikken"
|
||||
},
|
||||
"NotifyUI": {
|
||||
"Cannot find personal site, sorry!": "Finner ikke personlig side. Beklager.",
|
||||
@ -222,7 +240,8 @@
|
||||
"Report a problem": "Innrapporter et problem",
|
||||
"Go to your free personal site": "Gå til din kostnadsløse personlige side",
|
||||
"No notifications": "Ingen merknader",
|
||||
"Notifications": "Merknader"
|
||||
"Notifications": "Merknader",
|
||||
"Manage billing": "Håndter fakturering"
|
||||
},
|
||||
"RightPanel": {
|
||||
"COLUMN TYPE": "Kolonnetype",
|
||||
@ -253,7 +272,8 @@
|
||||
"Series_other": "Serier",
|
||||
"Sort & Filter": "Sorter og filtrer",
|
||||
"You do not have edit access to this document": "Du har ikke redigeringstilgang til dette dokumentet",
|
||||
"Widget": "Miniprogram"
|
||||
"Widget": "Miniprogram",
|
||||
"Add referenced columns": "Legg til kolonner å vise til"
|
||||
},
|
||||
"RowContextMenu": {
|
||||
"Delete": "Slett",
|
||||
@ -283,7 +303,9 @@
|
||||
"Current Version": "Nåværende versjon",
|
||||
"Download": "Last ned",
|
||||
"Duplicate Document": "Dupliser dokument",
|
||||
"Send to Google Drive": "Send til Google Drive"
|
||||
"Send to Google Drive": "Send til Google Drive",
|
||||
"Download...": "Last ned …",
|
||||
"Share": "Del"
|
||||
},
|
||||
"ThemeConfig": {
|
||||
"Switch appearance automatically to match system": "Bytt utseende automatisk for å samsvare med systemet",
|
||||
@ -356,13 +378,17 @@
|
||||
"You do not have access to this organization's documents.": "Du har ikke tilgang til denne organisasjonens dokumenter.",
|
||||
"Error{{suffix}}": "Feil-{{suffix}}",
|
||||
"Page not found{{suffix}}": "Fant ikke siden-{{suffix}}",
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Du er innlogget som {{email}}. Du kan logge inn med en annen konto, eller spørre en administrator om tilgang."
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Du er innlogget som {{email}}. Du kan logge inn med en annen konto, eller spørre en administrator om tilgang.",
|
||||
"Account deleted{{suffix}}": "Konto slettet{{suffix}}",
|
||||
"Your account has been deleted.": "Kontoen din har blitt slettet.",
|
||||
"Sign up": "Logg inn"
|
||||
},
|
||||
"search": {
|
||||
"Search in document": "Søk i dokumentet",
|
||||
"Find Next ": "Finn neste ",
|
||||
"Find Previous ": "Finn forrige ",
|
||||
"No results": "Resultatløst"
|
||||
"No results": "Resultatløst",
|
||||
"Search": "Søk"
|
||||
},
|
||||
"DiscussionEditor": {
|
||||
"Cancel": "Avbryt",
|
||||
@ -428,7 +454,9 @@
|
||||
"Lookup Table": "Oppslagstabell",
|
||||
"Lookup Column": "Oppslagskolonne",
|
||||
"Permission to access the document in full when needed": "Tilgang til hele dokumentet når det trengs",
|
||||
"Save": "Lagre"
|
||||
"Save": "Lagre",
|
||||
"Allow editors to edit structure (e.g. modify and delete tables, columns, layouts), and to write formulas, which give access to all data regardless of read restrictions.": "Tillat de som redigerer å endre struktur (f.eks. endre og slette tabeller, kolonner, oppsett), og å skrive formler, som gir tilgang til all data uavhengig av begrensninger.",
|
||||
"This default should be changed if editors' access is to be limited. ": "Dette forvalget må endres hvis de som redigerer ikke skal ha tilgang, "
|
||||
},
|
||||
"ChartView": {
|
||||
"Toggle chart aggregation": "Veksle diagramsvisning",
|
||||
@ -474,7 +502,9 @@
|
||||
"Locale:": "Lokalitet:",
|
||||
"Document ID copied to clipboard": "Dokument-ID kopiert til utklippstavlen",
|
||||
"Currency:": "Valuta:",
|
||||
"Document Settings": "Dokumentinnstillinger"
|
||||
"Document Settings": "Dokumentinnstillinger",
|
||||
"Manage Webhooks": "Håndter vevkroker",
|
||||
"Webhooks": "Vevkroker"
|
||||
},
|
||||
"DocumentUsage": {
|
||||
"Usage": "Bruk",
|
||||
@ -553,7 +583,16 @@
|
||||
"Try out changes in a copy, then decide whether to replace the original with your edits.": "Prøv ut endringer i en kopi, og avgjør så hvorvidt du vil erstatte originalen med endringene dine.",
|
||||
"Unpin to hide the the button while keeping the filter.": "Løsne for å skjule knappen og beholde filteret.",
|
||||
"Useful for storing the timestamp or author of a new record, data cleaning, and more.": "Nyttig for lagring av tidsstempel eller forfatter av en ny oppføring, datarensing, med mer.",
|
||||
"The total size of all data in this document, excluding attachments.": "Samlet størrelse av all data i dette dokumentet, fraregnet vedlegg."
|
||||
"The total size of all data in this document, excluding attachments.": "Samlet størrelse av all data i dette dokumentet, fraregnet vedlegg.",
|
||||
"You can choose one of our pre-made widgets or embed your own by providing its full URL.": "Du kan velge en av miniprogrammene som finnes, eller bygge inn ditt eget ved å angi dets fulle nettadresse.",
|
||||
"To configure your calendar, select columns for start": {
|
||||
"end dates and event titles. Note each column's type.": "Velg kolonner for start-/slutt-dato og begivenhetsnavn. Merk deg hvilken type hver kolonne er."
|
||||
},
|
||||
"Calendar": "Kalender",
|
||||
"To make an anchor link that takes the user to a specific cell, click on a row and press {{shortcut}}.": "Klikk på en rad og trykk {{shortcut}} for å lage en ankerlenke som tar brukeren til en gitt celle.",
|
||||
"Anchor Links": "Ankerlenker",
|
||||
"Can't find the right columns? Click 'Change Widget' to select the table with events data.": "Finner du ikke riktig kolonner? Klikk «Endre miniprogram» for å velge tabellen med begivenhetsdata.",
|
||||
"Custom Widgets": "Egendefinerte miniprogrammer"
|
||||
},
|
||||
"ViewAsDropdown": {
|
||||
"Example Users": "Eksempelbrukere",
|
||||
@ -564,7 +603,8 @@
|
||||
"Action Log failed to load": "Kunne ikke laste inn handlingslogg",
|
||||
"Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "Kolonnen {{colId}} ble påfølgende fjernet i handling #{{action.actionNum}}",
|
||||
"Table {{tableId}} was subsequently removed in action #{{actionNum}}": "Tabellen {{tableId}} ble påfølgende fjernet i handling #{{actionNum}}",
|
||||
"This row was subsequently removed in action {{action.actionNum}}": "Denne raden ble påfølgende fjernet i handling {{action.actionNum}}"
|
||||
"This row was subsequently removed in action {{action.actionNum}}": "Denne raden ble påfølgende fjernet i handling {{action.actionNum}}",
|
||||
"All tables": "Alle tabeller"
|
||||
},
|
||||
"ColorSelect": {
|
||||
"Apply": "Bruk",
|
||||
@ -630,12 +670,15 @@
|
||||
"Unfreeze {{count}} columns_other": "Tin {{count}} kolonner",
|
||||
"Reset {{count}} entire columns_other": "Tilbakestill {{count}} hele kolonner",
|
||||
"Unfreeze all columns": "Tin alle kolonner",
|
||||
"Add Column": "Legg til kolonne"
|
||||
"Add Column": "Legg til kolonne",
|
||||
"Insert column to the right": "Sett inn kolonne til høyre",
|
||||
"Insert column to the left": "Sett inn kolonne til venstre"
|
||||
},
|
||||
"GristDoc": {
|
||||
"Import from file": "Importer fra fil",
|
||||
"Added new linked section to view {{viewName}}": "Ny lenket avsnitt lagt til i visning {{viewName}}",
|
||||
"Saved linked section {{title}} in view {{name}}": "Lagret lenket {{title}}-avsnitt i {{name}}-visningen"
|
||||
"Saved linked section {{title}} in view {{name}}": "Lagret lenket {{title}}-avsnitt i {{name}}-visningen",
|
||||
"go to webhook settings": "gå til vevkroksinnstillinger"
|
||||
},
|
||||
"HomeIntro": {
|
||||
"Browse Templates": "Utforsk maler",
|
||||
@ -657,7 +700,12 @@
|
||||
"Get started by inviting your team and creating your first Grist document.": "Begynn ved å invitere laget ditt og ved å opprette ditt første Grist-dokument.",
|
||||
"Interested in using Grist outside of your team? Visit your free ": "Interessert i bruk av Grist utenfor laget ditt? Besøk ditt kostnadsløse ",
|
||||
"This workspace is empty.": "Arbeidsområdet er tomt.",
|
||||
"Any documents created in this site will appear here.": "Alle dokumenter opprettet i denne siden vil vises her."
|
||||
"Any documents created in this site will appear here.": "Alle dokumenter opprettet i denne siden vil vises her.",
|
||||
"Welcome to Grist, {{- name}}!": "Velkommen til Grist {{- name}}",
|
||||
"Visit our {{link}} to learn more about Grist.": "Besøk {{link}} for å lære mer om Grist.",
|
||||
"Welcome to {{- orgName}}": "Velkommen til {{- orgName}}",
|
||||
"Sign in": "Logg inn",
|
||||
"To use Grist, please either sign up or sign in.": "Logg inn eller registrer deg for å bruke Grist."
|
||||
},
|
||||
"PageWidgetPicker": {
|
||||
"Building {{- label}} widget": "Bygger {{- label}}-miniprogram",
|
||||
@ -687,14 +735,21 @@
|
||||
"CELL STYLE": "Cellestil",
|
||||
"Cell Style": "Cellestil",
|
||||
"Default cell style": "Forvalgt cellestil",
|
||||
"Open row styles": "Åpne radstiler"
|
||||
"Open row styles": "Åpne radstiler",
|
||||
"HEADER STYLE": "Topptekststil",
|
||||
"Header Style": "Topptekststil",
|
||||
"Default header style": "Forvalgt topptekststil"
|
||||
},
|
||||
"FormulaEditor": {
|
||||
"Errors in all {{numErrors}} cells": "Feil i alle {{numErrors}} celler",
|
||||
"Column or field is required": "Kolonne eller felt kreves angitt",
|
||||
"editingFormula is required": "«editingFormula» kreves",
|
||||
"Errors in {{numErrors}} of {{numCells}} cells": "Feil i {{numErrors}} av {{numCells}} celler",
|
||||
"Error in the cell": "Feil i cellen"
|
||||
"Error in the cell": "Feil i cellen",
|
||||
"Enter formula or {{button}}.": "Skriv inn formel eller {{button}}.",
|
||||
"Enter formula.": "Skriv inn formel.",
|
||||
"Expand Editor": "Utvid tekstbehandler",
|
||||
"use AI Assistant": "bruk AI-assistent"
|
||||
},
|
||||
"VisibleFieldsConfig": {
|
||||
"Cannot drop items into Hidden Fields": "Kan ikke plassere elementer i skjulte felter",
|
||||
@ -808,7 +863,8 @@
|
||||
"Save": "Lagre",
|
||||
"Provide a table name": "Angi et tabellnavn",
|
||||
"Override widget title": "Overstyr miniprogramsnavn",
|
||||
"WIDGET TITLE": "Miniprogramsnavn"
|
||||
"WIDGET TITLE": "Miniprogramsnavn",
|
||||
"WIDGET DESCRIPTION": "Miniprogramsbeskrivelse"
|
||||
},
|
||||
"menus": {
|
||||
"Select fields": "Velg felter",
|
||||
@ -904,7 +960,22 @@
|
||||
"Importer": {
|
||||
"Merge rows that match these fields:": "Flett rader som samsvarer med disse feltene:",
|
||||
"Select fields to match on": "Velg felter å jamføre",
|
||||
"Update existing records": "Oppdater eksisterende oppføringer"
|
||||
"Update existing records": "Oppdater eksisterende oppføringer",
|
||||
"Column mapping": "Kolonnetilknytning",
|
||||
"Grist column": "Grist-kolonne",
|
||||
"{{count}} unmatched field_one": "{{count}} usamsvarende felt",
|
||||
"{{count}} unmatched field in import_one": "{{count}} usamsvarende felt i import",
|
||||
"Revert": "Angre",
|
||||
"Skip Import": "Hopp over import",
|
||||
"{{count}} unmatched field_other": "{{count}} usamsvarende felter",
|
||||
"New Table": "Ny tabell",
|
||||
"Skip": "Hopp over",
|
||||
"Column Mapping": "Kolonnetilknytning",
|
||||
"Destination table": "Måltabell",
|
||||
"Skip Table on Import": "Hopp over tabell ved import",
|
||||
"Import from file": "Importer fra fil",
|
||||
"{{count}} unmatched field in import_other": "{{count}} usamsvarende felt i import",
|
||||
"Source column": "Kildekolonne"
|
||||
},
|
||||
"LeftPanelCommon": {
|
||||
"Help Center": "Hjelpesenter"
|
||||
@ -973,5 +1044,176 @@
|
||||
},
|
||||
"EditorTooltip": {
|
||||
"Convert column to formula": "Konverter kolonne til formel"
|
||||
},
|
||||
"FormulaAssistant": {
|
||||
"Data": "Data",
|
||||
"Press Enter to apply suggested formula.": "Trykk Enter for å bruke foreslått formel.",
|
||||
"See our {{helpFunction}} and {{formulaCheat}}, or visit our {{community}} for more help.": "Sjekk {{helpFunction}} og {{formulaCheat}}, eller besøk {{community}} for mer hjelp.",
|
||||
"Sign up for a free Grist account to start using the Formula AI Assistant.": "Registrer deg for en gratis Grist-konto for å bruke AI-formelassistenten.",
|
||||
"Clear Conversation": "Tøm samtale",
|
||||
"New Chat": "Ny sludring",
|
||||
"Code View": "Kodevisning",
|
||||
"Apply": "Bruk",
|
||||
"Learn more": "Lær mer",
|
||||
"Regenerate": "Regenerer",
|
||||
"Community": "Gemenskap",
|
||||
"I can only help with formulas. I cannot build tables, columns, and views, or write access rules.": "Jeg kan kun hjelpe deg med formler. Jeg kan ikke bygge tabeller, kolonner, visninger, eller skrive tilgangsregler.",
|
||||
"Hi, I'm the Grist Formula AI Assistant.": "Hei. Jeg er Grists AI-formelassistent.",
|
||||
"Preview": "Forhåndsvis",
|
||||
"Ask the bot.": "Spør botten.",
|
||||
"Function List": "Funksjonsliste",
|
||||
"For higher limits, contact the site owner.": "Kontakt sidens eier for høyere grenser.",
|
||||
"Tips": "Tips",
|
||||
"Save": "Lagre",
|
||||
"Sign Up for Free": "Registrer deg gratis",
|
||||
"Formula Cheat Sheet": "Formel-jukseark",
|
||||
"Grist's AI Assistance": "Grists AI-assistanse",
|
||||
"Formula AI Assistant is only available for logged in users.": "AI-formelassistenten er kun tilgjengelig for innloggede brukere.",
|
||||
"Grist's AI Formula Assistance. ": "Grists AI-formelassistanse ",
|
||||
"upgrade to the Pro Team plan": "oppgrader til proff lagplan",
|
||||
"You have used all available credits.": "Du har brukt alle tilgjengelige planpoeng.",
|
||||
"upgrade your plan": "oppgrader din plan",
|
||||
"Formula Help. ": "Formelhjelp. ",
|
||||
"You have {{numCredits}} remaining credits.": "Du har {{numCredits}} planpoeng igjen.",
|
||||
"Capabilities": "Ferdigeter",
|
||||
"What do you need help with?": "Hva trenger du hjelp med?",
|
||||
"Cancel": "Avbryt",
|
||||
"Need help? Our AI assistant can help.": "Hjelp? Vår AI-assistent kan hjelpe deg.",
|
||||
"AI Assistant": "AI-assistant",
|
||||
"For higher limits, {{upgradeNudge}}.": "{{upgradeNudge}} for høyere grenser.",
|
||||
"There are some things you should know when working with me:": "Det er noen ting du burde vite når du jobber med meg:"
|
||||
},
|
||||
"FloatingPopup": {
|
||||
"Maximize": "Maksimer",
|
||||
"Minimize": "Minimer"
|
||||
},
|
||||
"Clipboard": {
|
||||
"Unavailable Command": "Utilgjengelig kommando",
|
||||
"Got it": "Skjønner"
|
||||
},
|
||||
"SupportGristPage": {
|
||||
"You have opted out of telemetry.": "Du har reservert deg mot datainnsamling.",
|
||||
"Support Grist": "Støtt Grist",
|
||||
"Opt out of Telemetry": "Skru av datainnsamling",
|
||||
"GitHub Sponsors page": "GitHub Sponsor-side",
|
||||
"Sponsor Grist Labs on GitHub": "Spons Grist Labs på GitHub",
|
||||
"Manage Sponsorship": "Håndter sponsing",
|
||||
"Help Center": "Hjelpesenter",
|
||||
"We only collect usage statistics, as detailed in our {{link}}, never document contents.": "Kun bruksstatistikk samles inn og aldri dokumentinnhold. Dette er beskrevet nøye på {{link}}.",
|
||||
"You can opt out of telemetry at any time from this page.": "Du kan motsette deg datainnsamling når som helst fra denne siden.",
|
||||
"Home": "Hjem",
|
||||
"This instance is opted out of telemetry. Only the site administrator has permission to change this.": "Denne instansen har reservert seg mot datainnsamling. Kun sidens administrator har tilgang til å endre dette.",
|
||||
"Telemetry": "Telemetri",
|
||||
"Opt in to Telemetry": "Tillat datainnsamling",
|
||||
"You have opted in to telemetry. Thank you!": "Du har reservert deg mot datainnsamling. Takk.",
|
||||
"This instance is opted in to telemetry. Only the site administrator has permission to change this.": "Denne instansen har påslått datainnsamling. Kun sidens administrator har mulighet til å endre dette.",
|
||||
"GitHub": "GitHub"
|
||||
},
|
||||
"UserManager": {
|
||||
"Anyone with link ": "Alle med lenken ",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{name}}.": "Når du har fjernet din egen tilgang vil du ikke kunne logge inn igjen uten hjelp fra noen andre som har tilstrekkelig tilgang til {{name}}.",
|
||||
"{{limitAt}} of {{limitTop}} {{collaborator}}s": "{{limitAt}} av {{limitTop}} {{collaborator}}s",
|
||||
"Your role for this team site": "Din rolle på denne lagsiden",
|
||||
"Copy Link": "Kopier lenke",
|
||||
"User has view access to {{resource}} resulting from manually-set access to resources inside. If removed here, this user will lose access to resources inside.": "Bruker har visningstilgang for {{resource}}, som følge av manuelt satt tilgang til ressursene som finnes der. Hvis fjernet her vil brukeren miste tilgang til ressursene i {{resource}}.",
|
||||
"User may not modify their own access.": "Brukere kan ikke endre egen tilgang.",
|
||||
"member": "medlem",
|
||||
"Add {{member}} to your team": "Legg til {{member}} på laget ditt",
|
||||
"Collaborator": "Medarbeider",
|
||||
"Link copied to clipboard": "Lenke kopiert til utklippstavlen",
|
||||
"team site": "lagside",
|
||||
"Create a team to share with more people": "Opprett et lag for å dele med flere",
|
||||
"guest": "gjest",
|
||||
"Public access: ": "Offentlig tilgang: ",
|
||||
"Team member": "Lagmedlem",
|
||||
"Manage members of team site": "Håndter medlemmer av lagside",
|
||||
"Off": "Av",
|
||||
"free collaborator": "ledig medarbeider",
|
||||
"Save & ": "Lagre og ",
|
||||
"Outside collaborator": "Medarbeider annensteds fra",
|
||||
"{{collaborator}} limit exceeded": "{{collaborator}}-grense overskredet",
|
||||
"User inherits permissions from {{parent})}. To remove, set 'Inherit access' option to 'None'.": "Bruker nedarver tilganger fra {{parent})}. Fjern ved å sette «Nedarv tilgang» til «Ingen».",
|
||||
"Your role for this {{resourceType}}": "Din rolle i denne {{resourceType}}",
|
||||
"Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{resourceType}}.": "Når du har fjernet din egen tilgang vil du ikke kunne logge inn igjen uten hjelp fra noen andre som har tilstrekkelig tilgang til {{resourceType}}.",
|
||||
"Close": "Lukk",
|
||||
"Allow anyone with the link to open.": "Tillat alle med lenken å åpne.",
|
||||
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "Ingen forvalgt tilgang tillater tilgang å innvilges til individuelle dokumenter eller arbeidsoråder, snarere enn hele lagsiden.",
|
||||
"Invite people to {{resourceType}}": "Inviter noen til {{resourceType}}",
|
||||
"Public access inherited from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "Offentlig tilgang nedarvet fra {{parent}}. Fjern den ved å sette «Nedarv tilgang» til «Ingen».",
|
||||
"Remove my access": "Fjern min tilgang",
|
||||
"Public access": "Offentlig tilgang",
|
||||
"Public Access": "Offentlig tilgang",
|
||||
"Cancel": "Avbryt",
|
||||
"Grist support": "Grist-støtte",
|
||||
"You are about to remove your own access to this {{resourceType}}": "Du er i ferd med å fjerne din egen tilgang til denne {{resourceType}}",
|
||||
"User inherits permissions from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "Bruker nedarver tilganger fra {{parent}}. For å fjerne sett «Nedarv tilgang» til «Ingen».",
|
||||
"Guest": "Gjest",
|
||||
"Invite multiple": "Inviter flere",
|
||||
"Confirm": "Bekreft",
|
||||
"On": "På",
|
||||
"Open Access Rules": "Åpne tilgangsregler",
|
||||
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "Ingen forvalgt tilgang tillater tilgang å innvilges til individuelle dokumenter eller arbeidsoråder, snarere enn hele lagsiden."
|
||||
},
|
||||
"PagePanels": {
|
||||
"Open Creator Panel": "Åpne skaperpanel",
|
||||
"Close Creator Panel": "Lukk skaperpanel"
|
||||
},
|
||||
"buildViewSectionDom": {
|
||||
"Not all data is shown": "Ikke all dataen vises",
|
||||
"No row selected in {{title}}": "Ingen rader valgt i {{title}}",
|
||||
"No data": "Ingen data"
|
||||
},
|
||||
"ColumnTitle": {
|
||||
"Column ID copied to clipboard": "Kolonne-ID kopiert til utklippstavle",
|
||||
"Add description": "Legg til beskrivelse",
|
||||
"Column description": "Kolonnebeskrivelse",
|
||||
"COLUMN ID: ": "Kolonne-ID: ",
|
||||
"Provide a column label": "Angi en kolonneetikett",
|
||||
"Close": "Lukk",
|
||||
"Cancel": "Avbryt",
|
||||
"Column label": "Kolonneetikett",
|
||||
"Save": "Lagre"
|
||||
},
|
||||
"SupportGristNudge": {
|
||||
"Support Grist": "Støtt Grist",
|
||||
"Close": "Lukk",
|
||||
"Opt in to Telemetry": "Tillat datainnsamling",
|
||||
"Help Center": "Hjelpesenter",
|
||||
"Opted In": "Tillatt",
|
||||
"Contribute": "Bidra",
|
||||
"Support Grist page": "«Støtt Grist»-siden"
|
||||
},
|
||||
"WelcomeSitePicker": {
|
||||
"You have access to the following Grist sites.": "Du har tilgang til følgende Grist-sider.",
|
||||
"Welcome back": "Velkommen tilbake",
|
||||
"You can always switch sites using the account menu.": "Du kan alltid bytte sider fra kontomenyen."
|
||||
},
|
||||
"FieldContextMenu": {
|
||||
"Copy anchor link": "Kopier ankerlenke",
|
||||
"Hide field": "Skjul felt",
|
||||
"Copy": "Kopier",
|
||||
"Paste": "Lim inn",
|
||||
"Clear field": "Tøm felt",
|
||||
"Cut": "Klipp ut"
|
||||
},
|
||||
"DescriptionTextArea": {
|
||||
"DESCRIPTION": "Beskrivelse"
|
||||
},
|
||||
"WebhookPage": {
|
||||
"Webhook Settings": "Vevkroksinnstillinger",
|
||||
"Clear Queue": "Tøm kø"
|
||||
},
|
||||
"FloatingEditor": {
|
||||
"Collapse Editor": "Fold sammen tekstbehandler"
|
||||
},
|
||||
"GridView": {
|
||||
"Click to insert": "Klikk for å sette inn"
|
||||
},
|
||||
"searchDropdown": {
|
||||
"Search": "Søk"
|
||||
},
|
||||
"SearchModel": {
|
||||
"Search all tables": "Søk i alle tabeller",
|
||||
"Search all pages": "Søk på alle sider"
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,9 @@
|
||||
"Sign Out": "Wyloguj się",
|
||||
"Sign in": "Zaloguj się",
|
||||
"Switch Accounts": "Przełącz konta",
|
||||
"Toggle Mobile Mode": "Przełącz na tryb mobilny"
|
||||
"Toggle Mobile Mode": "Przełącz na tryb mobilny",
|
||||
"Activation": "Aktywacja",
|
||||
"Sign In": "Zaloguj się"
|
||||
},
|
||||
"ViewAsDropdown": {
|
||||
"View As": "Wyświetl jako",
|
||||
@ -791,7 +793,7 @@
|
||||
},
|
||||
"OnBoardingPopups": {
|
||||
"Next": "Następny",
|
||||
"Finish": "Skończyć"
|
||||
"Finish": "Zakończ"
|
||||
},
|
||||
"SortFilterConfig": {
|
||||
"Revert": "Przywrócić",
|
||||
|
@ -207,7 +207,9 @@
|
||||
"{{wrongTypeCount}} non-{{columnType}} column is not shown": "{{wrongTypeCount}} a não-{{columnType}} coluna não é mostrada",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown": "{{wrongTypeCount}} as não-{{columnType}} colunas não são mostradas",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} a não-{{columnType}} coluna não é mostrada",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} as não-{{columnType}} colunas não são mostradas"
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} as não-{{columnType}} colunas não são mostradas",
|
||||
"No {{columnType}} columns in table.": "Não há colunas {{columnType}} na tabela.",
|
||||
"Clear selection": "Limpar seleção"
|
||||
},
|
||||
"DataTables": {
|
||||
"Click to copy": "Clique para copiar",
|
||||
|
@ -164,7 +164,9 @@
|
||||
"Select Custom Widget": "Выбор пользовательского виджета",
|
||||
"Pick a column": "Выберать столбец",
|
||||
"Read selected table": "Просмотр выбранной таблицы",
|
||||
"Widget does not require any permissions.": "Виджет не требует никаких разрешений."
|
||||
"Widget does not require any permissions.": "Виджет не требует никаких разрешений.",
|
||||
"No {{columnType}} columns in table.": "Нет {{columnType}} столбцов в таблице.",
|
||||
"Clear selection": "Очистить выбор"
|
||||
},
|
||||
"AccountWidget": {
|
||||
"Access Details": "Сведения о доступе",
|
||||
@ -769,7 +771,10 @@
|
||||
"Contact support": "Обратитесь в службу поддержки",
|
||||
"Sign in to access this organization's documents.": "Войдите, чтобы получить доступ к документам этой организации.",
|
||||
"The requested page could not be found.{{separator}}Please check the URL and try again.": "Запрашиваемая страница не может быть найдена.{{separator}}Пожалуйста, проверьте URL-адрес и повторите попытку.",
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Вы вошли как {{email}}. Вы можете войти с другой учетной записью или запросить доступ у администратора."
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Вы вошли как {{email}}. Вы можете войти с другой учетной записью или запросить доступ у администратора.",
|
||||
"Account deleted{{suffix}}": "Аккаунт удален{{suffix}}",
|
||||
"Your account has been deleted.": "Ваш аккаунт был удален.",
|
||||
"Sign up": "Регистрация"
|
||||
},
|
||||
"CellStyle": {
|
||||
"CELL STYLE": "СТИЛЬ ЯЧЕЙКИ",
|
||||
|
@ -61,7 +61,9 @@
|
||||
"Save Document": "Зберегти документ",
|
||||
"Send to Google Drive": "Надіслати на Google Drive",
|
||||
"Show in folder": "Показати в папці",
|
||||
"Work on a Copy": "Працювати над копією"
|
||||
"Work on a Copy": "Працювати над копією",
|
||||
"Download...": "Завантажити...",
|
||||
"Share": "Поділитися"
|
||||
},
|
||||
"MakeCopyMenu": {
|
||||
"Be careful, the original has changes not in this document. Those changes will be overwritten.": "Зверніть увагу! В оригіналі є зміни, яких немає в цьому документі. Ці зміни будуть перезаписані.",
|
||||
@ -86,7 +88,10 @@
|
||||
"Workspace": "Робочий простір",
|
||||
"You do not have write access to the selected workspace": "Ви не маєте права на запис у вибраному робочому просторі",
|
||||
"You do not have write access to this site": "Ви не маєте права на запис на цьому сайті",
|
||||
"However, it appears to be already identical.": "Втім, схоже, що вони вже ідентичні."
|
||||
"However, it appears to be already identical.": "Втім, схоже, що вони вже ідентичні.",
|
||||
"Remove all data but keep the structure to use as a template": "Видалити всі дані, але зберегти структуру, щоб використовувати як шаблон",
|
||||
"Remove document history (can significantly reduce file size)": "Видалити історію документа (може значно зменшити розмір файлу)",
|
||||
"Download full document and history": "Завантажити повний документ та історію"
|
||||
},
|
||||
"SortConfig": {
|
||||
"Add Column": "Додати стовпець",
|
||||
@ -134,7 +139,7 @@
|
||||
"Freeze {{count}} columns_other": "Закріпити {{count}} стовпців",
|
||||
"Hide {{count}} columns_other": "Сховати {{count}} стовпців",
|
||||
"Insert column to the {{to}}": "Вставити стовпець у {{to}}",
|
||||
"More sort options ...": "Більше варіантів сортування…",
|
||||
"More sort options ...": "Більше опцій сортування…",
|
||||
"Rename column": "Перейменувати стовпець",
|
||||
"Reset {{count}} columns_one": "Скинути стовпець",
|
||||
"Reset {{count}} entire columns_one": "Скинути весь стовпець",
|
||||
@ -144,7 +149,9 @@
|
||||
"Sorted (#{{count}})_other": "Відсортовано (#{{count}})",
|
||||
"Unfreeze all columns": "Відкріпити всі стовпці",
|
||||
"Unfreeze {{count}} columns_one": "Відкріпити цей стовпець",
|
||||
"Unfreeze {{count}} columns_other": "Відкріпити {{count}} стовпців"
|
||||
"Unfreeze {{count}} columns_other": "Відкріпити {{count}} стовпців",
|
||||
"Insert column to the right": "Вставити стовпець праворуч",
|
||||
"Insert column to the left": "Вставити стовпець ліворуч"
|
||||
},
|
||||
"ThemeConfig": {
|
||||
"Switch appearance automatically to match system": "Автоматично змінювати оформлення відповідно до системи",
|
||||
@ -170,7 +177,12 @@
|
||||
"Welcome to {{orgName}}": "Ласкаво просимо до {{orgName}}",
|
||||
"You have read-only access to this site. Currently there are no documents.": "Ви маєте доступ до цього сайту лише для читання. Наразі документів немає.",
|
||||
"personal site": "особистий сайт",
|
||||
"{{signUp}} to save your work. ": "{{signUp}}, щоб зберегти вашу роботу. "
|
||||
"{{signUp}} to save your work. ": "{{signUp}}, щоб зберегти вашу роботу. ",
|
||||
"Welcome to Grist, {{- name}}!": "Ласкаво просимо до Grist, {{- name}}!",
|
||||
"Visit our {{link}} to learn more about Grist.": "Відвідайте наш {{link}}, щоб дізнатися більше про Grist.",
|
||||
"Welcome to {{- orgName}}": "Ласкаво просимо до {{- orgName}}",
|
||||
"Sign in": "Увійти в систему",
|
||||
"To use Grist, please either sign up or sign in.": "Щоб користуватися Grist, будь ласка, зареєструйтеся або увійдіть."
|
||||
},
|
||||
"NotifyUI": {
|
||||
"Go to your free personal site": "Перейдіть на свій безкоштовний персональний сайт",
|
||||
@ -181,7 +193,8 @@
|
||||
"Notifications": "Повідомлення",
|
||||
"Renew": "Поновити",
|
||||
"Report a problem": "Повідомити про проблему",
|
||||
"Upgrade Plan": "Змінити тарифний план"
|
||||
"Upgrade Plan": "Змінити тарифний план",
|
||||
"Manage billing": "Управління рахунком"
|
||||
},
|
||||
"ViewSectionMenu": {
|
||||
"Revert": "Повернутися",
|
||||
@ -268,7 +281,9 @@
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} не-{{columnType}} стовпець не відображається",
|
||||
"{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} не-{{columnType}} стовпці не відображаються",
|
||||
"Widget does not require any permissions.": "Віджет не вимагає ніяких дозволів.",
|
||||
"Widget needs to {{read}} the current table.": "Віджету необхідно {{read}} поточну таблицю."
|
||||
"Widget needs to {{read}} the current table.": "Віджету необхідно {{read}} поточну таблицю.",
|
||||
"No {{columnType}} columns in table.": "У таблиці немає стовпців типу {{columnType}}.",
|
||||
"Clear selection": "Очистити вибір"
|
||||
},
|
||||
"ACUserManager": {
|
||||
"Invite new member": "Запросити нового користувача",
|
||||
@ -310,7 +325,9 @@
|
||||
"Remove column {{- colId }} from {{- tableId }} rules": "Видалити стовпець {{- colId }} з правил {{- tableId }}",
|
||||
"When adding table rules, automatically add a rule to grant OWNER full access.": "При додаванні правил таблиці, автоматично додавати правило для надання ВЛАСНИКУ повного доступу.",
|
||||
"Seed rules": "Успадковані правила",
|
||||
"Allow everyone to copy the entire document, or view it in full in fiddle mode.\nUseful for examples and templates, but not for sensitive data.": "Дозволити кожному скопіювати весь документ або переглянути його повністю в режимі створення нових копій.\nКорисно для прикладів і шаблонів, але не для конфіденційних даних."
|
||||
"Allow everyone to copy the entire document, or view it in full in fiddle mode.\nUseful for examples and templates, but not for sensitive data.": "Дозволити кожному скопіювати весь документ або переглянути його повністю в режимі створення нових копій.\nКорисно для прикладів і шаблонів, але не для конфіденційних даних.",
|
||||
"Allow editors to edit structure (e.g. modify and delete tables, columns, layouts), and to write formulas, which give access to all data regardless of read restrictions.": "Дозволити редакторам редагувати структуру (наприклад, змінювати та видаляти таблиці, стовпці, макети), а також писати формули, які надають доступ до всіх даних незалежно від обмежень на читання.",
|
||||
"This default should be changed if editors' access is to be limited. ": "Цей параметр слід змінити, якщо ви хочете обмежити доступ редакторів. "
|
||||
},
|
||||
"AccountPage": {
|
||||
"API": "API",
|
||||
@ -341,7 +358,14 @@
|
||||
"Sign in": "Увійти в систему",
|
||||
"Toggle Mobile Mode": "Переключити в мобільний режим",
|
||||
"Document Settings": "Параметри документа",
|
||||
"Switch Accounts": "Змінити обліковий запис"
|
||||
"Switch Accounts": "Змінити обліковий запис",
|
||||
"Activation": "Активація",
|
||||
"Support Grist": "Підтримати Grist",
|
||||
"Upgrade Plan": "Оновити План",
|
||||
"Use This Template": "Використати цей шаблон",
|
||||
"Billing Account": "Рахунок оплати",
|
||||
"Sign In": "Увійти в систему",
|
||||
"Sign Up": "Зареєструватися"
|
||||
},
|
||||
"ViewAsDropdown": {
|
||||
"View As": "Переглянути як",
|
||||
@ -352,7 +376,8 @@
|
||||
"Action Log failed to load": "Не вдалося завантажити журнал дій",
|
||||
"Table {{tableId}} was subsequently removed in action #{{actionNum}}": "Таблиця {{tableId}} згодом була видалена під час події #{{actionNum}}",
|
||||
"This row was subsequently removed in action {{action.actionNum}}": "Ця строка згодом була видалена під час події {{action.actionNum}}",
|
||||
"Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "Колонка {{colId}} згодом була видалена під час події #{{action.actionNum}}"
|
||||
"Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "Колонка {{colId}} згодом була видалена під час події #{{action.actionNum}}",
|
||||
"All tables": "Всі таблиці"
|
||||
},
|
||||
"ApiKey": {
|
||||
"Click to show": "Натисніть, щоб показати",
|
||||
@ -377,7 +402,8 @@
|
||||
"Home Page": "Домашня сторінка",
|
||||
"Legacy": "Застаріла версія",
|
||||
"Personal Site": "Особистий сайт",
|
||||
"Team Site": "Сайт команди"
|
||||
"Team Site": "Сайт команди",
|
||||
"Grist Templates": "Шаблони від Grist"
|
||||
},
|
||||
"CellContextMenu": {
|
||||
"Clear cell": "Очистити клітинку",
|
||||
@ -398,7 +424,11 @@
|
||||
"Reset {{count}} columns_one": "Скинути стовпець",
|
||||
"Reset {{count}} columns_other": "Скинути {{count}} стовпці",
|
||||
"Reset {{count}} entire columns_one": "Скинути весь стовпець",
|
||||
"Reset {{count}} entire columns_other": "Скинути {{count}} всі стовпці"
|
||||
"Reset {{count}} entire columns_other": "Скинути {{count}} всі стовпці",
|
||||
"Copy": "Копіювати",
|
||||
"Comment": "Коментар",
|
||||
"Cut": "Вирізати",
|
||||
"Paste": "Вставити"
|
||||
},
|
||||
"ColorSelect": {
|
||||
"Apply": "Застосовувати",
|
||||
@ -487,7 +517,9 @@
|
||||
"API": "API",
|
||||
"Document ID copied to clipboard": "Ідентифікатор документа скопійований у буфер",
|
||||
"Ok": "ОК",
|
||||
"Engine (experimental {{span}} change at own risk):": "Обчислювальна система (експериментальна версія {{span}} змінюйте на власний ризик):"
|
||||
"Engine (experimental {{span}} change at own risk):": "Обчислювальна система (експериментальна версія {{span}} змінюйте на власний ризик):",
|
||||
"Manage Webhooks": "Керування веб-хуками",
|
||||
"Webhooks": "Веб-хуки"
|
||||
},
|
||||
"DocTour": {
|
||||
"No valid document tour": "Немає дійсного огляду документа",
|
||||
@ -516,7 +548,8 @@
|
||||
"GristDoc": {
|
||||
"Added new linked section to view {{viewName}}": "Додано новий пов’язаний розділ для перегляду {{viewName}}",
|
||||
"Import from file": "Імпортувати з файлу",
|
||||
"Saved linked section {{title}} in view {{name}}": "Збережений зв'язаний розділ {{title}}, що відображається в {{name}}"
|
||||
"Saved linked section {{title}} in view {{name}}": "Збережений зв'язаний розділ {{title}}, що відображається в {{name}}",
|
||||
"go to webhook settings": "перейти до налаштувань веб-хуків"
|
||||
},
|
||||
"HomeLeftPane": {
|
||||
"Delete": "Видалити",
|
||||
@ -526,17 +559,33 @@
|
||||
"Manage Users": "Керувати користувачами",
|
||||
"Delete {{workspace}} and all included documents?": "Видалити {{workspace}} та всі включені документи?",
|
||||
"Create Workspace": "Створити робочій простір",
|
||||
"Examples & Templates": "Приклади та шаблони",
|
||||
"Examples & Templates": "Шаблони",
|
||||
"Import Document": "Імпортувати документ",
|
||||
"Trash": "Кошик",
|
||||
"Workspace will be moved to Trash.": "Робочий простір буде переміщено до кошика.",
|
||||
"Rename": "Перейменувати",
|
||||
"Workspaces": "Робочій простір"
|
||||
"Workspaces": "Робочій простір",
|
||||
"Tutorial": "Туторіал"
|
||||
},
|
||||
"Importer": {
|
||||
"Merge rows that match these fields:": "Об'єднати рядки, які відповідають цим полям:",
|
||||
"Update existing records": "Оновити існуючі записи",
|
||||
"Select fields to match on": "Оберіть поля для зіставлення"
|
||||
"Select fields to match on": "Оберіть поля для зіставлення",
|
||||
"Column mapping": "Співставлення колонок",
|
||||
"Grist column": "Стовпець у Grist",
|
||||
"{{count}} unmatched field_one": "{{count}} невідповідне поле",
|
||||
"{{count}} unmatched field in import_one": "{{count}} невідповідне поле при імпорті",
|
||||
"Revert": "Повернути",
|
||||
"Skip Import": "Пропустити імпорт",
|
||||
"{{count}} unmatched field_other": "{{count}} невідповідних полів",
|
||||
"New Table": "Нова таблиця",
|
||||
"Skip": "Пропустити",
|
||||
"Column Mapping": "Співставлення колонок",
|
||||
"Destination table": "Таблиця призначення",
|
||||
"Skip Table on Import": "Пропустити таблицю при імпорті",
|
||||
"Import from file": "Імпорт з файлу",
|
||||
"{{count}} unmatched field in import_other": "{{count}} невідповідних полів при імпорті",
|
||||
"Source column": "Вихідний стовпець"
|
||||
},
|
||||
"LeftPanelCommon": {
|
||||
"Help Center": "Центр допомоги"
|
||||
@ -609,12 +658,13 @@
|
||||
"Select Widget": "Виберіть віджет",
|
||||
"Series_one": "Серії",
|
||||
"Series_other": "Серії",
|
||||
"Sort & Filter": "Сортування та фільтрація",
|
||||
"Sort & Filter": "Порядок та фільтр",
|
||||
"TRANSFORM": "ПЕРЕТВОРИТИ",
|
||||
"Theme": "Тема",
|
||||
"WIDGET TITLE": "НАЗВА ВІДЖЕТА",
|
||||
"Widget": "Віджет",
|
||||
"You do not have edit access to this document": "Ви не маєте права на редагування цього документа"
|
||||
"You do not have edit access to this document": "Ви не маєте права на редагування цього документа",
|
||||
"Add referenced columns": "Додати стовпець за посиланням"
|
||||
},
|
||||
"RowContextMenu": {
|
||||
"Copy anchor link": "Скопіювати якірне посилання",
|
||||
@ -679,7 +729,7 @@
|
||||
"Make On-Demand": "Встановити статус \"на вимогу\""
|
||||
},
|
||||
"ViewLayoutMenu": {
|
||||
"Advanced Sort & Filter": "Розширене сортування та фільтр",
|
||||
"Advanced Sort & Filter": "Розширені порядок та фільтр",
|
||||
"Copy anchor link": "Скопіювати якірне посилання",
|
||||
"Data selection": "Вибір даних",
|
||||
"Delete record": "Видалити запис",
|
||||
@ -718,7 +768,8 @@
|
||||
"Override widget title": "Перевизначити назву віджета",
|
||||
"Provide a table name": "Вкажіть ім'я таблиці",
|
||||
"Save": "Зберегти",
|
||||
"WIDGET TITLE": "НАЗВА ВІДЖЕТА"
|
||||
"WIDGET TITLE": "НАЗВА ВІДЖЕТА",
|
||||
"WIDGET DESCRIPTION": "ОПИС ВІДЖЕТУ"
|
||||
},
|
||||
"breadcrumbs": {
|
||||
"fiddle": "fiddle",
|
||||
@ -749,7 +800,10 @@
|
||||
"You do not have access to this organization's documents.": "У вас немає доступу до документів цієї організації.",
|
||||
"Sign in to access this organization's documents.": "Увійдіть, щоб отримати доступ до документів цієї організації.",
|
||||
"The requested page could not be found.{{separator}}Please check the URL and try again.": "Не вдалося знайти запитувану сторінку.{{separator}}Будь ласка, перевірте URL-адресу і спробуйте ще раз.",
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Ви увійшли як {{email}}. Ви можете увійти під іншим обліковим записом або попросити доступ у адміністратора."
|
||||
"You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Ви увійшли як {{email}}. Ви можете увійти під іншим обліковим записом або попросити доступ у адміністратора.",
|
||||
"Account deleted{{suffix}}": "Обліковий запис видалено{{suffix}}",
|
||||
"Your account has been deleted.": "Ваш обліковий запис видалено.",
|
||||
"Sign up": "Зареєструватися"
|
||||
},
|
||||
"menus": {
|
||||
"* Workspaces are available on team plans. ": "* Робочі простори доступні в командних тарифах. ",
|
||||
@ -783,7 +837,8 @@
|
||||
"Find Next ": "Знайти наступне ",
|
||||
"Find Previous ": "Знайти попереднє ",
|
||||
"Search in document": "Пошук у документі",
|
||||
"No results": "Немає результатів"
|
||||
"No results": "Немає результатів",
|
||||
"Search": "Пошук"
|
||||
},
|
||||
"sendToDrive": {
|
||||
"Sending file to Google Drive": "Надсилання файлу на Google Диск"
|
||||
@ -809,7 +864,10 @@
|
||||
"Default cell style": "Стиль клітинки за замовчуванням",
|
||||
"Mixed style": "Змішаний стиль",
|
||||
"Open row styles": "Відкрити стилі рядків",
|
||||
"Cell Style": "Стиль клітинки"
|
||||
"Cell Style": "Стиль клітинки",
|
||||
"HEADER STYLE": "СТИЛЬ ЗАГОЛОВКА",
|
||||
"Header Style": "Стиль Заголовка",
|
||||
"Default header style": "За замовчуванням"
|
||||
},
|
||||
"ChoiceTextBox": {
|
||||
"CHOICES": "ВАРІАНТИ"
|
||||
|
@ -230,6 +230,14 @@ describe('DocApi', function () {
|
||||
|
||||
// Contains the tests. This is where you want to add more test.
|
||||
function testDocApi() {
|
||||
async function generateDocAndUrl(docName: string = "Dummy") {
|
||||
const wid = (await userApi.getOrgWorkspaces('current')).find((w) => w.name === 'Private')!.id;
|
||||
const docId = await userApi.newDoc({name: docName}, wid);
|
||||
const docUrl = `${serverUrl}/api/docs/${docId}`;
|
||||
const tableUrl = `${serverUrl}/api/docs/${docId}/tables/Table1`;
|
||||
return { docUrl, tableUrl, docId };
|
||||
}
|
||||
|
||||
it("creator should be owner of a created ws", async () => {
|
||||
const kiwiEmail = 'kiwi@getgrist.com';
|
||||
const ws1 = (await userApi.getOrgWorkspaces('current'))[0].id;
|
||||
@ -547,9 +555,7 @@ function testDocApi() {
|
||||
}
|
||||
|
||||
it("GET /docs/{did}/tables/{tid}/data retrieves data in column format", async function () {
|
||||
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/data`, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
assert.deepEqual(resp.data, {
|
||||
const data = {
|
||||
id: [1, 2, 3, 4],
|
||||
A: ['hello', '', '', ''],
|
||||
B: ['', 'world', '', ''],
|
||||
@ -557,68 +563,73 @@ function testDocApi() {
|
||||
D: [null, null, null, null],
|
||||
E: ['HELLO', '', '', ''],
|
||||
manualSort: [1, 2, 3, 4]
|
||||
});
|
||||
};
|
||||
const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/data`, chimpy);
|
||||
assert.equal(respWithTableId.status, 200);
|
||||
assert.deepEqual(respWithTableId.data, data);
|
||||
const respWithTableRef = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/data`, chimpy);
|
||||
assert.equal(respWithTableRef.status, 200);
|
||||
assert.deepEqual(respWithTableRef.data, data);
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/tables/{tid}/records retrieves data in records format", async function () {
|
||||
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
assert.deepEqual(resp.data,
|
||||
{
|
||||
records:
|
||||
[
|
||||
{
|
||||
id: 1,
|
||||
fields: {
|
||||
A: 'hello',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: 'HELLO',
|
||||
},
|
||||
const data = {
|
||||
records:
|
||||
[
|
||||
{
|
||||
id: 1,
|
||||
fields: {
|
||||
A: 'hello',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: 'HELLO',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
fields: {
|
||||
A: '',
|
||||
B: 'world',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
fields: {
|
||||
A: '',
|
||||
B: 'world',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
fields: {
|
||||
A: '',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
fields: {
|
||||
A: '',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
fields: {
|
||||
A: '',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
fields: {
|
||||
A: '',
|
||||
B: '',
|
||||
C: '',
|
||||
D: null,
|
||||
E: '',
|
||||
},
|
||||
]
|
||||
});
|
||||
},
|
||||
]
|
||||
};
|
||||
const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, chimpy);
|
||||
assert.equal(respWithTableId.status, 200);
|
||||
assert.deepEqual(respWithTableId.data, data);
|
||||
const respWithTableRef = await axios.get(
|
||||
`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/records`, chimpy);
|
||||
assert.equal(respWithTableRef.status, 200);
|
||||
assert.deepEqual(respWithTableRef.data, data);
|
||||
});
|
||||
|
||||
it('GET /docs/{did}/tables/{tid}/records honors the "hidden" param', async function () {
|
||||
const params = { hidden: true };
|
||||
const resp = await axios.get(
|
||||
`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`,
|
||||
{...chimpy, params }
|
||||
);
|
||||
assert.equal(resp.status, 200);
|
||||
assert.deepEqual(resp.data.records[0], {
|
||||
const data = {
|
||||
id: 1,
|
||||
fields: {
|
||||
manualSort: 1,
|
||||
@ -628,7 +639,19 @@ function testDocApi() {
|
||||
D: null,
|
||||
E: 'HELLO',
|
||||
},
|
||||
});
|
||||
};
|
||||
const respWithTableId = await axios.get(
|
||||
`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`,
|
||||
{...chimpy, params }
|
||||
);
|
||||
assert.equal(respWithTableId.status, 200);
|
||||
assert.deepEqual(respWithTableId.data.records[0], data);
|
||||
const respWithTableRef = await axios.get(
|
||||
`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/records`,
|
||||
{...chimpy, params }
|
||||
);
|
||||
assert.equal(respWithTableRef.status, 200);
|
||||
assert.deepEqual(respWithTableRef.data.records[0], data);
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/tables/{tid}/records handles errors and hidden columns", async function () {
|
||||
@ -683,119 +706,121 @@ function testDocApi() {
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/tables/{tid}/columns retrieves columns", async function () {
|
||||
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/columns`, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
assert.deepEqual(resp.data,
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
id: 'A',
|
||||
fields: {
|
||||
colRef: 2,
|
||||
parentId: 1,
|
||||
parentPos: 1,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'A',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
fields: {
|
||||
colRef: 3,
|
||||
parentId: 1,
|
||||
parentPos: 2,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'B',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'C',
|
||||
fields: {
|
||||
colRef: 4,
|
||||
parentId: 1,
|
||||
parentPos: 3,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'C',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'D',
|
||||
fields: {
|
||||
colRef: 5,
|
||||
parentId: 1,
|
||||
parentPos: 3,
|
||||
type: 'Any',
|
||||
widgetOptions: '',
|
||||
isFormula: true,
|
||||
formula: '',
|
||||
label: 'D',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'E',
|
||||
fields: {
|
||||
colRef: 6,
|
||||
parentId: 1,
|
||||
parentPos: 4,
|
||||
type: 'Any',
|
||||
widgetOptions: '',
|
||||
isFormula: true,
|
||||
formula: '$A.upper()',
|
||||
label: 'E',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
const data = {
|
||||
columns: [
|
||||
{
|
||||
id: 'A',
|
||||
fields: {
|
||||
colRef: 2,
|
||||
parentId: 1,
|
||||
parentPos: 1,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'A',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
fields: {
|
||||
colRef: 3,
|
||||
parentId: 1,
|
||||
parentPos: 2,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'B',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'C',
|
||||
fields: {
|
||||
colRef: 4,
|
||||
parentId: 1,
|
||||
parentPos: 3,
|
||||
type: 'Text',
|
||||
widgetOptions: '',
|
||||
isFormula: false,
|
||||
formula: '',
|
||||
label: 'C',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'D',
|
||||
fields: {
|
||||
colRef: 5,
|
||||
parentId: 1,
|
||||
parentPos: 3,
|
||||
type: 'Any',
|
||||
widgetOptions: '',
|
||||
isFormula: true,
|
||||
formula: '',
|
||||
label: 'D',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'E',
|
||||
fields: {
|
||||
colRef: 6,
|
||||
parentId: 1,
|
||||
parentPos: 4,
|
||||
type: 'Any',
|
||||
widgetOptions: '',
|
||||
isFormula: true,
|
||||
formula: '$A.upper()',
|
||||
label: 'E',
|
||||
description: '',
|
||||
untieColIdFromLabel: false,
|
||||
summarySourceCol: 0,
|
||||
displayCol: 0,
|
||||
visibleCol: 0,
|
||||
rules: null,
|
||||
recalcWhen: 0,
|
||||
recalcDeps: null
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/columns`, chimpy);
|
||||
assert.equal(respWithTableId.status, 200);
|
||||
assert.deepEqual(respWithTableId.data, data);
|
||||
const respWithTableRef = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/columns`, chimpy);
|
||||
assert.equal(respWithTableRef.status, 200);
|
||||
assert.deepEqual(respWithTableRef.data, data);
|
||||
});
|
||||
|
||||
it('GET /docs/{did}/tables/{tid}/columns retrieves hidden columns when "hidden" is set', async function () {
|
||||
@ -869,6 +894,13 @@ function testDocApi() {
|
||||
]
|
||||
});
|
||||
|
||||
// POST /columns: Create new columns using tableRef in URL
|
||||
resp = await axios.post(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/5/columns`, {
|
||||
columns: [{id: "NewCol6", fields: {}}],
|
||||
}, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
assert.deepEqual(resp.data, {columns: [{id: "NewCol6"}]});
|
||||
|
||||
// POST /columns to invalid table ID
|
||||
resp = await axios.post(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/NoSuchTable/columns`,
|
||||
{columns: [{}]}, chimpy);
|
||||
@ -1032,6 +1064,7 @@ function testDocApi() {
|
||||
{colId: "NewCol4", label: 'NewCol4'},
|
||||
{colId: "NewCol4_2", label: 'NewCol4_2'},
|
||||
// NewCol5 is hidden by ACL
|
||||
{colId: "NewCol6", label: 'NewCol6'},
|
||||
]);
|
||||
|
||||
resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/NewTable2_2/columns`, chimpy);
|
||||
@ -1055,13 +1088,13 @@ function testDocApi() {
|
||||
});
|
||||
|
||||
describe("/docs/{did}/tables/{tid}/columns", function () {
|
||||
async function generateDocAndUrl(docName: string = "Dummy") {
|
||||
const wid = (await userApi.getOrgWorkspaces('current')).find((w) => w.name === 'Private')!.id;
|
||||
const docId = await userApi.newDoc({name: docName}, wid);
|
||||
const url = `${serverUrl}/api/docs/${docId}/tables/Table1/columns`;
|
||||
return { url, docId };
|
||||
async function generateDocAndUrlForColumns(name: string) {
|
||||
const { tableUrl, docId } = await generateDocAndUrl(name);
|
||||
return {
|
||||
docId,
|
||||
url: `${tableUrl}/columns`,
|
||||
};
|
||||
}
|
||||
|
||||
describe("PUT /docs/{did}/tables/{tid}/columns", function () {
|
||||
async function getColumnFieldsMapById(url: string, params: any) {
|
||||
const result = await axios.get(url, {...chimpy, params});
|
||||
@ -1079,7 +1112,7 @@ function testDocApi() {
|
||||
expectedFieldsByColId: Record<string, object>,
|
||||
opts?: { getParams?: any }
|
||||
) {
|
||||
const {url} = await generateDocAndUrl('ColumnsPut');
|
||||
const {url} = await generateDocAndUrlForColumns('ColumnsPut');
|
||||
const body: ColumnsPut = { columns };
|
||||
const resp = await axios.put(url, body, {...chimpy, params});
|
||||
assert.equal(resp.status, 200);
|
||||
@ -1150,7 +1183,7 @@ function testDocApi() {
|
||||
|
||||
it('should forbid update by viewers', async function () {
|
||||
// given
|
||||
const { url, docId } = await generateDocAndUrl('ColumnsPut');
|
||||
const { url, docId } = await generateDocAndUrlForColumns('ColumnsPut');
|
||||
await userApi.updateDocPermissions(docId, {users: {'kiwi@getgrist.com': 'viewers'}});
|
||||
|
||||
// when
|
||||
@ -1162,7 +1195,7 @@ function testDocApi() {
|
||||
|
||||
it("should return 404 when table is not found", async function() {
|
||||
// given
|
||||
const { url } = await generateDocAndUrl('ColumnsPut');
|
||||
const { url } = await generateDocAndUrlForColumns('ColumnsPut');
|
||||
const notFoundUrl = url.replace("Table1", "NonExistingTable");
|
||||
|
||||
// when
|
||||
@ -1176,7 +1209,7 @@ function testDocApi() {
|
||||
|
||||
describe("DELETE /docs/{did}/tables/{tid}/columns/{colId}", function () {
|
||||
it('should delete some column', async function() {
|
||||
const {url} = await generateDocAndUrl('ColumnDelete');
|
||||
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
|
||||
const deleteUrl = url + '/A';
|
||||
const resp = await axios.delete(deleteUrl, chimpy);
|
||||
|
||||
@ -1190,7 +1223,7 @@ function testDocApi() {
|
||||
});
|
||||
|
||||
it('should return 404 if table not found', async function() {
|
||||
const {url} = await generateDocAndUrl('ColumnDelete');
|
||||
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
|
||||
const deleteUrl = url.replace("Table1", "NonExistingTable") + '/A';
|
||||
const resp = await axios.delete(deleteUrl, chimpy);
|
||||
|
||||
@ -1199,7 +1232,7 @@ function testDocApi() {
|
||||
});
|
||||
|
||||
it('should return 404 if column not found', async function() {
|
||||
const {url} = await generateDocAndUrl('ColumnDelete');
|
||||
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
|
||||
const deleteUrl = url + '/NonExistingColId';
|
||||
const resp = await axios.delete(deleteUrl, chimpy);
|
||||
|
||||
@ -1208,7 +1241,7 @@ function testDocApi() {
|
||||
});
|
||||
|
||||
it('should forbid column deletion by viewers', async function() {
|
||||
const {url, docId} = await generateDocAndUrl('ColumnDelete');
|
||||
const {url, docId} = await generateDocAndUrlForColumns('ColumnDelete');
|
||||
await userApi.updateDocPermissions(docId, {users: {'kiwi@getgrist.com': 'viewers'}});
|
||||
const deleteUrl = url + '/A';
|
||||
const resp = await axios.delete(deleteUrl, kiwi);
|
||||
@ -2584,6 +2617,25 @@ function testDocApi() {
|
||||
assert.equal(resp2.data, 'A,B\nSanta,1\nBob,11\nAlice,2\nFelix,22\n');
|
||||
});
|
||||
|
||||
it('GET /docs/{did}/download/csv with header=colId shows columns id in the header instead of their name',
|
||||
async function () {
|
||||
const { docUrl } = await generateDocAndUrl('csvWithColIdAsHeader');
|
||||
const AColRef = 2;
|
||||
const userActions = [
|
||||
['AddRecord', 'Table1', null, {A: 'a1', B: 'b1'}],
|
||||
['UpdateRecord', '_grist_Tables_column', AColRef, { untieColIdFromLabel: true }],
|
||||
['UpdateRecord', '_grist_Tables_column', AColRef, {
|
||||
label: 'Column label for A',
|
||||
colId: 'AColId'
|
||||
}]
|
||||
];
|
||||
const resp = await axios.post(`${docUrl}/apply`, userActions, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
const csvResp = await axios.get(`${docUrl}/download/csv?tableId=Table1&header=colId`, chimpy);
|
||||
assert.equal(csvResp.status, 200);
|
||||
assert.equal(csvResp.data, 'AColId,B,C\na1,b1,\n');
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/download/csv respects permissions", async function () {
|
||||
// kiwi has no access to TestDoc
|
||||
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.TestDoc}/download/csv?tableId=Table1`, kiwi);
|
||||
|
Loading…
Reference in New Issue
Block a user