(core) updates from grist-core

This commit is contained in:
Paul Fitzpatrick 2024-04-29 15:01:40 -04:00
commit a3442aee77
16 changed files with 1470 additions and 1159 deletions

132
README.md
View File

@ -233,72 +233,72 @@ For more on Grist Labs' history and principles, see our [About Us](https://www.g
Grist can be configured in many ways. Here are the main environment variables it is sensitive to:
Variable | Purpose
-------- | -------
ALLOWED_WEBHOOK_DOMAINS | comma-separated list of permitted domains to use in webhooks (e.g. webhook.site,zapier.com). You can set this to `*` to allow all domains, but if doing so, we recommend using a carefully locked-down proxy (see `GRIST_HTTPS_PROXY`) if you do not entirely trust users. Otherwise services on your internal network may become vulnerable to manipulation.
APP_DOC_URL | doc worker url, set when starting an individual doc worker (other servers will find doc worker urls via redis)
APP_DOC_INTERNAL_URL | like `APP_DOC_URL` but used by the home server to reach the server using an internal domain name resolution (like in a docker environment). Defaults to `APP_DOC_URL`
APP_HOME_URL | url prefix for home api (home and doc servers need this)
APP_STATIC_URL | url prefix for static resources
APP_STATIC_INCLUDE_CUSTOM_CSS | set to "true" to include custom.css (from APP_STATIC_URL) in static pages
APP_UNTRUSTED_URL | URL at which to serve/expect plugin content.
GRIST_ADAPT_DOMAIN | set to "true" to support multiple base domains (careful, host header should be trustworthy)
GRIST_APP_ROOT | directory containing Grist sandbox and assets (specifically the sandbox and static subdirectories).
GRIST_BACKUP_DELAY_SECS | wait this long after a doc change before making a backup
GRIST_BOOT_KEY | if set, offer diagnostics at /boot/GRIST_BOOT_KEY
GRIST_DATA_DIR | directory in which to store document caches.
GRIST_DEFAULT_EMAIL | if set, login as this user if no other credentials presented
GRIST_DEFAULT_PRODUCT | if set, this controls enabled features and limits of new sites. See names of PRODUCTS in Product.ts.
GRIST_DEFAULT_LOCALE | Locale to use as fallback when Grist cannot honour the browser locale.
GRIST_DOMAIN | in hosted Grist, Grist is served from subdomains of this domain. Defaults to "getgrist.com".
GRIST_EXPERIMENTAL_PLUGINS | enables experimental plugins
GRIST_ENABLE_REQUEST_FUNCTION | enables the REQUEST function. This function performs HTTP requests in a similar way to `requests.request`. This function presents a significant security risk, since it can let users call internal endpoints when Grist is available publicly. This function can also cause performance issues. Unset by default.
GRIST_HIDE_UI_ELEMENTS | comma-separated list of UI features to disable. Allowed names of parts: `helpCenter,billing,templates,createSite,multiSite,multiAccounts,sendToDrive,tutorials,supportGrist`. If a part also exists in GRIST_UI_FEATURES, it will still be disabled.
GRIST_HOST | hostname to use when listening on a port.
GRIST_HTTPS_PROXY | if set, use this proxy for webhook payload delivery.
GRIST_ID_PREFIX | for subdomains of form o-*, expect or produce o-${GRIST_ID_PREFIX}*.
GRIST_IGNORE_SESSION | if set, Grist will not use a session for authentication.
GRIST_INCLUDE_CUSTOM_SCRIPT_URL | if set, will load the referenced URL in a `<script>` tag on all app pages.
GRIST_INST_DIR | path to Grist instance configuration files, for Grist server.
GRIST_LIST_PUBLIC_SITES | if set to true, sites shared with the public will be listed for anonymous users. Defaults to false.
GRIST_MANAGED_WORKERS | if set, Grist can assume that if a url targeted at a doc worker returns a 404, that worker is gone
GRIST_MAX_UPLOAD_ATTACHMENT_MB | max allowed size for attachments (0 or empty for unlimited).
GRIST_MAX_UPLOAD_IMPORT_MB | max allowed size for imports (except .grist files) (0 or empty for unlimited).
GRIST_OFFER_ALL_LANGUAGES | if set, all translated langauages are offered to the user (by default, only languages with a special 'good enough' key set are offered to user).
GRIST_ORG_IN_PATH | if true, encode org in path rather than domain
GRIST_PAGE_TITLE_SUFFIX | a string to append to the end of the `<title>` in HTML documents. Defaults to `" - Grist"`. Set to `_blank` for no suffix at all.
~GRIST_PROXY_AUTH_HEADER~ | Deprecated, and interpreted as a synonym for GRIST_FORWARD_AUTH_HEADER.
GRIST_ROUTER_URL | optional url for an api that allows servers to be (un)registered with a load balancer
GRIST_SERVE_SAME_ORIGIN | set to "true" to access home server and doc workers on the same protocol-host-port as the top-level page, same as for custom domains (careful, host header should be trustworthy)
GRIST_SERVERS | the types of server to setup. Comma separated values which may contain "home", "docs", static" and/or "app". Defaults to "home,docs,static".
GRIST_SESSION_COOKIE | if set, overrides the name of Grist's cookie
GRIST_SESSION_DOMAIN | if set, associates the cookie with the given domain - otherwise defaults to GRIST_DOMAIN
GRIST_SESSION_SECRET | a key used to encode sessions
GRIST_SKIP_BUNDLED_WIDGETS | if set, Grist will ignore any bundled widgets included via NPM packages.
GRIST_ANON_PLAYGROUND | When set to 'false' deny anonymous users access to the home page
GRIST_FORCE_LOGIN | Much like GRIST_ANON_PLAYGROUND but don't support anonymous access at all (features like sharing docs publicly requires authentication)
GRIST_SINGLE_ORG | set to an org "domain" to pin client to that org
GRIST_TEMPLATE_ORG | set to an org "domain" to show public docs from that org
GRIST_HELP_CENTER | set the help center link ref
FREE_COACHING_CALL_URL | set the link to the human help (example: email adress or meeting scheduling tool)
GRIST_CONTACT_SUPPORT_URL | set the link to contact support on error pages (example: email adress or online form)
GRIST_SUPPORT_ANON | if set to 'true', show UI for anonymous access (not shown by default)
GRIST_SUPPORT_EMAIL | if set, give a user with the specified email support powers. The main extra power is the ability to share sites, workspaces, and docs with all users in a listed way.
GRIST_TELEMETRY_LEVEL | the telemetry level. Can be set to: `off` (default), `limited`, or `full`.
GRIST_THROTTLE_CPU | if set, CPU throttling is enabled
GRIST_TRUST_PLUGINS | if set, plugins are expect to be served from the same host as the rest of the Grist app, rather than from a distinct host. Ordinarily, plugins are served from a distinct host so that the cookies used by the Grist app are not automatically available to them. Enable this only if you understand the security implications.
GRIST_USER_ROOT | an extra path to look for plugins in - Grist will scan for plugins in `$GRIST_USER_ROOT/plugins`.
GRIST_UI_FEATURES | comma-separated list of UI features to enable. Allowed names of parts: `helpCenter,billing,templates,createSite,multiSite,multiAccounts,sendToDrive,tutorials,supportGrist`. If a part also exists in GRIST_HIDE_UI_ELEMENTS, it won't be enabled.
GRIST_UNTRUSTED_PORT | if set, plugins will be served from the given port. This is an alternative to setting APP_UNTRUSTED_URL.
GRIST_WIDGET_LIST_URL | a url pointing to a widget manifest, by default `https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json` is used
COOKIE_MAX_AGE | session cookie max age, defaults to 90 days; can be set to "none" to make it a session cookie
HOME_PORT | port number to listen on for REST API server; if set to "share", add API endpoints to regular grist port.
PORT | port number to listen on for Grist server
REDIS_URL | optional redis server for browser sessions and db query caching
GRIST_SKIP_REDIS_CHECKSUM_MISMATCH | Experimental. If set, only warn if the checksum in Redis differs with the one in your S3 backend storage. You may turn it on if your backend storage implements the [read-after-write consistency](https://aws.amazon.com/fr/blogs/aws/amazon-s3-update-strong-read-after-write-consistency/). Defaults to false.
GRIST_SNAPSHOT_TIME_CAP | optional. Define the caps for tracking buckets. Usage: {"hour": 25, "day": 32, "isoWeek": 12, "month": 96, "year": 1000}
GRIST_SNAPSHOT_KEEP | optional. Number of recent snapshots to retain unconditionally for a document, regardless of when they were made
GRIST_PROMCLIENT_PORT | optional. If set, serve the Prometheus metrics on the specified port number. ⚠️ Be sure to use a port which is not publicly exposed ⚠️.
| Variable | Purpose |
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ALLOWED_WEBHOOK_DOMAINS | comma-separated list of permitted domains to use in webhooks (e.g. webhook.site,zapier.com). You can set this to `*` to allow all domains, but if doing so, we recommend using a carefully locked-down proxy (see `GRIST_HTTPS_PROXY`) if you do not entirely trust users. Otherwise services on your internal network may become vulnerable to manipulation. |
| APP_DOC_URL | doc worker url, set when starting an individual doc worker (other servers will find doc worker urls via redis) |
| APP_DOC_INTERNAL_URL | like `APP_DOC_URL` but used by the home server to reach the server using an internal domain name resolution (like in a docker environment). Defaults to `APP_DOC_URL` |
| APP_HOME_URL | url prefix for home api (home and doc servers need this) |
| APP_STATIC_URL | url prefix for static resources |
| APP_STATIC_INCLUDE_CUSTOM_CSS | set to "true" to include custom.css (from APP_STATIC_URL) in static pages |
| APP_UNTRUSTED_URL | URL at which to serve/expect plugin content. |
| GRIST_ADAPT_DOMAIN | set to "true" to support multiple base domains (careful, host header should be trustworthy) |
| GRIST_APP_ROOT | directory containing Grist sandbox and assets (specifically the sandbox and static subdirectories). |
| GRIST_BACKUP_DELAY_SECS | wait this long after a doc change before making a backup |
| GRIST_BOOT_KEY | if set, offer diagnostics at /boot/GRIST_BOOT_KEY |
| GRIST_DATA_DIR | Directory in which to store documents. Defaults to `docs/` relative to the Grist application directory. In Grist's default Docker image, its default value is /persist/docs so that it will be used as a mounted volume. |
| GRIST_DEFAULT_EMAIL | if set, login as this user if no other credentials presented |
| GRIST_DEFAULT_PRODUCT | if set, this controls enabled features and limits of new sites. See names of PRODUCTS in Product.ts. |
| GRIST_DEFAULT_LOCALE | Locale to use as fallback when Grist cannot honour the browser locale. |
| GRIST_DOMAIN | in hosted Grist, Grist is served from subdomains of this domain. Defaults to "getgrist.com". |
| GRIST_EXPERIMENTAL_PLUGINS | enables experimental plugins |
| GRIST_ENABLE_REQUEST_FUNCTION | enables the REQUEST function. This function performs HTTP requests in a similar way to `requests.request`. This function presents a significant security risk, since it can let users call internal endpoints when Grist is available publicly. This function can also cause performance issues. Unset by default. |
| GRIST_HIDE_UI_ELEMENTS | comma-separated list of UI features to disable. Allowed names of parts: `helpCenter,billing,templates,createSite,multiSite,multiAccounts,sendToDrive,tutorials,supportGrist`. If a part also exists in GRIST_UI_FEATURES, it will still be disabled. |
| GRIST_HOST | hostname to use when listening on a port. |
| GRIST_HTTPS_PROXY | if set, use this proxy for webhook payload delivery. |
| GRIST_ID_PREFIX | for subdomains of form o-*, expect or produce o-${GRIST_ID_PREFIX}*. |
| GRIST_IGNORE_SESSION | if set, Grist will not use a session for authentication. |
| GRIST_INCLUDE_CUSTOM_SCRIPT_URL | if set, will load the referenced URL in a `<script>` tag on all app pages. |
| GRIST_INST_DIR | path to Grist instance configuration files, for Grist server. |
| GRIST_LIST_PUBLIC_SITES | if set to true, sites shared with the public will be listed for anonymous users. Defaults to false. |
| GRIST_MANAGED_WORKERS | if set, Grist can assume that if a url targeted at a doc worker returns a 404, that worker is gone |
| GRIST_MAX_UPLOAD_ATTACHMENT_MB | max allowed size for attachments (0 or empty for unlimited). |
| GRIST_MAX_UPLOAD_IMPORT_MB | max allowed size for imports (except .grist files) (0 or empty for unlimited). |
| GRIST_OFFER_ALL_LANGUAGES | if set, all translated langauages are offered to the user (by default, only languages with a special 'good enough' key set are offered to user). |
| GRIST_ORG_IN_PATH | if true, encode org in path rather than domain |
| GRIST_PAGE_TITLE_SUFFIX | a string to append to the end of the `<title>` in HTML documents. Defaults to `" - Grist"`. Set to `_blank` for no suffix at all. |
| ~GRIST_PROXY_AUTH_HEADER~ | Deprecated, and interpreted as a synonym for GRIST_FORWARD_AUTH_HEADER. |
| GRIST_ROUTER_URL | optional url for an api that allows servers to be (un)registered with a load balancer |
| GRIST_SERVE_SAME_ORIGIN | set to "true" to access home server and doc workers on the same protocol-host-port as the top-level page, same as for custom domains (careful, host header should be trustworthy) |
| GRIST_SERVERS | the types of server to setup. Comma separated values which may contain "home", "docs", static" and/or "app". Defaults to "home,docs,static". |
| GRIST_SESSION_COOKIE | if set, overrides the name of Grist's cookie |
| GRIST_SESSION_DOMAIN | if set, associates the cookie with the given domain - otherwise defaults to GRIST_DOMAIN |
| GRIST_SESSION_SECRET | a key used to encode sessions |
| GRIST_SKIP_BUNDLED_WIDGETS | if set, Grist will ignore any bundled widgets included via NPM packages. |
| GRIST_ANON_PLAYGROUND | When set to 'false' deny anonymous users access to the home page |
| GRIST_FORCE_LOGIN | Much like GRIST_ANON_PLAYGROUND but don't support anonymous access at all (features like sharing docs publicly requires authentication) |
| GRIST_SINGLE_ORG | set to an org "domain" to pin client to that org |
| GRIST_TEMPLATE_ORG | set to an org "domain" to show public docs from that org |
| GRIST_HELP_CENTER | set the help center link ref |
| FREE_COACHING_CALL_URL | set the link to the human help (example: email adress or meeting scheduling tool) |
| GRIST_CONTACT_SUPPORT_URL | set the link to contact support on error pages (example: email adress or online form) |
| GRIST_SUPPORT_ANON | if set to 'true', show UI for anonymous access (not shown by default) |
| GRIST_SUPPORT_EMAIL | if set, give a user with the specified email support powers. The main extra power is the ability to share sites, workspaces, and docs with all users in a listed way. |
| GRIST_TELEMETRY_LEVEL | the telemetry level. Can be set to: `off` (default), `limited`, or `full`. |
| GRIST_THROTTLE_CPU | if set, CPU throttling is enabled |
| GRIST_TRUST_PLUGINS | if set, plugins are expect to be served from the same host as the rest of the Grist app, rather than from a distinct host. Ordinarily, plugins are served from a distinct host so that the cookies used by the Grist app are not automatically available to them. Enable this only if you understand the security implications. |
| GRIST_USER_ROOT | an extra path to look for plugins in - Grist will scan for plugins in `$GRIST_USER_ROOT/plugins`. |
| GRIST_UI_FEATURES | comma-separated list of UI features to enable. Allowed names of parts: `helpCenter,billing,templates,createSite,multiSite,multiAccounts,sendToDrive,tutorials,supportGrist`. If a part also exists in GRIST_HIDE_UI_ELEMENTS, it won't be enabled. |
| GRIST_UNTRUSTED_PORT | if set, plugins will be served from the given port. This is an alternative to setting APP_UNTRUSTED_URL. |
| GRIST_WIDGET_LIST_URL | a url pointing to a widget manifest, by default `https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json` is used |
| COOKIE_MAX_AGE | session cookie max age, defaults to 90 days; can be set to "none" to make it a session cookie |
| HOME_PORT | port number to listen on for REST API server; if set to "share", add API endpoints to regular grist port. |
| PORT | port number to listen on for Grist server |
| REDIS_URL | optional redis server for browser sessions and db query caching |
| GRIST_SKIP_REDIS_CHECKSUM_MISMATCH | Experimental. If set, only warn if the checksum in Redis differs with the one in your S3 backend storage. You may turn it on if your backend storage implements the [read-after-write consistency](https://aws.amazon.com/fr/blogs/aws/amazon-s3-update-strong-read-after-write-consistency/). Defaults to false. |
| GRIST_SNAPSHOT_TIME_CAP | optional. Define the caps for tracking buckets. Usage: {"hour": 25, "day": 32, "isoWeek": 12, "month": 96, "year": 1000} |
| GRIST_SNAPSHOT_KEEP | optional. Number of recent snapshots to retain unconditionally for a document, regardless of when they were made |
| GRIST_PROMCLIENT_PORT | optional. If set, serve the Prometheus metrics on the specified port number. ⚠️ Be sure to use a port which is not publicly exposed ⚠️. |
#### AI Formula Assistant related variables (all optional):

View File

@ -6,6 +6,7 @@ import {AppModel, reportError} from 'app/client/models/AppModel';
import {IProgress} from 'app/client/models/NotifyModel';
import {openFilePicker} from 'app/client/ui/FileDialog';
import {byteString} from 'app/common/gutil';
import { AxiosProgressEvent } from 'axios';
import {Disposable} from 'grainjs';
/**
@ -39,9 +40,9 @@ export async function fileImport(
const timezone = await guessTimezone();
if (workspaceId === "unsaved") {
function onUploadProgress(ev: ProgressEvent) {
if (ev.lengthComputable) {
progress.setUploadProgress(ev.loaded / ev.total * 100); // percentage complete
function onUploadProgress(ev: AxiosProgressEvent) {
if (ev.event.lengthComputable) {
progress.setUploadProgress(ev.event.loaded / ev.event.total * 100); // percentage complete
}
}
return await app.api.importUnsavedDoc(files[0], {timezone, onUploadProgress});

View File

@ -22,6 +22,7 @@ import {
WebhookUpdate
} from 'app/common/Triggers';
import {addCurrentOrgToPath, getGristConfig} from 'app/common/urlUtils';
import { AxiosProgressEvent } from 'axios';
import omitBy from 'lodash/omitBy';
@ -405,7 +406,7 @@ export interface UserAPI {
importUnsavedDoc(material: UploadType, options?: {
filename?: string,
timezone?: string,
onUploadProgress?: (ev: ProgressEvent) => void,
onUploadProgress?: (ev: AxiosProgressEvent) => void,
}): Promise<string>;
deleteUser(userId: number, name: string): Promise<void>;
getBaseUrl(): string; // Get the prefix for all the endpoints this object wraps.
@ -826,7 +827,7 @@ export class UserAPIImpl extends BaseAPI implements UserAPI {
public async importUnsavedDoc(material: UploadType, options?: {
filename?: string,
timezone?: string,
onUploadProgress?: (ev: ProgressEvent) => void,
onUploadProgress?: (ev: AxiosProgressEvent) => void,
}): Promise<string> {
options = options || {};
const formData = this.newFormData();

View File

@ -132,7 +132,7 @@ export class MinIOExternalStorage implements ExternalStorage {
v.lastModified && (v as any).versionId &&
(options?.includeDeleteMarkers || !(v as any).isDeleteMarker))
.map(v => ({
lastModified: v.lastModified.toISOString(),
lastModified: v.lastModified!.toISOString(),
// Circumvent inconsistency of MinIO API with versionId by casting it to string
// PR to MinIO so we don't have to do that anymore:
// https://github.com/minio/minio-js/pull/1193

View File

@ -74,6 +74,10 @@ module.exports = {
{ test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
},
{
test: /\.css$/,
type: 'asset/resource'
}
]
},

View File

@ -87,14 +87,14 @@
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"app-module-path": "2.2.0",
"catw": "1.0.1",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"chance": "1.0.16",
"chokidar-cli": "3.0.0",
"esbuild-loader": "2.19.0",
"eslint": "8.18.0",
"http-proxy": "1.18.1",
"i18next-scanner": "4.1.0",
"i18next-scanner": "4.4.0",
"mocha": "10.2.0",
"mocha-webdriver": "0.3.2",
"moment-locales-webpack-plugin": "^1.2.0",
@ -106,7 +106,7 @@
"tmp-promise": "1.0.5",
"ts-interface-builder": "0.3.2",
"typescript": "4.7.4",
"webpack": "5.73.0",
"webpack": "5.91.0",
"webpack-cli": "4.10.0",
"why-is-node-running": "2.0.3"
},
@ -123,9 +123,9 @@
"accept-language-parser": "1.5.0",
"ace-builds": "1.23.3",
"async-mutex": "0.2.4",
"axios": "0.21.2",
"axios": "1.6.8",
"backbone": "1.3.3",
"bootstrap": "3.3.5",
"bootstrap": "3.4.1",
"bootstrap-datepicker": "1.9.0",
"bowser": "2.7.0",
"collect-js-deps": "^0.1.1",
@ -135,7 +135,7 @@
"connect-redis": "3.4.0",
"cookie": "0.5.0",
"cookie-parser": "1.4.3",
"csv": "4.0.0",
"csv": "6.3.8",
"currency-symbol-map": "5.1.0",
"diff-match-patch": "1.0.5",
"dompurify": "3.0.6",
@ -143,7 +143,7 @@
"engine.io": "^6.5.4",
"engine.io-client": "^6.5.3",
"exceljs": "4.2.1",
"express": "4.18.2",
"express": "4.19.2",
"file-type": "16.5.4",
"fs-extra": "7.0.0",
"grain-rpc": "0.1.7",
@ -160,12 +160,12 @@
"js-yaml": "3.14.1",
"jsdom": "^23.0.0",
"jsesc": "3.0.2",
"jsonwebtoken": "8.3.0",
"jsonwebtoken": "9.0.2",
"knockout": "3.5.0",
"locale-currency": "0.0.2",
"lodash": "4.17.21",
"marked": "4.2.12",
"minio": "7.0.32",
"minio": "7.1.3",
"moment": "2.29.4",
"moment-timezone": "0.5.35",
"morgan": "1.9.1",
@ -184,13 +184,13 @@
"randomcolor": "0.5.3",
"redis": "3.1.1",
"redlock": "3.1.2",
"saml2-js": "2.0.5",
"saml2-js": "4.0.2",
"short-uuid": "3.1.1",
"slugify": "1.6.6",
"swagger-ui-dist": "5.11.0",
"tmp": "0.0.33",
"ts-interface-checker": "1.0.2",
"typeorm": "0.3.9",
"typeorm": "0.3.20",
"underscore": "1.12.1",
"uuid": "3.3.2",
"winston": "2.4.5",

View File

@ -16,7 +16,9 @@ if [ ! -e _build ]; then
fi
tsc --build -w --preserveWatchOutput $PROJECT &
catw app/client/*.css app/client/*/*.css -o static/bundle.css -v & webpack --config $WEBPACK_CONFIG --mode development --watch &
css_files="app/client/**/*.css"
chokidar "${css_files}" -c "bash -O globstar -c 'cat ${css_files} > static/bundle.css'" &
webpack --config $WEBPACK_CONFIG --mode development --watch &
NODE_PATH=_build:_build/stubs:_build/ext nodemon ${NODE_INSPECT:+--inspect} --delay 1 -w _build/app/server -w _build/app/common _build/stubs/app/server/server.js &
wait

View File

@ -207,12 +207,12 @@
"Duplicate Table": "Dupliquer la table",
"You do not have edit access to this document": "Vous navez pas accès en écriture à ce document",
"Delete {{formattedTableName}} data, and remove it from all pages?": "Supprimer les données de {{formattedTableName}} et les supprimer de toutes les pages ?",
"Edit Record Card": "Modifier la vue carte",
"Edit Record Card": "Modifier la vue fiche",
"Rename Table": "Renommer la table",
"{{action}} Record Card": "{{action}} la vue carte",
"Record Card": "Vue carte",
"{{action}} Record Card": "{{action}} la vue fiche",
"Record Card": "Vue fiche",
"Remove Table": "Supprimer la table",
"Record Card Disabled": "Vue carte désactivée"
"Record Card Disabled": "Vue fiche désactivée"
},
"DocHistory": {
"Activity": "Activité",
@ -478,7 +478,8 @@
"Welcome to {{- orgName}}": "Bienvenue sur {{- orgName}}",
"Visit our {{link}} to learn more about Grist.": "Visitez notre {{link}} pour en savoir plus sur Grist.",
"Sign in": "Connexion",
"To use Grist, please either sign up or sign in.": "Pour utiliser Grist, connectez-vous ou créez-vous un compte."
"To use Grist, please either sign up or sign in.": "Pour utiliser Grist, connectez-vous ou créez-vous un compte.",
"Learn more in our {{helpCenterLink}}, or find an expert via our {{sproutsProgram}}.": "Pour en savoir plus, consultez notre {{helpCenterLink}}, ou trouvez un expert via notre {{sproutsProgram}}."
},
"HomeLeftPane": {
"All Documents": "Tous les documents",
@ -665,7 +666,7 @@
"Duplicate rows_other": "Dupliquer les lignes",
"Delete": "Supprimer",
"Copy anchor link": "Copier l'ancre",
"View as card": "Voir en carte",
"View as card": "Voir en fiche",
"Use as table headers": "Utiliser en tant qu'en-têtes de table"
},
"SelectionSummary": {
@ -694,7 +695,10 @@
"Share": "Partager",
"Download...": "Télécharger...",
"Microsoft Excel (.xlsx)": "Microsoft Excel (.xlsx)",
"Export as...": "Exporter en tant que..."
"Export as...": "Exporter en tant que...",
"Comma Separated Values (.csv)": "Comma Separated Values (.csv)",
"DOO Separated Values (.dsv)": "DOO Separated Values (.dsv)",
"Tab Separated Values (.tsv)": "Tab Separated Values (.tsv)"
},
"SiteSwitcher": {
"Switch Sites": "Changer despace",
@ -776,7 +780,7 @@
"Form": "Formulaire",
"Compact": "Compact",
"Blocks": "Blocs",
"Edit Card Layout": "Disposition de la carte",
"Edit Card Layout": "Disposition de la fiche",
"Plugin: ": "Plugin : ",
"Section: ": "Section : "
},
@ -787,7 +791,7 @@
"Print widget": "Imprimer la vue",
"Download as CSV": "Télécharger en CSV",
"Download as XLSX": "Télécharger en XLSX",
"Edit Card Layout": "Disposition de la carte",
"Edit Card Layout": "Disposition de la fiche",
"Widget options": "Options de la vue",
"Advanced Sort & Filter": "Tri et filtre avancés",
"Data selection": "Sélection des données",
@ -931,7 +935,11 @@
},
"NTextBox": {
"false": "faux",
"true": "vrai"
"true": "vrai",
"Single line": "Ligne unique",
"Multi line": "Multi-lignes",
"Lines": "Lignes",
"Field Format": "Format du champ"
},
"ViewAsDropdown": {
"View As": "Voir en tant que",
@ -1002,7 +1010,7 @@
"Make it relational! Use the {{ref}} type to link tables. ": "Rendez-le relationnel ! Utilisez le type {{ref}} pour lier les tableaux. ",
"Toggle the {{creatorPanel}} to format columns, ": "Ouvrez le {{creatorPanel}} pour formater les colonnes ",
"Use {{addNew}} to add widgets, pages, or import more data. ": "Cliquez sur {{addNew}} pour ajouter des vues, des pages ou importer plus de données. ",
"convert to card view, select data, and more.": "convertir en vue carte, sélectionner des données et plus.",
"convert to card view, select data, and more.": "convertir en vue fiche, sélectionner des données et plus.",
"Set formatting options, formulas, or column types, such as dates, choices, or attachments. ": "Définissez des options de formatage de la colonne, des formules ou bien les types de la colonne (dates, liste à choix unique, pièce jointe etc.) ",
"Use {{helpCenter}} for documentation or questions.": "Cliquez sur {{helpCenter}} pour voir la documentation et poser vos questions.",
"creator panel": "menu latéral"
@ -1065,7 +1073,11 @@
"Decimals": "Décimales",
"Currency": "Devise",
"Default currency ({{defaultCurrency}})": "Devise par défaut ({{defaultCurrency}})",
"Number Format": "Format de nombre"
"Number Format": "Format de nombre",
"min": "min",
"Text": "Texte",
"max": "max",
"Field Format": "Format du champ"
},
"LanguageMenu": {
"Language": "Langue"
@ -1092,12 +1104,12 @@
"Only those rows will appear which match all of the filters.": "Seules les lignes qui correspondent à tous les filtres apparaîtront.",
"Select the table to link to.": "Sélectionnez la table vers laquelle vous souhaitez établir un lien.",
"Selecting Data": "Sélection de données",
"Rearrange the fields in your card by dragging and resizing cells.": "Réorganisez les champs de votre vue carte en faisant glisser et en redimensionnant les cellules.",
"Rearrange the fields in your card by dragging and resizing cells.": "Réorganisez les champs de votre vue fiche en faisant glisser et en redimensionnant les cellules.",
"The Raw Data page lists all data tables in your document, including summary tables and tables not included in page layouts.": "La page des données sources liste toutes les tables de votre document, y compris les tables récapitulatives et les tables non-inclues dans les mises en page.",
"The total size of all data in this document, excluding attachments.": "La taille totale de toutes les données de ce document, à l'exclusion des pièces jointes.",
"Click the Add New button to create new documents or workspaces, or import data.": "Cliquez sur le bouton Nouveau pour créer de nouveaux documents dans votre espace de travail ou importer des données.",
"Clicking {{EyeHideIcon}} in each cell hides the field from this view without deleting it.": "En cliquant sur {{EyeHideIcon}} dans chaque cellule, vous masquez le champ de cette vue sans le supprimer.",
"Editing Card Layout": "Modification de la mise en page de la carte",
"Editing Card Layout": "Modification de la mise en page de la fiche",
"Linking Widgets": "Lier les vues",
"Nested Filtering": "Filtrage imbriqué",
"Pinned filters are displayed as buttons above the widget.": "Les filtres épinglés sont affichés sous forme de boutons au-dessus de la vue.",
@ -1230,7 +1242,8 @@
"Opt in to Telemetry": "S'inscrire à l'envoi de données de télémétrie",
"Opted In": "Accepté",
"Support Grist page": "Soutenir Grist",
"Admin Panel": "Panneau d'administration"
"Admin Panel": "Panneau d'administration",
"Thank you! Your trust and support is greatly appreciated. Opt out any time from the {{link}} in the user menu.": "Nous vous remercions! Votre confiance et votre soutien sont très appréciés. Vous pouvez vous désinscrire à tout moment en cliquant sur le {{link}} dans le menu utilisateur."
},
"GridView": {
"Click to insert": "Cliquer pour insérer"
@ -1327,12 +1340,12 @@
"You have access to the following Grist sites.": "Vous avez accès aux espaces Grist suivants."
},
"CardContextMenu": {
"Insert card above": "Insérer une carte au dessus",
"Duplicate card": "Dupliquer la carte",
"Insert card below": "Insérer une carte en dessous",
"Delete card": "Supprimer la carte",
"Insert card above": "Insérer une fiche au dessus",
"Duplicate card": "Dupliquer la fiche",
"Insert card below": "Insérer une fiche en dessous",
"Delete card": "Supprimer la fiche",
"Copy anchor link": "Copier le lien d'ancrage",
"Insert card": "Insérer une carte"
"Insert card": "Insérer une fiche"
},
"WelcomeCoachingCall": {
"Maybe Later": "Peut-être plus tard",
@ -1387,7 +1400,17 @@
},
"FormConfig": {
"Field rules": "Règles du champ",
"Required field": "Champ obligatoire"
"Required field": "Champ obligatoire",
"Ascending": "Croissant",
"Default": "Par défaut",
"Descending": "Décroissant",
"Field Format": "Format du champ",
"Field Rules": "Règles du champ",
"Horizontal": "Horizontale",
"Options Alignment": "Option d'alignement",
"Options Sort Order": "Option d'ordonnancement",
"Vertical": "Verticale",
"Radio": "Radio"
},
"Editor": {
"Delete": "Supprimer"
@ -1414,7 +1437,8 @@
},
"FormSuccessPage": {
"Form Submitted": "Formulaire envoyé",
"Thank you! Your response has been recorded.": "Nous vous remercions. Votre réponse a été enregistrée."
"Thank you! Your response has been recorded.": "Nous vous remercions. Votre réponse a été enregistrée.",
"Submit new response": "Soumettre une nouvelle réponse"
},
"DateRangeOptions": {
"Last 30 days": "30 derniers jours",
@ -1449,5 +1473,29 @@
"Sponsor": "Parrainage",
"Support Grist": "Soutenir Grist",
"Version": "Version"
},
"Field": {
"No choices configured": "Aucun choix configuré",
"No values in show column of referenced table": "Pas de valeurs dans la colonne montrée de la table de référence"
},
"CreateTeamModal": {
"Cancel": "Annuler",
"Choose a name and url for your team site": "Choisissez un nom et une adresse URL pour votre espace d'équipe",
"Create site": "Créer",
"Domain name is invalid": "Le nom de domaine n'est pas valide",
"Domain name is required": "Le nom de domaine est requis",
"Go to your site": "Aller vers votre espace d'équipe",
"Team name": "Nom de l'espace d'équipe",
"Team site created": "L'espace d'équipe a été créé",
"Team url": "Url de l'espace d'équipe",
"Work as a Team": "Travailler en équipe",
"Billing is not supported in grist-core": "La facturation n'est pas prise en charge dans un Grist auto hébergé",
"Team name is required": "Le nom de l'espace d'équipe est requis"
},
"Toggle": {
"Field Format": "Format du champ"
},
"Columns": {
"Remove Column": "Supprimer la colonne"
}
}

