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
cea0404a22
107
README.md
107
README.md
@ -1,23 +1,12 @@
|
||||
# Grist
|
||||
|
||||
Grist is a modern relational spreadsheet. It combines the flexibility of a spreadsheet with the
|
||||
robustness of a database to organize your data and make you more productive.
|
||||
Grist is a modern relational spreadsheet. It combines the flexibility of a spreadsheet with the robustness of a database to organize your data and make you more productive.
|
||||
|
||||
This repository, `grist-core`, is the heart of Grist, and has what you
|
||||
need to run a powerful spreadsheet hosting server. If you wish to view and edit
|
||||
spreadsheets stored locally, another option is to use the
|
||||
[`grist-electron`](https://github.com/gristlabs/grist-electron) desktop app for Linux, Mac, and Windows. And to show Grist spreadsheets on a website
|
||||
without any special back-end support, your options include
|
||||
[`grist-static`](https://github.com/gristlabs/grist-static),
|
||||
a fully in-browser build of Grist.
|
||||
The `grist-core` repository is the basis for all these options, and
|
||||
for the hosted spreadsheet services offered by
|
||||
[`Grist Labs`](https://getgrist.com), an NYC-based company 🇺🇸 that is the main developer of Grist, and by
|
||||
[`ANCT Données et Territoires`](https://donnees.incubateur.anct.gouv.fr/toolbox/grist),
|
||||
a French government agency 🇫🇷 whose developers have made many
|
||||
contributions to the code-base.
|
||||
The `grist-core`, `grist-electron`, and `grist-static` repositories
|
||||
are all open-source (Apache License, Version 2.0).
|
||||
This repository, `grist-core`, is the heart of Grist, and has what you need to run a powerful spreadsheet hosting server. If you wish to view and edit spreadsheets stored locally, another option is to use the [`grist-electron`](https://github.com/gristlabs/grist-electron) desktop app for Linux, Mac, and Windows. And to show Grist spreadsheets on a website without any special back-end support, you can use [`grist-static`](https://github.com/gristlabs/grist-static), a fully in-browser build of Grist.
|
||||
|
||||
The `grist-core` repository is the basis for all these options, and for the hosted spreadsheet services offered by [Grist Labs](https://getgrist.com), an NYC-based company 🇺🇸 that is the main developer of Grist, and by [ANCT Données et Territoires](https://donnees.incubateur.anct.gouv.fr/toolbox/grist), a French government agency 🇫🇷 whose developers have made many contributions to the codebase.
|
||||
|
||||
The `grist-core`, `grist-electron`, and `grist-static` repositories are all open source (Apache License, Version 2.0).
|
||||
|
||||
https://user-images.githubusercontent.com/118367/151245587-892e50a6-41f5-4b74-9786-fe3566f6b1fb.mp4
|
||||
|
||||
@ -25,81 +14,69 @@ https://user-images.githubusercontent.com/118367/151245587-892e50a6-41f5-4b74-97
|
||||
|
||||
Grist is a hybrid database/spreadsheet, meaning that:
|
||||
|
||||
- Columns work like they do in databases. They are named, and hold one kind of data.
|
||||
- Columns work like they do in databases: they are named, and they hold one kind of data.
|
||||
- Columns can be filled by formula, spreadsheet-style, with automatic updates when referenced cells change.
|
||||
|
||||
This difference can confuse people coming directly from Excel or Google Sheets. Give it a chance!
|
||||
If you are coming from Airtable, you'll find the model familiar though (and there's a
|
||||
[Grist vs Airtable](https://www.getgrist.com/blog/grist-v-airtable/) article that might interest you).
|
||||
|
||||
This difference can confuse people coming directly from Excel or Google Sheets. Give it a chance! If you are coming from Airtable, you'll find the model familiar though (and there's a [Grist vs Airtable](https://www.getgrist.com/blog/grist-v-airtable/) article that might interest you).
|
||||
|
||||
Here are some specific feature highlights of Grist:
|
||||
|
||||
* Python formulas.
|
||||
- Full [Python syntax is supported](https://support.getgrist.com/formulas/#python), and the standard library.
|
||||
- Full [Python syntax is supported](https://support.getgrist.com/formulas/#python), including the standard library.
|
||||
- Many [Excel functions](https://support.getgrist.com/functions/) also available.
|
||||
- An [AI Assistant](https://www.getgrist.com/ai-formula-assistant/) specifically tuned for formula generation (using OpenAI gpt-3.5-turbo or [Llama](https://ai.meta.com/llama/) via <a href="https://github.com/abetlen/llama-cpp-python">llama-cpp-python</a>).
|
||||
* A portable, self-contained format.
|
||||
- Based on SQLite, the most widely deployed database engine.
|
||||
- Any tool that can read SQLite can read numeric and text data from a Grist file.
|
||||
- Great format for [backups](https://support.getgrist.com/exports/#backing-up-an-entire-document) that you can be confident you can restore in full.
|
||||
- Great format for moving between different hosts.
|
||||
- Can be displayed on a static website with [grist-static](https://github.com/gristlabs/grist-static), no special server needed.
|
||||
- There's a self-contained desktop app available for viewing and editing: [grist-electron](https://github.com/gristlabs/grist-electron).
|
||||
- Enables [backups](https://support.getgrist.com/exports/#backing-up-an-entire-document) that you can confidently restore in full.
|
||||
- Great for moving between different hosts.
|
||||
* Can be displayed on a static website with [`grist-static`](https://github.com/gristlabs/grist-static) – no special server needed.
|
||||
* A self-contained desktop app for viewing and editing locally: [`grist-electron`](https://github.com/gristlabs/grist-electron).
|
||||
* Convenient editing and formatting features.
|
||||
- Choices and [choice lists](https://support.getgrist.com/col-types/#choice-list-columns), for adding colorful tags to records without fuss.
|
||||
- Choices and [choice lists](https://support.getgrist.com/col-types/#choice-list-columns), for adding colorful tags to records.
|
||||
- [References](https://support.getgrist.com/col-refs/#creating-a-new-reference-list-column) and reference lists, for cross-referencing records in other tables.
|
||||
- [Attachments](https://support.getgrist.com/col-types/#attachment-columns), to include media or document files in records.
|
||||
- Dates and times, toggles, and special numerics such as currency all have specialized editors and formatting options.
|
||||
- [Conditional Formatting](https://support.getgrist.com/conditional-formatting/), letting you control the style of cells with formulas, to draw attention to important information.
|
||||
* Great for dashboards, visualizations, and data entry.
|
||||
- [Conditional Formatting](https://support.getgrist.com/conditional-formatting/), letting you control the style of cells with formulas to draw attention to important information.
|
||||
* Drag-and-drop dashboards.
|
||||
- [Charts](https://support.getgrist.com/widget-chart/) for visualization.
|
||||
- [Summary tables](https://support.getgrist.com/summary-tables/) for summing and counting across groups.
|
||||
- [Widget linking](https://support.getgrist.com/linking-widgets/) streamlines filtering and editing data.
|
||||
Grist has a unique approach to visualization, where you can lay out and link distinct widgets to show together,
|
||||
without cramming mixed material into a table.
|
||||
- The [Filter bar](https://support.getgrist.com/search-sort-filter/#filter-buttons) is great for quick slicing and dicing.
|
||||
- [Filter bar](https://support.getgrist.com/search-sort-filter/#filter-buttons) for quick slicing and dicing.
|
||||
* [Incremental imports](https://support.getgrist.com/imports/#updating-existing-records).
|
||||
- So you can import a CSV of the last three months activity from your bank...
|
||||
- ... and import new activity a month later without fuss or duplicates.
|
||||
- Import a CSV of the last three months activity from your bank...
|
||||
- ...and import new activity a month later without fuss or duplication.
|
||||
* Integrations.
|
||||
- A [REST API](https://support.getgrist.com/api/), [Zapier actions/triggers](https://support.getgrist.com/integrators/#integrations-via-zapier), and support from similar [integrators](https://support.getgrist.com/integrators/).
|
||||
- Import/export to Google drive, Excel format, CSV.
|
||||
- Can link data with custom widgets hosted externally.
|
||||
- You can set up outgoing webhooks.
|
||||
- Link data with [custom widgets](https://support.getgrist.com/widget-custom/#_top), hosted externally.
|
||||
- Configurable outgoing webhooks.
|
||||
* [Many templates](https://templates.getgrist.com/) to get you started, from investment research to organizing treasure hunts.
|
||||
* Access control options.
|
||||
- (You'll need SSO logins set up to make use of these options; [grist-omnibus](https://github.com/gristlabs/grist-omnibus) has a prepackaged solution if configuring this feels daunting)
|
||||
- Share [individual documents](https://support.getgrist.com/sharing/), or workspaces, or [team sites](https://support.getgrist.com/team-sharing/).
|
||||
- (You'll need SSO logins set up to make use of these options; [`grist-omnibus`](https://github.com/gristlabs/grist-omnibus) has a prepackaged solution if configuring this feels daunting)
|
||||
- Share [individual documents](https://support.getgrist.com/sharing/), workspaces, or [team sites](https://support.getgrist.com/team-sharing/).
|
||||
- Control access to [individual rows, columns, and tables](https://support.getgrist.com/access-rules/).
|
||||
- Control access based on cell values and user attributes.
|
||||
* Can be self-maintained.
|
||||
* Self-maintainable.
|
||||
- Useful for intranet operation and specific compliance requirements.
|
||||
* Sandboxing options for untrusted documents.
|
||||
- On Linux or with docker, you can enable
|
||||
[gVisor](https://github.com/google/gvisor) sandboxing at the individual
|
||||
document level.
|
||||
- On OSX, you can use native sandboxing.
|
||||
- On Linux or with Docker, you can enable [gVisor](https://github.com/google/gvisor) sandboxing at the individual document level.
|
||||
- On macOS, you can use native sandboxing.
|
||||
- On any OS, including Windows, you can use a wasm-based sandbox.
|
||||
* Translated to many languages.
|
||||
* Support for an AI Formula Assistant (using OpenAI gpt-3.5-turbo or comparable models).
|
||||
* `F1` key brings up some quick help. This used to go without saying. In general Grist has good keyboard support.
|
||||
* We post progress on [𝕏 or Twitter or whatever](https://twitter.com/getgrist).
|
||||
|
||||
If you are curious about where Grist is going heading,
|
||||
see [our roadmap](https://github.com/gristlabs/grist-core/projects/1), drop a
|
||||
question in [our forum](https://community.getgrist.com),
|
||||
or browse [our extensive documentation](https://support.getgrist.com).
|
||||
* `F1` key brings up some quick help. This used to go without saying, but in general Grist has good keyboard support.
|
||||
* We post progress on [𝕏 or Twitter or whatever](https://twitter.com/getgrist) and publish [monthly newsletters](https://support.getgrist.com/newsletters/).
|
||||
|
||||
If you are curious about where Grist is heading, see [our roadmap](https://github.com/gristlabs/grist-core/projects/1), drop a question in [our forum](https://community.getgrist.com), or browse [our extensive documentation](https://support.getgrist.com).
|
||||
|
||||
## Using Grist
|
||||
|
||||
If you just want a quick demo of Grist:
|
||||
|
||||
* You can try Grist out at the hosted service run
|
||||
by Grist Labs at [docs.getgrist.com](https://docs.getgrist.com)
|
||||
(no registration needed).
|
||||
* Or you can see an experimental fully in-browser build of Grist
|
||||
at [gristlabs.github.io/grist-static](https://gristlabs.github.io/grist-static/).
|
||||
* You can try Grist out at the hosted service run by Grist Labs at [docs.getgrist.com](https://docs.getgrist.com) (no registration needed).
|
||||
* Or you can see a fully in-browser build of Grist at [gristlabs.github.io/grist-static](https://gristlabs.github.io/grist-static/).
|
||||
* Or you can download Grist as a desktop app from [github.com/gristlabs/grist-electron](https://github.com/gristlabs/grist-electron).
|
||||
|
||||
To get `grist-core` running on your computer with [Docker](https://www.docker.com/get-started), do:
|
||||
@ -150,7 +127,7 @@ Grist formulas in documents will be run using Python executed directly on your
|
||||
machine. You can configure sandboxing using a `GRIST_SANDBOX_FLAVOR`
|
||||
environment variable.
|
||||
|
||||
* On OSX, `export GRIST_SANDBOX_FLAVOR=macSandboxExec`
|
||||
* On macOS, `export GRIST_SANDBOX_FLAVOR=macSandboxExec`
|
||||
uses the native `sandbox-exec` command for sandboxing.
|
||||
* On Linux with [gVisor's runsc](https://github.com/google/gvisor)
|
||||
installed, `export GRIST_SANDBOX_FLAVOR=gvisor` is an option.
|
||||
@ -195,18 +172,15 @@ did the hard work of making a good chunk of the application localizable. Merci b
|
||||
|
||||
This repository, [grist-core](https://github.com/gristlabs/grist-core), is maintained by Grist
|
||||
Labs. Our flagship product available at [getgrist.com](https://www.getgrist.com) is built from the code you see
|
||||
here, combined with business-specific software designed to scale it to many users, handle billing,
|
||||
here, combined with business-specific software designed to scale to many users, handle billing,
|
||||
etc.
|
||||
|
||||
Grist Labs is an open-core company. We offer Grist hosting as a
|
||||
service, with free and paid plans. We also develop and sell
|
||||
features related to Grist using a proprietary license, targeted at the
|
||||
needs of enterprises with large self-managed installations. We see
|
||||
data portability and autonomy as a key value Grist can bring to our
|
||||
users, and `grist-core` as an essential means to deliver that. We are
|
||||
committed to maintaining and improving the `grist-core` codebase, and
|
||||
to be thoughtful about how proprietary offerings impact data portability
|
||||
and autonomy.
|
||||
needs of enterprises with large self-managed installations.
|
||||
|
||||
We see data portability and autonomy as a key value, and `grist-core` is an essential part of that. We are committed to maintaining and improving the `grist-core` codebase, and to be thoughtful about how proprietary offerings impact data portability and autonomy.
|
||||
|
||||
By opening its source code and offering an [OSI](https://opensource.org/)-approved free license,
|
||||
Grist benefits its users:
|
||||
@ -223,10 +197,9 @@ Grist benefits its users:
|
||||
- **Price flexibility.** If you are low on funds but have time to invest, self-hosting is a great
|
||||
option to have. And DIY users may have the technical savvy and motivation to delve in and make improvements,
|
||||
which can benefit all users of Grist.
|
||||
- **Extensibility.** For developers, having the source open makes it easier to build extensions (such as the
|
||||
experimental [Custom Widget](https://support.getgrist.com/widget-custom/)). You can more easily
|
||||
include Grist in your pipeline. And if a feature is missing, you can just take the source code and
|
||||
build on top of it.
|
||||
- **Extensibility.** For developers, having the source open makes it easier to build extensions (such as [Custom Widgets](https://support.getgrist.com/widget-custom/)). You can more easily include Grist in your pipeline. And if a feature is missing, you can just take the source code and build on top of it.
|
||||
|
||||
For more on Grist Labs' history and principles, see our [About Us](https://www.getgrist.com/about/) page.
|
||||
|
||||
## Sponsors
|
||||
|
||||
|
@ -72,6 +72,8 @@ export interface SessionObj {
|
||||
oidc?: {
|
||||
// codeVerifier is used during OIDC authentication, to protect against attacks like CSRF.
|
||||
codeVerifier?: string;
|
||||
state?: string;
|
||||
targetUrl?: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ export async function collectTableSchemaInFrictionlessFormat(
|
||||
req: express.Request,
|
||||
options: DownloadOptions
|
||||
): Promise<FrictionlessFormat> {
|
||||
const {tableId} = options;
|
||||
const {tableId, header} = options;
|
||||
if (!activeDoc.docData) {
|
||||
throw new Error('No docData in active document');
|
||||
}
|
||||
@ -50,24 +50,15 @@ export async function collectTableSchemaInFrictionlessFormat(
|
||||
throw new ApiError(`Table ${tableId} not found.`, 404);
|
||||
}
|
||||
|
||||
const data = await exportTable(activeDoc, tableRef, req);
|
||||
const tableSchema = columnsToTableSchema(tableId, data, settings.locale);
|
||||
return tableSchema;
|
||||
}
|
||||
|
||||
function columnsToTableSchema(
|
||||
tableId: string,
|
||||
{tableName, columns}: {tableName: string, columns: ExportColumn[]},
|
||||
locale: string,
|
||||
): FrictionlessFormat {
|
||||
const {tableName, columns} = await exportTable(activeDoc, tableRef, req);
|
||||
return {
|
||||
name: tableId.toLowerCase().replace(/_/g, '-'),
|
||||
title: tableName,
|
||||
schema: {
|
||||
fields: columns.map(col => ({
|
||||
name: col.label,
|
||||
name: col[header || "label"],
|
||||
...(col.description ? {description: col.description} : {}),
|
||||
...buildTypeField(col, locale),
|
||||
...buildTypeField(col, settings.locale),
|
||||
})),
|
||||
}
|
||||
};
|
||||
|
@ -78,6 +78,7 @@ export class OIDCConfig {
|
||||
redirect_uris: [ this._redirectUrl ],
|
||||
response_types: [ 'code' ],
|
||||
});
|
||||
log.info(`OIDCConfig: initialized with issuer ${issuerUrl}`);
|
||||
}
|
||||
|
||||
public addEndpoints(app: express.Application, sessions: Sessions): void {
|
||||
@ -85,15 +86,18 @@ export class OIDCConfig {
|
||||
}
|
||||
|
||||
public async handleCallback(sessions: Sessions, req: express.Request, res: express.Response): Promise<void> {
|
||||
const mreq = req as RequestWithLogin;
|
||||
try {
|
||||
const params = this._client.callbackParams(req);
|
||||
const { state } = params;
|
||||
const { state, targetUrl } = mreq.session?.oidc ?? {};
|
||||
if (!state) {
|
||||
throw new Error('Login or logout failed to complete');
|
||||
}
|
||||
|
||||
const codeVerifier = await this._retrieveCodeVerifierFromSession(req);
|
||||
|
||||
// The callback function will compare the state present in the params and the one we retrieved from the session.
|
||||
// If they don't match, it will throw an error.
|
||||
const tokenSet = await this._client.callback(
|
||||
this._redirectUrl,
|
||||
params,
|
||||
@ -102,23 +106,29 @@ export class OIDCConfig {
|
||||
|
||||
const userInfo = await this._client.userinfo(tokenSet);
|
||||
const profile = this._makeUserProfileFromUserInfo(userInfo);
|
||||
log.info(`OIDCConfig: got OIDC response for ${profile.email} (${profile.name}) redirecting to ${targetUrl}`);
|
||||
|
||||
const scopedSession = sessions.getOrCreateSessionFromRequest(req);
|
||||
await scopedSession.operateOnScopedSession(req, async (user) => Object.assign(user, {
|
||||
profile,
|
||||
}));
|
||||
|
||||
res.redirect('/');
|
||||
delete mreq.session.oidc;
|
||||
res.redirect(targetUrl ?? '/');
|
||||
} catch (err) {
|
||||
log.error(`OIDC callback failed: ${err.message}`);
|
||||
res.status(500).send(`OIDC callback failed: ${err.message}`);
|
||||
log.error(`OIDC callback failed: ${err.stack}`);
|
||||
// Delete the session data even if the login failed.
|
||||
// This way, we prevent several login attempts.
|
||||
//
|
||||
// Also session deletion must be done before sending the response.
|
||||
delete mreq.session.oidc;
|
||||
res.status(500).send(`OIDC callback failed.`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getLoginRedirectUrl(req: express.Request): Promise<string> {
|
||||
const codeVerifier = await this._generateAndStoreCodeVerifier(req);
|
||||
public async getLoginRedirectUrl(req: express.Request, targetUrl: URL): Promise<string> {
|
||||
const { codeVerifier, state } = await this._generateAndStoreConnectionInfo(req, targetUrl.href);
|
||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||
const state = generators.state();
|
||||
|
||||
const authUrl = this._client.authorizationUrl({
|
||||
scope: process.env.GRIST_OIDC_IDP_SCOPES || 'openid email profile',
|
||||
@ -135,15 +145,18 @@ export class OIDCConfig {
|
||||
});
|
||||
}
|
||||
|
||||
private async _generateAndStoreCodeVerifier(req: express.Request) {
|
||||
private async _generateAndStoreConnectionInfo(req: express.Request, targetUrl: string) {
|
||||
const mreq = req as RequestWithLogin;
|
||||
if (!mreq.session) { throw new Error('no session available'); }
|
||||
const codeVerifier = generators.codeVerifier();
|
||||
const state = generators.state();
|
||||
mreq.session.oidc = {
|
||||
codeVerifier,
|
||||
state,
|
||||
targetUrl
|
||||
};
|
||||
|
||||
return codeVerifier;
|
||||
return { codeVerifier, state };
|
||||
}
|
||||
|
||||
private async _retrieveCodeVerifierFromSession(req: express.Request) {
|
||||
@ -151,7 +164,6 @@ export class OIDCConfig {
|
||||
if (!mreq.session) { throw new Error('no session available'); }
|
||||
const codeVerifier = mreq.session.oidc?.codeVerifier;
|
||||
if (!codeVerifier) { throw new Error('Login is stale'); }
|
||||
delete mreq.session.oidc?.codeVerifier;
|
||||
return codeVerifier;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "grist-core",
|
||||
"version": "1.1.7",
|
||||
"version": "1.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Grist is the evolution of spreadsheets",
|
||||
"homepage": "https://github.com/gristlabs/grist-core",
|
||||
|
@ -17,7 +17,9 @@
|
||||
"Everyone": "Všichni",
|
||||
"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.": "Umožni všem kopírovat celý dokument, nebo zobrazit plně v \"fiddle\" režimu.\nUžiitečné pro ukázky a šablony, ale ne pro citlivá data.",
|
||||
"Add Default Rule": "Přidej Základní Pravidlo",
|
||||
"Checking...": "Kontroluji…"
|
||||
"Checking...": "Kontroluji…",
|
||||
"Permissions": "Povolení",
|
||||
"Permission to view Access Rules": "Povolení na zobrazení Přistupových Pravidel"
|
||||
},
|
||||
"ACUserManager": {
|
||||
"Invite new member": "Pozvi nového uživatele",
|
||||
|
@ -1055,7 +1055,8 @@
|
||||
"Can't find the right columns? Click 'Change Widget' to select the table with events data.": "Не можете найти нужные столбцы? Нажмите «Изменить виджет», чтобы выбрать таблицу с данными о событиях.",
|
||||
"A UUID is a randomly-generated string that is useful for unique identifiers and link keys.": "UUID - это случайно сгенерированная строка, которая полезна для уникальных идентификаторов и ключевых ссылок.",
|
||||
"Lookups return data from related tables.": "Lookups возвращают данные из связанных таблиц.",
|
||||
"Use reference columns to relate data in different tables.": "Используйте ссылочные столбцы для сопоставления данных в разных таблицах."
|
||||
"Use reference columns to relate data in different tables.": "Используйте ссылочные столбцы для сопоставления данных в разных таблицах.",
|
||||
"You can choose from widgets available to you in the dropdown, or embed your own by providing its full URL.": "Вы можете выбрать виджеты, доступные вам в раскрывающемся списке, или встроить свой собственный, указав его полный URL-адрес."
|
||||
},
|
||||
"DescriptionConfig": {
|
||||
"DESCRIPTION": "ОПИСАНИЕ"
|
||||
|
@ -19,19 +19,19 @@
|
||||
"Type a message...": "Vnesite sporočilo…",
|
||||
"User Attributes": "Atributi uporabnika",
|
||||
"View As": "Poglej kot",
|
||||
"When adding table rules, automatically add a rule to grant OWNER full access.": "Pri dodajanju pravil za tabele samodejno dodajte pravilo, ki lastniku omogoča popoln dostop.",
|
||||
"When adding table rules, automatically add a rule to grant OWNER full access.": "Pri dodajanju pravil za tabele samodejno dodaj pravilo, ki lastniku omogoča popoln dostop.",
|
||||
"Permission to edit document structure": "Dovoljenje za urejanje strukture dokumenta",
|
||||
"Everyone": "Vsi",
|
||||
"Everyone Else": "Vsi ostali",
|
||||
"Checking...": "Preverjanje…",
|
||||
"Condition": "Stanje",
|
||||
"Enter Condition": "Vnesite pogoj",
|
||||
"Add Column Rule": "Dodajanje pravila za stolpce",
|
||||
"Add Column Rule": "Dodaj pravila za stolpec",
|
||||
"Add Default Rule": "Dodaj privzeto pravilo",
|
||||
"Add Table Rules": "Dodajanje pravil tabele",
|
||||
"Add User Attributes": "Dodajanje atributov uporabnika",
|
||||
"Add Table Rules": "Dodaj pravila za tabelo",
|
||||
"Add User Attributes": "Dodaj atribute za uporabnika",
|
||||
"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.": "Vsakomur omogočite kopiranje celotnega dokumenta ali pa si ga oglejte v celoti v načinu fiddle.\nUporabno za primere in predloge, ne pa za občutljive podatke.",
|
||||
"Allow everyone to view Access Rules.": "Vsakomur omogočite ogled pravil za dostop.",
|
||||
"Allow everyone to view Access Rules.": "Omogoči vsakomur ogled pravil za dostop.",
|
||||
"Attribute name": "Ime atributa",
|
||||
"Attribute to Look Up": "Atribut za iskanje",
|
||||
"Lookup Table": "Preglednica za iskanje",
|
||||
@ -40,8 +40,8 @@
|
||||
},
|
||||
"ACUserManager": {
|
||||
"We'll email an invite to {{email}}": "Vabilo bomo poslali po e-pošti {{email}}",
|
||||
"Enter email address": "Vnesite e-poštni naslov",
|
||||
"Invite new member": "Povabite novega člana"
|
||||
"Enter email address": "Vnesi e-poštni naslov",
|
||||
"Invite new member": "Povabi novega člana"
|
||||
},
|
||||
"AccountPage": {
|
||||
"API": "API",
|
||||
@ -110,7 +110,7 @@
|
||||
"Translators: please translate this only when your language is ready to be offered to users": "Prevajalci: prosimo, prevedite to šele, ko bo vaš jezik pripravljen, da se ponudi uporabnikom"
|
||||
},
|
||||
"CellContextMenu": {
|
||||
"Delete {{count}} columns_one": "Brisanje stolpca",
|
||||
"Delete {{count}} columns_one": "Briši stolpec",
|
||||
"Delete {{count}} columns_other": "Brisanje stolpcev {{count}}",
|
||||
"Delete {{count}} rows_one": "Brisanje vrstice",
|
||||
"Delete {{count}} rows_other": "Brisanje vrstic {{count}}",
|
||||
@ -118,12 +118,12 @@
|
||||
"Copy anchor link": "Kopiranje sidrne povezave",
|
||||
"Duplicate rows_one": "Podvoji vrstico",
|
||||
"Duplicate rows_other": "Podvoji vrstice",
|
||||
"Insert column to the right": "Vstavi stolpec na desno",
|
||||
"Insert column to the left": "Vstavi stolpec na levo",
|
||||
"Insert column to the right": "Vstavi stolpec na desno stran",
|
||||
"Insert column to the left": "Vstavi stolpec na levo stran",
|
||||
"Insert row": "Vstavljanje vrstice",
|
||||
"Insert row above": "Vstavite vrstico zgoraj",
|
||||
"Insert row below": "Vstavi vrstico spodaj",
|
||||
"Reset {{count}} columns_one": "Ponastavitev stolpca",
|
||||
"Reset {{count}} columns_one": "Ponastavi stolpec",
|
||||
"Reset {{count}} columns_other": "Ponastavit {{count}} stolpcev",
|
||||
"Reset {{count}} entire columns_one": "Ponastavi celote stolpec",
|
||||
"Reset {{count}} entire columns_other": "Ponastavi {{count}} celotnih stolpcev",
|
||||
@ -177,7 +177,7 @@
|
||||
},
|
||||
"GridViewMenus": {
|
||||
"Rename column": "Preimenuj stolpec",
|
||||
"Delete {{count}} columns_one": "Brisanje stolpca",
|
||||
"Delete {{count}} columns_one": "Briši stolpec",
|
||||
"Delete {{count}} columns_other": "Brisanje stolpcev {{count}}",
|
||||
"Unfreeze {{count}} columns_one": "Odmrzni ta stolpec",
|
||||
"Sorted (#{{count}})_one": "Razvrščeno (#{{count}})",
|
||||
@ -189,7 +189,7 @@
|
||||
"Filter Data": "Filtriranje podatkov",
|
||||
"Hide {{count}} columns_other": "Skrij {{count}} stolpcev",
|
||||
"Add Column": "Dodaj stolpec",
|
||||
"Reset {{count}} columns_one": "Ponastavitev stolpca",
|
||||
"Reset {{count}} columns_one": "Ponastavi stolpec",
|
||||
"Freeze {{count}} columns_one": "Zamrzni stolpec",
|
||||
"More sort options ...": "Več možnosti razvrščanja…",
|
||||
"Freeze {{count}} more columns_one": "Zamrznite še en stolpec",
|
||||
@ -200,10 +200,10 @@
|
||||
"Freeze {{count}} more columns_other": "Zamrznite še {{count}} stolpcev",
|
||||
"Hide {{count}} columns_one": "Skrij stolpec",
|
||||
"Sorted (#{{count}})_other": "Razvrščeno (#{{count}})",
|
||||
"Insert column to the {{to}}": "Vstavi stolpec v {{to}}",
|
||||
"Insert column to the {{to}}": "Vstavi stolpec na {{to}}",
|
||||
"Reset {{count}} entire columns_other": "Ponastavi {{count}} stolpcev",
|
||||
"Unfreeze {{count}} columns_other": "Odmrznite {{count}} stolpcev",
|
||||
"Insert column to the right": "Vstavi stolpec na desno",
|
||||
"Insert column to the right": "Vstavi stolpec na desno stran",
|
||||
"Reset {{count}} entire columns_one": "Ponastavi celoten stolpec",
|
||||
"Insert column to the left": "Vstavi stolpec na levo",
|
||||
"Shortcuts": "Bližnjice",
|
||||
@ -270,7 +270,7 @@
|
||||
"Access Rules": "Pravila dostopa",
|
||||
"Code View": "Pogled kode",
|
||||
"Raw Data": "Neobdelani podatki",
|
||||
"Document History": "Zgodovina dokumentov",
|
||||
"Document History": "Zgodovina Dokumentov",
|
||||
"Validate Data": "Potrdi podatke",
|
||||
"How-to Tutorial": "Vadnica kako narediti",
|
||||
"Tour of this Document": "Ogled tega dokumenta",
|
||||
@ -278,7 +278,7 @@
|
||||
},
|
||||
"pages": {
|
||||
"Rename": "Preimenuj",
|
||||
"Duplicate Page": "Podvojena stran",
|
||||
"Duplicate Page": "Podvoji stran",
|
||||
"You do not have edit access to this document": "Nimate dovoljenja za urejanje tega dokumenta",
|
||||
"Remove": "Odstrani"
|
||||
},
|
||||
@ -293,7 +293,7 @@
|
||||
"Add New": "Dodaj"
|
||||
},
|
||||
"DataTables": {
|
||||
"Delete {{formattedTableName}} data, and remove it from all pages?": "Izbrišite podatke {{formattedTableName}} in jih odstranite z vseh strani?",
|
||||
"Delete {{formattedTableName}} data, and remove it from all pages?": "Izbriši podatke {{formattedTableName}} in jih odstrani z vseh strani?",
|
||||
"Click to copy": "Kliknite za kopiranje",
|
||||
"Duplicate Table": "Podvojena tabela",
|
||||
"Table ID copied to clipboard": "ID tabele kopiran v odložišče",
|
||||
@ -305,8 +305,8 @@
|
||||
"Delete widget": "Izbriši gradnik",
|
||||
"Advanced Sort & Filter": "Napredno razvrščanje in filtriranje",
|
||||
"Data selection": "Izbira podatkov",
|
||||
"Download as XLSX": "Prenesite kot XLSX",
|
||||
"Download as CSV": "Prenesite kot CSV",
|
||||
"Download as XLSX": "Prenesi kot XLSX",
|
||||
"Download as CSV": "Prenesi kot CSV",
|
||||
"Widget options": "Možnosti gradnika",
|
||||
"Print widget": "Gradnik za tiskanje",
|
||||
"Open configuration": "Odpri konfiguracijo",
|
||||
@ -423,13 +423,13 @@
|
||||
"Formula Columns_other": "Stolpci formule",
|
||||
"Formula Columns_one": "Stolpec formule",
|
||||
"Enter formula": "Vnesite formulo",
|
||||
"Clear and make into formula": "Brišite in pretvorite v formulo",
|
||||
"Clear and make into formula": "Briši in pretvori v formulo",
|
||||
"Mixed Behavior": "Mešano vedenje",
|
||||
"Convert to trigger formula": "Pretvori v sprožitveno formulo",
|
||||
"Data Columns_one": "Stolpec podatkov",
|
||||
"TRIGGER FORMULA": "SPROŽILNA FORMULA",
|
||||
"Set trigger formula": "Nastavite sprožitveno formulo",
|
||||
"Make into data column": "Spremenite v podatkovni stolpec",
|
||||
"Set trigger formula": "Nastavi sprožitveno formulo",
|
||||
"Make into data column": "Spremeni v podatkovni stolpec",
|
||||
"COLUMN BEHAVIOR": "OBNAŠANJE STOLPCA"
|
||||
},
|
||||
"DuplicateTable": {
|
||||
@ -440,7 +440,7 @@
|
||||
},
|
||||
"DocPageModel": {
|
||||
"Sorry, access to this document has been denied. [{{error}}]": "Žal je bil dostop do tega dokumenta zavrnjen. [{{error}}]",
|
||||
"Add Empty Table": "Dodajte prazno tabelo",
|
||||
"Add Empty Table": "Dodaj prazno tabelo",
|
||||
"You do not have edit access to this document": "Nimate dostopa za urejanje tega dokumenta",
|
||||
"Add Widget to Page": "Dodaj widget na stran",
|
||||
"Add Page": "Dodaj stran",
|
||||
@ -552,7 +552,7 @@
|
||||
"SOURCE DATA": "IZVORNI PODATKI",
|
||||
"CHART TYPE": "VRSTA DIAGRAMA",
|
||||
"Detach": "Odklopi",
|
||||
"Change Widget": "Spremite widget",
|
||||
"Change Widget": "Spremeni Pripomoček",
|
||||
"Columns_one": "Stolpec",
|
||||
"Series_other": "Serija",
|
||||
"Fields_other": "Polja",
|
||||
@ -661,8 +661,8 @@
|
||||
"Apply conditional formatting to cells in this column when formula conditions are met.": "Uporabi pogojno oblikovanje za celice v tem stolpcu, ko so izpolnjeni pogoji formule.",
|
||||
"To make an anchor link that takes the user to a specific cell, click on a row and press {{shortcut}}.": "Če želite narediti sidrno povezavo, ki uporabnika pripelje do določene celice, kliknite vrstico in pritisnite {{shortcut}}.",
|
||||
"Unpin to hide the the button while keeping the filter.": "Odpnite, da skrijete gumb in obdržite filter.",
|
||||
"Apply conditional formatting to rows based on formulas.": "Uporabite pogojno oblikovanje za vrstice na podlagi formul.",
|
||||
"Click on “Open row styles” to apply conditional formatting to rows.": "Kliknite »Odpri sloge vrstic«, da za vrstice uporabite pogojno oblikovanje.",
|
||||
"Apply conditional formatting to rows based on formulas.": "Uporabi pogojno oblikovanje za vrstice na podlagi formul.",
|
||||
"Click on “Open row styles” to apply conditional formatting to rows.": "Klikni »Odpri sloge vrstic«, da za vrstice uporabiš pogojno oblikovanje.",
|
||||
"Pinned filters are displayed as buttons above the widget.": "Pripeti filtri so prikazani kot gumbi nad pripomočkom.",
|
||||
"Link your new widget to an existing widget on this page.": "Povežite svoj novi pripomoček z obstoječim pripomočkom na tej strani.",
|
||||
"Use the \\u{1D6BA} icon to create summary (or pivot) tables, for totals or subtotals.": "Uporabite ikono \\u{1D6BA} za ustvarjanje povzetkov (ali vrtilnih) tabel za vsote ali delne vsote.",
|
||||
@ -688,7 +688,7 @@
|
||||
"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.": "Uporabnik ima vpogled v {{resource}}, ki je posledica ročno nastavljenega dostopa do virov v njem. Če ga tukaj odstranite, bo ta uporabnik izgubil dostop do notranjih virov.",
|
||||
"User may not modify their own access.": "Uporabnik ne more spreminjati lastnega dostopa.",
|
||||
"member": "član",
|
||||
"Add {{member}} to your team": "Dodajte {{member}} v svojo ekipo",
|
||||
"Add {{member}} to your team": "Dodaj {{member}} v svojo ekipo",
|
||||
"Collaborator": "Sodelavec",
|
||||
"Link copied to clipboard": "Povezava kopirana v odložišče",
|
||||
"team site": "spletno mesto ekipe",
|
||||
@ -758,7 +758,7 @@
|
||||
},
|
||||
"ColumnTitle": {
|
||||
"Column ID copied to clipboard": "ID stolpca kopiran v odložišče",
|
||||
"Add description": "Dodajte opis",
|
||||
"Add description": "Dodaj opis",
|
||||
"Column description": "Opis stolpca",
|
||||
"Provide a column label": "Navedite oznako stolpca",
|
||||
"Close": "Zapri",
|
||||
@ -870,7 +870,7 @@
|
||||
},
|
||||
"RecordLayoutEditor": {
|
||||
"Show field {{- label}}": "Prikaži polje {{- label}}",
|
||||
"Add Field": "Dodajte polje",
|
||||
"Add Field": "Dodaj polje",
|
||||
"Save Layout": "Shrani postavitev",
|
||||
"Cancel": "Prekliči",
|
||||
"Create New Field": "Ustvari novo polje"
|
||||
@ -926,7 +926,7 @@
|
||||
"Switch appearance automatically to match system": "Samodejno preklopite videz, da se ujema s sistemom"
|
||||
},
|
||||
"SiteSwitcher": {
|
||||
"Switch Sites": "Preklopite mesta",
|
||||
"Switch Sites": "Preklopi mesta",
|
||||
"Create new team site": "Ustvarite novo spletno mesto ekipe"
|
||||
},
|
||||
"WebhookPage": {
|
||||
@ -1016,7 +1016,7 @@
|
||||
"Reference List": "Referenčni seznam",
|
||||
"* Workspaces are available on team plans. ": "* Delovni prostori so na voljo v skupinskih načrtih. ",
|
||||
"Upgrade now": "Nadgradi zdaj",
|
||||
"Toggle": "Preklopi",
|
||||
"Toggle": "Preklop",
|
||||
"Choice List": "Izbirni seznam",
|
||||
"Any": "Katerikoli",
|
||||
"DateTime": "Datum čas",
|
||||
@ -1170,8 +1170,8 @@
|
||||
"ConditionalStyle": {
|
||||
"Rule must return True or False": "Pravilo mora vrniti True ali False",
|
||||
"Row Style": "Slog vrstice",
|
||||
"Add another rule": "Dodajte še eno pravilo",
|
||||
"Add conditional style": "Dodajte pogojni slog",
|
||||
"Add another rule": "Dodaj dodatno pravilo",
|
||||
"Add conditional style": "Dodaj pogojni slog",
|
||||
"Error in style rule": "Napaka v slogovnem pravilu"
|
||||
},
|
||||
"EditorTooltip": {
|
||||
@ -1219,7 +1219,7 @@
|
||||
},
|
||||
"duplicatePage": {
|
||||
"Note that this does not copy data, but creates another view of the same data.": "Upoštevajte, da s tem ne kopirate podatkov, ampak ustvarite drug pogled istih podatkov.",
|
||||
"Duplicate page {{pageName}}": "Podvojena stran {{pageName}}"
|
||||
"Duplicate page {{pageName}}": "Podvoji stran {{pageName}}"
|
||||
},
|
||||
"CurrencyPicker": {
|
||||
"Invalid currency": "Neveljavna valuta"
|
||||
|
@ -151,7 +151,27 @@
|
||||
"Unfreeze {{count}} columns_one": "Відкріпити цей стовпець",
|
||||
"Unfreeze {{count}} columns_other": "Відкріпити {{count}} стовпців",
|
||||
"Insert column to the right": "Вставити стовпець праворуч",
|
||||
"Insert column to the left": "Вставити стовпець ліворуч"
|
||||
"Insert column to the left": "Вставити стовпець ліворуч",
|
||||
"Detect Duplicates in...": "Виявлення дублікатів у...",
|
||||
"UUID": "UUID",
|
||||
"Shortcuts": "Ярлики",
|
||||
"Show hidden columns": "Показати приховані стовпці",
|
||||
"Created At": "Дата створення",
|
||||
"Authorship": "Авторство",
|
||||
"Last Updated By": "Автор останньої зміни",
|
||||
"Hidden Columns": "Приховані стовпці",
|
||||
"Lookups": "Lookups",
|
||||
"No reference columns.": "Немає довідкових стовпців.",
|
||||
"Apply on record changes": "Застосовувати при зміні записів",
|
||||
"Duplicate in {{- label}}": "Дублікат у {{- label}}\"",
|
||||
"Created By": "Автор створення",
|
||||
"Last Updated At": "Дата останньої зміни",
|
||||
"Apply to new records": "Застосувати до нових записів",
|
||||
"Search columns": "Шукати стовпці",
|
||||
"Timestamp": "Часова мітка",
|
||||
"no reference column": "немає довідкових стовпців",
|
||||
"Adding UUID column": "Adding UUID column",
|
||||
"Adding duplicates column": "Adding duplicates column"
|
||||
},
|
||||
"ThemeConfig": {
|
||||
"Switch appearance automatically to match system": "Автоматично змінювати оформлення відповідно до системи",
|
||||
@ -820,7 +840,8 @@
|
||||
"Reference": "Посилання",
|
||||
"DateTime": "Дата і час",
|
||||
"Reference List": "Список посилань",
|
||||
"Attachment": "Вкладення"
|
||||
"Attachment": "Вкладення",
|
||||
"Search columns": "Шукати стовпці"
|
||||
},
|
||||
"modals": {
|
||||
"Cancel": "Відмінити",
|
||||
@ -913,14 +934,15 @@
|
||||
},
|
||||
"FieldBuilder": {
|
||||
"CELL FORMAT": "ФОРМАТ КЛІТИНКИ",
|
||||
"Changing multiple column types": "Зміна кількох типів стовпців",
|
||||
"Changing multiple column types": "Changing multiple column types",
|
||||
"DATA FROM TABLE": "ДАНІ З ТАБЛИЦІ",
|
||||
"Mixed format": "Змішаний формат",
|
||||
"Mixed types": "Змішані типи",
|
||||
"Use separate field settings for {{colId}}": "Використати окремі налаштування поля для {{colId}}",
|
||||
"Apply Formula to Data": "Застосувати формулу до даних",
|
||||
"Revert field settings for {{colId}} to common": "Змінити налаштування поля для {{colId}} на загальні",
|
||||
"Save field settings for {{colId}} as common": "Зберегти налаштування поля для {{colId}} як загальні"
|
||||
"Save field settings for {{colId}} as common": "Зберегти налаштування поля для {{colId}} як загальні",
|
||||
"Changing column type": "Changing column type"
|
||||
},
|
||||
"FieldEditor": {
|
||||
"Unable to finish saving edited cell": "Неможливо зберегти відредаговану клітинку",
|
||||
@ -931,7 +953,11 @@
|
||||
"Error in the cell": "Помилка в клітинці",
|
||||
"Errors in all {{numErrors}} cells": "Помилки в усіх клітинках {{numErrors}}",
|
||||
"editingFormula is required": "editingFormula обов'язкове",
|
||||
"Errors in {{numErrors}} of {{numCells}} cells": "Помилки в {{numErrors}} в {{numCells}} клітинках"
|
||||
"Errors in {{numErrors}} of {{numCells}} cells": "Помилки в {{numErrors}} в {{numCells}} клітинках",
|
||||
"Enter formula or {{button}}.": "Введіть формулу або {{button}}.",
|
||||
"Enter formula.": "Введіть формулу.",
|
||||
"Expand Editor": "Розгорнути редактор",
|
||||
"use AI Assistant": "Використати AI Помічника"
|
||||
},
|
||||
"HyperLinkEditor": {
|
||||
"[link label] url": "[мітка посилання] URL"
|
||||
@ -1017,9 +1043,14 @@
|
||||
"Useful for storing the timestamp or author of a new record, data cleaning, and more.": "Корисно для збереження позначки часу або автора нового запису, очищення даних тощо.",
|
||||
"You can filter by more than one column.": "Ви можете фільтрувати за кількома стовпцями.",
|
||||
"Try out changes in a copy, then decide whether to replace the original with your edits.": "Спробуйте змінити копію, а потім вирішіть, чи слід замінювати оригінал своїми правками.",
|
||||
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Правила доступу дають вам можливість створювати детальні правила, щоб визначити, хто може бачити або редагувати (та які) частини вашого документа."
|
||||
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Правила доступу дають вам можливість створювати детальні правила, щоб визначити, хто може бачити або редагувати (та які) частини вашого документа.",
|
||||
"To make an anchor link that takes the user to a specific cell, click on a row and press {{shortcut}}.": "Щоб створити якірне посилання, яке перенаправляє користувача до певної комірки, клацніть на рядку та натисніть {{shortcut}}.",
|
||||
"Anchor Links": "Якірні посилання"
|
||||
},
|
||||
"DescriptionConfig": {
|
||||
"DESCRIPTION": "ОПИС"
|
||||
},
|
||||
"FieldContextMenu": {
|
||||
"Copy anchor link": "Скопіювати якірне посилання"
|
||||
}
|
||||
}
|
||||
|
@ -2700,6 +2700,43 @@ function testDocApi() {
|
||||
assert.equal(resp2.data, 'A,B\nSanta,1\nBob,11\nAlice,2\nFelix,22\n');
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/download/table-schema serves table-schema-encoded document with header=colId", async function () {
|
||||
const { docUrl, tableUrl } = await generateDocAndUrl('tableSchemaWithColIdAsHeader');
|
||||
const columns = [
|
||||
{
|
||||
id: 'Some_ID',
|
||||
fields: {
|
||||
label: 'Some Label',
|
||||
type: 'Text',
|
||||
}
|
||||
},
|
||||
];
|
||||
const setupColResp = await axios.put(`${tableUrl}/columns`, { columns }, {...chimpy, params: { replaceall: true }});
|
||||
assert.equal(setupColResp.status, 200);
|
||||
|
||||
const resp = await axios.get(`${docUrl}/download/table-schema?tableId=Table1&header=colId`, chimpy);
|
||||
assert.equal(resp.status, 200);
|
||||
const expected = {
|
||||
format: "csv",
|
||||
mediatype: "text/csv",
|
||||
encoding: "utf-8",
|
||||
dialect: {
|
||||
delimiter: ",",
|
||||
doubleQuote: true,
|
||||
},
|
||||
name: 'table1',
|
||||
title: 'Table1',
|
||||
schema: {
|
||||
fields: [{
|
||||
name: 'Some_ID',
|
||||
type: 'string',
|
||||
format: 'default',
|
||||
}]
|
||||
}
|
||||
};
|
||||
assert.deepInclude(resp.data, expected);
|
||||
});
|
||||
|
||||
it("GET /docs/{did}/download/table-schema respects permissions", async function () {
|
||||
// kiwi has no access to TestDoc
|
||||
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.TestDoc}/download/table-schema?tableId=Table1`, kiwi);
|
||||
|
Loading…
Reference in New Issue
Block a user