From ffec5265f20082a7dab5f6c30977c1e0420cd5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C4=8Dek=20Prijatelj?= Date: Tue, 14 Nov 2023 09:50:21 +0000 Subject: [PATCH 01/11] Translated using Weblate (Slovenian) Currently translated at 100.0% (999 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/sl/ --- static/locales/sl.client.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/static/locales/sl.client.json b/static/locales/sl.client.json index 81acba81..56af3b17 100644 --- a/static/locales/sl.client.json +++ b/static/locales/sl.client.json @@ -26,12 +26,12 @@ "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}}", @@ -123,7 +123,7 @@ "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", @@ -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" }, @@ -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" From e8789e6531a632eb12cdc0ef9d5409132ac43241 Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 15 Nov 2023 15:23:32 +0100 Subject: [PATCH 02/11] Issue 740 OIDC login redirect (#742) * Fix OIDC redirects from team site to personal page after login #740 Also: - compare state in session and state passed through parameters (otherwise the state won't have any effect regarding the security). - delete the session even after an authentication failure * More logs for OIDC #740 --------- Co-authored-by: Florent FAYOLLE --- app/server/lib/BrowserSession.ts | 2 ++ app/server/lib/OIDCConfig.ts | 32 ++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/server/lib/BrowserSession.ts b/app/server/lib/BrowserSession.ts index 69b5d97b..07136e3e 100644 --- a/app/server/lib/BrowserSession.ts +++ b/app/server/lib/BrowserSession.ts @@ -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; } } diff --git a/app/server/lib/OIDCConfig.ts b/app/server/lib/OIDCConfig.ts index d5436169..b0889236 100644 --- a/app/server/lib/OIDCConfig.ts +++ b/app/server/lib/OIDCConfig.ts @@ -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 { + 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 { - const codeVerifier = await this._generateAndStoreCodeVerifier(req); + public async getLoginRedirectUrl(req: express.Request, targetUrl: URL): Promise { + 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; } From 37dc1f8029173fc8a79e5dde24a603caa7ae7aca Mon Sep 17 00:00:00 2001 From: nbush Date: Wed, 15 Nov 2023 10:21:06 -0500 Subject: [PATCH 03/11] Readme revision (#748) * Update README.md cleaning up and trimming a few things * Update README.md other small tweaks --- README.md | 109 ++++++++++++++++++++---------------------------------- 1 file changed, 41 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 296d7d0c..c2bbca8b 100644 --- a/README.md +++ b/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. - -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). +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, 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 llama-cpp-python). * 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 From d8b224b45dd8cc9c6452f223f4670325ead1377b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C4=8Dek=20Prijatelj?= Date: Wed, 15 Nov 2023 17:05:16 +0000 Subject: [PATCH 04/11] Translated using Weblate (Slovenian) Currently translated at 100.0% (999 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/sl/ --- static/locales/sl.client.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/static/locales/sl.client.json b/static/locales/sl.client.json index 56af3b17..3a682f55 100644 --- a/static/locales/sl.client.json +++ b/static/locales/sl.client.json @@ -118,8 +118,8 @@ "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", @@ -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", @@ -428,8 +428,8 @@ "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": { @@ -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.", @@ -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", From 7bc862fb020adb6076b951848887399e7244cd48 Mon Sep 17 00:00:00 2001 From: Florent Date: Fri, 17 Nov 2023 16:45:15 +0100 Subject: [PATCH 05/11] Add header=colId option for the table-schema API #719 (#749) --- app/server/lib/ExportTableSchema.ts | 17 ++++--------- test/server/lib/DocApi.ts | 37 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/app/server/lib/ExportTableSchema.ts b/app/server/lib/ExportTableSchema.ts index 79c1f29b..13867841 100644 --- a/app/server/lib/ExportTableSchema.ts +++ b/app/server/lib/ExportTableSchema.ts @@ -36,7 +36,7 @@ export async function collectTableSchemaInFrictionlessFormat( req: express.Request, options: DownloadOptions ): Promise { - 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), })), } }; diff --git a/test/server/lib/DocApi.ts b/test/server/lib/DocApi.ts index 07798345..57c40413 100644 --- a/test/server/lib/DocApi.ts +++ b/test/server/lib/DocApi.ts @@ -2695,6 +2695,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); From daa7b50068bd1fa66f784d00bee055458ec52c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=92?= Date: Thu, 16 Nov 2023 21:19:08 +0000 Subject: [PATCH 06/11] Translated using Weblate (Russian) Currently translated at 99.5% (995 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/ru/ --- static/locales/ru.client.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/locales/ru.client.json b/static/locales/ru.client.json index b4e4b04b..dfb3bf6b 100644 --- a/static/locales/ru.client.json +++ b/static/locales/ru.client.json @@ -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": "ОПИСАНИЕ" From aa6e31f069e8fe12da4389588b7e19292d76fede Mon Sep 17 00:00:00 2001 From: Dmitry Sagalovskiy Date: Fri, 17 Nov 2023 19:38:51 +0000 Subject: [PATCH 07/11] Translated using Weblate (Ukrainian) Currently translated at 85.4% (854 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/uk/ --- static/locales/uk.client.json | 43 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/static/locales/uk.client.json b/static/locales/uk.client.json index f4a3340d..df06295c 100644 --- a/static/locales/uk.client.json +++ b/static/locales/uk.client.json @@ -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": "Скопіювати якірне посилання" } } From 1aeedd8a62d61541c44ed53b0255ef51970a4fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C4=8Dek=20Prijatelj?= Date: Thu, 16 Nov 2023 21:53:04 +0000 Subject: [PATCH 08/11] Translated using Weblate (Slovenian) Currently translated at 100.0% (999 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/sl/ --- static/locales/sl.client.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/locales/sl.client.json b/static/locales/sl.client.json index 3a682f55..b9d8520a 100644 --- a/static/locales/sl.client.json +++ b/static/locales/sl.client.json @@ -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", From 764775d10438170cf5d8a436bca9c638936858be Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Mon, 20 Nov 2023 10:20:13 -0500 Subject: [PATCH 09/11] v1.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e7dc761..9edc8019 100644 --- a/package.json +++ b/package.json @@ -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", From e92aa3ab924954387ace5d8b74703f9541fa42be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C4=8Dek=20Prijatelj?= Date: Sun, 19 Nov 2023 11:04:56 +0000 Subject: [PATCH 10/11] Translated using Weblate (Slovenian) Currently translated at 100.0% (999 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/sl/ --- static/locales/sl.client.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/static/locales/sl.client.json b/static/locales/sl.client.json index b9d8520a..636d4d9e 100644 --- a/static/locales/sl.client.json +++ b/static/locales/sl.client.json @@ -19,7 +19,7 @@ "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", @@ -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", @@ -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", @@ -423,7 +423,7 @@ "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", @@ -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", @@ -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" @@ -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": { From c3a72882a79b46db9818ab51c849421b977eaf62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Bl=C3=A1ha?= Date: Sun, 19 Nov 2023 15:56:44 +0000 Subject: [PATCH 11/11] Translated using Weblate (Czech) Currently translated at 2.3% (23 of 999 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/cs/ --- static/locales/cs.client.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/locales/cs.client.json b/static/locales/cs.client.json index ebe9f60e..38847b6b 100644 --- a/static/locales/cs.client.json +++ b/static/locales/cs.client.json @@ -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",