View File

@ -1274,7 +1274,7 @@
"User inherits permissions from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "Пользователь наследует разрешения от {{parent}}. Чтобы удалить, установите для параметра 'Наследовать доступ' значение 'None'.",
"Allow anyone with the link to open.": "Разрешить открывать всем, у кого есть ссылка.",
"Grist support": "Grist поддержка",
"Invite people to {{resourceType}}": "Пригласите людей в {{resourceType}}",
"Invite people to {{resourceType}}": "Пригласите людей к {{resourceType}}",
"Create a team to share with more people": "Создайте команду, чтобы поделиться с большим количеством людей",
"Link copied to clipboard": "Ссылка скопирована в буфер обмена",
"No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "Отсутствие доступа по умолчанию позволяет предоставлять доступ к отдельным документам или рабочим областям, а не ко всему групповому сайту.",

View File

@ -31,7 +31,7 @@ import { ActiveDoc, Deps as ActiveDocDeps } from "app/server/lib/ActiveDoc";
import { DEPS, sendForCompletion } from "app/server/lib/Assistance";
import log from 'app/server/lib/log';
import crypto from 'crypto';
import parse from 'csv-parse/lib/sync';
import { parse } from 'csv-parse/sync';
import fetch, {RequestInfo, RequestInit, Response} from 'node-fetch';
import * as fs from "fs";
import JSZip from "jszip";

View File

@ -254,7 +254,7 @@ export class TestSession {
) {
const resp = await axios.get(`${this.home.getOwnUrl()}/test/session`,
{validateStatus: (s => s < 400), headers: this.headers});
const cookie = this.headers.Cookie || resp.headers['set-cookie'][0];
const cookie = this.headers.Cookie || resp.headers['set-cookie']![0];
const cid = decodeURIComponent(cookie.split('=')[1].split(';')[0]);
const sessionId = this.home.getSessions().getSessionIdFromCookie(cid);
const scopedSession = this.home.getSessions().getOrCreateSession(sessionId as string, org, '');

View File

@ -21,7 +21,7 @@ export function configForUser(username: string): AxiosRequestConfig {
}
};
if (username !== 'Anonymous') {
config.headers.Authorization = 'Bearer api_key_for_' + username.toLowerCase();
config.headers!.Authorization = 'Bearer api_key_for_' + username.toLowerCase();
}
return config;
}

View File

@ -166,7 +166,7 @@ export async function openClient(server: FlexServer, email: string, org: string,
const headers: Record<string, string> = {};
if (!emailHeader) {
const resp = await axios.get(`${server.getOwnUrl()}/test/session`);
const cookie = resp.headers['set-cookie'][0];
const cookie = resp.headers['set-cookie']![0];
if (email !== 'anon@getgrist.com') {
const cid = decodeURIComponent(cookie.split('=')[1].split(';')[0]);
const comm = server.getComm();

View File

@ -1351,10 +1351,10 @@ function testDocApi() {
}
} else {
if (sort) {
config.headers['x-sort'] = sort.join(',');
config.headers!['x-sort'] = sort.join(',');
}
if (limit) {
config.headers['x-limit'] = String(limit);
config.headers!['x-limit'] = String(limit);
}
}
return axios.get(url.href, config);
@ -4976,11 +4976,11 @@ function testDocApi() {
const chimpyConfig = configForUser("Chimpy");
const anonConfig = configForUser("Anonymous");
delete chimpyConfig.headers["X-Requested-With"];
delete anonConfig.headers["X-Requested-With"];
delete chimpyConfig.headers!["X-Requested-With"];
delete anonConfig.headers!["X-Requested-With"];
// Target a more realistic Host than "localhost:port"
anonConfig.headers.Host = chimpyConfig.headers.Host = 'api.example.com';
anonConfig.headers!.Host = chimpyConfig.headers!.Host = 'api.example.com';
const url = `${serverUrl}/api/docs/${docId}/tables/Table1/records`;
const data = { records: [{ fields: {} }] };
@ -4989,7 +4989,7 @@ function testDocApi() {
const forbiddenOrigin = 'http://evil.com';
// Normal same origin requests
anonConfig.headers.Origin = allowedOrigin;
anonConfig.headers!.Origin = allowedOrigin;
let response: AxiosResponse;
for (response of [
await axios.post(url, data, anonConfig),
@ -5005,7 +5005,7 @@ function testDocApi() {
// Cross origin requests from untrusted origin.
for (const config of [anonConfig, chimpyConfig]) {
config.headers.Origin = forbiddenOrigin;
config.headers!.Origin = forbiddenOrigin;
for (response of [
await axios.post(url, data, config),
await axios.get(url, config),

View File

@ -28,6 +28,12 @@ describe('ManyFetches', function() {
let docs: TestServer;
let userApi: UserAPIImpl;
before(function () {
if (!process.env.TEST_REDIS_URL) {
return this.skip();
}
});
beforeEach(async function() {
oldEnv = new EnvironmentSnapshot(); // Needed for prepareDatabase, which changes process.env
log.info("Starting servers");

2331
yarn.lock

File diff suppressed because it is too large Load Diff