diff --git a/Dockerfile b/Dockerfile index 20a645e7..f6cafa43 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,12 @@ COPY stubs /grist/stubs COPY buildtools /grist/buildtools RUN yarn run build:prod +# Prepare material for optional pyodide sandbox +COPY sandbox/pyodide /grist/sandbox/pyodide +COPY sandbox/requirements3.txt /grist/sandbox/requirements3.txt +RUN \ + cd /grist/sandbox/pyodide && make setup + ################################################################################ ## Python collection stage ################################################################################ @@ -73,7 +79,7 @@ FROM node:18-buster-slim # Install pgrep for managing gvisor processes. RUN \ apt-get update && \ - apt-get install -y --no-install-recommends libexpat1 libsqlite3-0 procps && \ + apt-get install -y --no-install-recommends libexpat1 libsqlite3-0 procps tini && \ rm -rf /var/lib/apt/lists/* # Keep all storage user may want to persist in a distinct directory @@ -108,6 +114,9 @@ ADD sandbox /grist/sandbox ADD plugins /grist/plugins ADD static /grist/static +# Make optional pyodide sandbox available +COPY --from=builder /grist/sandbox/pyodide /grist/sandbox/pyodide + # Finalize static directory RUN \ mv /grist/static-built/* /grist/static && \ @@ -142,4 +151,5 @@ ENV \ EXPOSE 8484 +ENTRYPOINT ["/usr/bin/tini", "-s", "--"] CMD ["./sandbox/run.sh"] diff --git a/app/server/lib/BootProbes.ts b/app/server/lib/BootProbes.ts index df9427f2..61ac66eb 100644 --- a/app/server/lib/BootProbes.ts +++ b/app/server/lib/BootProbes.ts @@ -118,7 +118,7 @@ const _webSocketsProbe: Probe = { url, }; ws.on('open', () => { - ws.send('Just nod if you can hear me.'); + ws.send('{"msg": "Just nod if you can hear me."}'); resolve({ status: 'success', details, diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index 9d3b28cc..46e4a508 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -1123,6 +1123,7 @@ export class FlexServer implements GristServer { welcomeNewUser ], formMiddleware: [ + this._userIdMiddleware, forcedLoginMiddleware, ], forceLogin: this._redirectToLoginUnconditionally, diff --git a/static/locales/de.client.json b/static/locales/de.client.json index 67fb9665..af9e532d 100644 --- a/static/locales/de.client.json +++ b/static/locales/de.client.json @@ -114,7 +114,9 @@ "Legacy": "Hinterlassenschaft", "Personal Site": "Persönliche Seite", "Team Site": "Teamseite", - "Grist Templates": "Grist Vorlagen" + "Grist Templates": "Grist Vorlagen", + "Billing Account": "Abrechnungskonto", + "Manage Team": "Team verwalten" }, "AppModel": { "This team site is suspended. Documents can be read, but not modified.": "Diese Teamseite ist gesperrt. Die Dokumente können gelesen, aber nicht geändert werden." @@ -335,7 +337,17 @@ "Data Engine": "Datenmaschine", "Default for DateTime columns": "Standard für DateTime-Spalten", "Document ID": "Dokument-ID", - "Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}": "Dokument-ID, die bei Aufrufen der REST-API für {{docId}} zu verwenden ist. Siehe {{apiURL}}" + "Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}": "Dokument-ID, die bei Aufrufen der REST-API für {{docId}} zu verwenden ist. Siehe {{apiURL}}", + "Reload data engine": "Datenmaschine neu laden", + "Reload data engine?": "Datenmaschine neu laden?", + "Start timing": "Startzeitpunkt", + "Stop timing...": "Stoppt die Zeitmessung...", + "Time reload": "Zeit nachladen", + "Force reload the document while timing formulas, and show the result.": "Erzwingen Sie das Neuladen des Dokuments während der Zeitmessung von Formeln, und zeigen Sie das Ergebnis an.", + "Formula timer": "Formel Timer", + "Cancel": "Abbrechen", + "Timing is on": "Das Timing läuft", + "You can make changes to the document, then stop timing to see the results.": "Sie können Änderungen an dem Dokument vornehmen und dann die Zeitmessung stoppen, um die Ergebnisse zu sehen." }, "DocumentUsage": { "Attachments Size": "Größe der Anhänge", @@ -557,7 +569,8 @@ "Trash": "Papierkorb", "Workspace will be moved to Trash.": "Der Arbeitsbereich wird in den Papierkorb verschoben.", "Workspaces": "Arbeitsbereiche", - "Tutorial": "Tutorial" + "Tutorial": "Tutorial", + "Terms of service": "Nutzungsbedingungen" }, "Importer": { "Merge rows that match these fields:": "Zeilen zusammenführen, die mit diesen Feldern übereinstimmen:", @@ -1099,7 +1112,9 @@ "Error in style rule": "Fehler in der Stilregel", "Rule must return True or False": "Regel muss wahr oder falsch zurückgeben", "Add another rule": "Eine weitere Regel hinzufügen", - "Row Style": "Zeilenstil" + "Row Style": "Zeilenstil", + "IF...": "WENN...", + "Conditional Style": "Bedingter Stil" }, "CurrencyPicker": { "Invalid currency": "Ungültige Währung" @@ -1582,7 +1597,21 @@ "unconfigured": "unkonfiguriert", "unknown": "unbekannt", "Check now": "Jetzt prüfen", - "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist ermöglicht sehr leistungsfähige Formeln, die Python verwenden. Wir empfehlen, die Umgebungsvariable GRIST_SANDBOX_FLAVOR auf gvisor zu setzen, wenn Ihre Hardware dies unterstützt (was bei den meisten der Fall ist), um Formeln in jedem Dokument innerhalb einer Sandbox auszuführen, die von anderen Dokumenten und vom Netzwerk isoliert ist." + "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist ermöglicht sehr leistungsfähige Formeln, die Python verwenden. Wir empfehlen, die Umgebungsvariable GRIST_SANDBOX_FLAVOR auf gvisor zu setzen, wenn Ihre Hardware dies unterstützt (was bei den meisten der Fall ist), um Formeln in jedem Dokument innerhalb einer Sandbox auszuführen, die von anderen Dokumenten und vom Netzwerk isoliert ist.", + "Results": "Ergebnisse", + "Self Checks": "Selbstkontrolle", + "Check failed.": "Prüfung fehlgeschlagen.", + "Administrator Panel Unavailable": "Administrator-Panel Nicht verfügbar", + "Authentication": "Authentifizierung", + "Check succeeded.": "Prüfung gelungen.", + "Notes": "Anmerkungen", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "Sie haben keinen Zugriff auf das Administrator-Panel.\nBitte melden Sie sich als Administrator an.", + "Current authentication method": "Aktuelle Authentifizierungsmethode", + "Details": "Details", + "No fault detected.": "Kein Fehler erkannt.", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "In Grist können verschiedene Arten der Authentifizierung konfiguriert werden, darunter SAML und OIDC. Wir empfehlen, eine davon zu aktivieren, wenn Grist über das Netzwerk zugänglich ist oder mehreren Personen zur Verfügung gestellt wird.", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "Als Ausweichmöglichkeit können Sie auch {{bootKey}} in der Umgebung einstellen und {{url}} besuchen", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "In Grist können verschiedene Arten der Authentifizierung konfiguriert werden, darunter SAML und OIDC. Wir empfehlen, eine davon zu aktivieren, wenn Grist über das Netzwerk zugänglich ist oder mehreren Personen zur Verfügung gestellt wird." }, "Section": { "Insert section above": "Abschnitt oben einfügen", @@ -1651,5 +1680,15 @@ "Table": "Tabelle", "Chart": "Diagramm", "Custom": "Benutzerdefiniert" + }, + "TimingPage": { + "Max Time (s)": "Max Zeit(en)", + "Number of Calls": "Anzahl der Anrufe", + "Table ID": "Tabelle ID", + "Total Time (s)": "Gesamtzeit(en)", + "Formula timer": "Formel Timer", + "Average Time (s)": "Durchschnittliche Zeit(en)", + "Loading timing data. Don't close this tab.": "Zeitpunktsdaten laden. Schließen Sie diese Registerkarte nicht.", + "Column ID": "Spalte ID" } } diff --git a/static/locales/es.client.json b/static/locales/es.client.json index 891afbd2..c8163854 100644 --- a/static/locales/es.client.json +++ b/static/locales/es.client.json @@ -281,7 +281,15 @@ "Python": "Python", "python3 (recommended)": "python3 (recomendado)", "Cancel": "Cancelar", - "Force reload the document while timing formulas, and show the result.": "Fuerza la recarga del documento mientras sincronizas las fórmulas y muestra el resultado." + "Force reload the document while timing formulas, and show the result.": "Fuerza la recarga del documento mientras sincronizas las fórmulas y muestra el resultado.", + "Formula timer": "Temporizador de formulas", + "Time reload": "Duración de la recarga", + "Timing is on": "El tiempo está activado", + "Start timing": "Iniciar cronometraje", + "Reload data engine": "Recargar el motor de datos", + "Reload data engine?": "¿Recargar motor de datos?", + "You can make changes to the document, then stop timing to see the results.": "Puede realizar cambios en el documento y luego detener el cronometraje para ver los resultados.", + "Stop timing...": "Dejando de cronometrar..." }, "DuplicateTable": { "Copy all data in addition to the table structure.": "Copiar todos los datos además de la estructura de la tabla.", @@ -477,7 +485,8 @@ "Trash": "Papelera", "Workspace will be moved to Trash.": "El espacio de trabajo se moverá a la papelera.", "Workspaces": "Espacios de trabajo", - "Tutorial": "Tutorial" + "Tutorial": "Tutorial", + "Terms of service": "Términos del servicio" }, "LeftPanelCommon": { "Help Center": "Centro de ayuda" @@ -1166,7 +1175,9 @@ "Error in style rule": "Error en la regla de estilo", "Rule must return True or False": "La regla debe regresar Verdadera o Falsa", "Add conditional style": "Añadir estilo condicional", - "Row Style": "Estilo de fila" + "Row Style": "Estilo de fila", + "IF...": "SI...", + "Conditional Style": "Estilo condicional" }, "Reference": { "SHOW COLUMN": "MOSTRAR COLUMNA", @@ -1580,7 +1591,21 @@ "OK": "De acuerdo", "Sandbox settings for data engine": "Configuración del entorno de pruebas para el motor de datos", "unconfigured": "desconfigurado", - "Learn more.": "Más información." + "Learn more.": "Más información.", + "Authentication": "Autentificación", + "Check succeeded.": "Verificación exitosa.", + "Notes": "Notas", + "Administrator Panel Unavailable": "Panel de administrador no disponible", + "Check failed.": "La verificación falló.", + "Current authentication method": "Método de autenticación actual", + "Details": "Detalles", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist permite configurar diferentes tipos de autenticación, incluidos SAML y OIDC. Recomendamos habilitar uno de estos si se puede acceder a Grist a través de la red o si está disponible a varias personas.", + "No fault detected.": "No se detectó ningún error.", + "Results": "Resultados", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "O, como alternativa, puedes configurar: {{bootKey}} en el entorno y visita: {{url}}", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "No tienes acceso al panel de administrador.\nInicia sesión como administrador.", + "Self Checks": "Controles automáticos", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist permite configurar diferentes tipos de autenticación, incluidos SAML y OIDC. Recomendamos habilitar uno de estos si se puede acceder a Grist a través de la red o si está disponible para varias personas." }, "CreateTeamModal": { "Cancel": "Cancelar", @@ -1645,5 +1670,15 @@ "Custom": "Personalizado", "Form": "Formulario", "Table": "Tabla" + }, + "TimingPage": { + "Average Time (s)": "Tiempo promedio (s)", + "Formula timer": "Temporizador de formulas", + "Loading timing data. Don't close this tab.": "Cargando datos del cronometraje. No cierres esta pestaña.", + "Number of Calls": "Número de llamadas", + "Table ID": "ID de la tabla", + "Total Time (s)": "Tiempo total (s)", + "Column ID": "ID de la columna", + "Max Time (s)": "Tiempo máximo (s)" } } diff --git a/static/locales/pt_BR.client.json b/static/locales/pt_BR.client.json index 6c7f2223..950fd01c 100644 --- a/static/locales/pt_BR.client.json +++ b/static/locales/pt_BR.client.json @@ -114,7 +114,9 @@ "Legacy": "Legado", "Personal Site": "Site pessoal", "Team Site": "Site da Equipe", - "Grist Templates": "Modelos de Grist" + "Grist Templates": "Modelos de Grist", + "Billing Account": "Conta de faturamento", + "Manage Team": "Gerenciar Equipe" }, "AppModel": { "This team site is suspended. Documents can be read, but not modified.": "Este site da equipe está suspenso. Os documentos podem ser lidos, mas não modificados." @@ -335,7 +337,17 @@ "Notify other services on doc changes": "Notifique outros serviços em alterações doc", "Reload": "Recarregar", "Time Zone": "Fuso horário", - "python3 (recommended)": "python3 (recomendado)" + "python3 (recommended)": "python3 (recomendado)", + "Time reload": "Recarga de tempo", + "Timing is on": "O tempo está ligado", + "You can make changes to the document, then stop timing to see the results.": "Você pode fazer alterações no documento e, em seguida, interromper a cronometragem para ver os resultados.", + "Cancel": "Cancelar", + "Force reload the document while timing formulas, and show the result.": "Forçar o recarregamento do documento durante a cronometragem das fórmulas e mostrar o resultado.", + "Formula timer": "Temporizador de Fórmula", + "Reload data engine": "Recarregar o motor de dados", + "Reload data engine?": "Recarregar o motor de dados?", + "Start timing": "Iniciar cronometragem", + "Stop timing...": "Pare de cronometrar..." }, "DocumentUsage": { "Attachments Size": "Tamanho dos Anexos", @@ -557,7 +569,8 @@ "Trash": "Lixeira", "Workspace will be moved to Trash.": "A Área de Trabalho será movida pra Lixeira.", "Workspaces": "Áreas de Trabalho", - "Tutorial": "Tutorial" + "Tutorial": "Tutorial", + "Terms of service": "Termos de serviço" }, "Importer": { "Merge rows that match these fields:": "Mesclar linhas que correspondem a estes campos:", @@ -1083,7 +1096,9 @@ "Rule must return True or False": "A regra deve retornar Verdadeiro ou Falso", "Error in style rule": "Erro na regra de estilo", "Add another rule": "Adicionar outra regra", - "Add conditional style": "Adicionar estilo condicional" + "Add conditional style": "Adicionar estilo condicional", + "Conditional Style": "Estilo condicional", + "IF...": "SE..." }, "CurrencyPicker": { "Invalid currency": "Moeda inválida" @@ -1586,7 +1601,21 @@ "Auto-check when this page loads": "Verificar automaticamente quando esta página carregar", "Checking for updates...": "Verificando atualizações...", "Error checking for updates": "Erro ao verificar atualizações", - "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist permite fórmulas muito poderosas, usando Python. Recomendamos definir a variável de ambiente GRIST_SANDBOX_FLAVOR para gvisor se o seu hardware o suporta (a maioria suportará), para executar fórmulas em cada documento dentro de uma caixa de areia isolada de outros documentos e isolada da rede." + "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist permite fórmulas muito poderosas, usando Python. Recomendamos definir a variável de ambiente GRIST_SANDBOX_FLAVOR para gvisor se o seu hardware o suporta (a maioria suportará), para executar fórmulas em cada documento dentro de uma caixa de areia isolada de outros documentos e isolada da rede.", + "Details": "Detalhes", + "No fault detected.": "Nenhuma falha detectada.", + "Notes": "Notas", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "Ou, como alternativa, você pode definir: {{bootKey}} no ambiente e visitar: {{url}}", + "Results": "Resultados", + "Self Checks": "Autoverificações", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "Você não tem acesso ao painel do administrador.\nFaça login como administrador.", + "Administrator Panel Unavailable": "Painel do administrador indisponível", + "Check succeeded.": "A verificação foi bem-sucedida.", + "Authentication": "Autenticação", + "Check failed.": "A verificação falhou.", + "Current authentication method": "Método de autenticação atual", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "O Grist permite a configuração de diferentes tipos de autenticação, incluindo SAML e OIDC. Recomendamos ativar um desses tipos se o Grist for acessível pela rede ou estiver sendo disponibilizado para várias pessoas.", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "O Grist permite a configuração de diferentes tipos de autenticação, incluindo SAML e OIDC. Recomendamos ativar um desses tipos se o Grist for acessível pela rede ou estiver sendo disponibilizado para várias pessoas." }, "Field": { "No choices configured": "Nenhuma opção configurada", @@ -1651,5 +1680,15 @@ "Custom": "Personalizado", "Table": "Tabela", "Form": "Formulário" + }, + "TimingPage": { + "Average Time (s)": "Tempo(s) médio(s)", + "Max Time (s)": "Tempo(s) máximo(s)", + "Total Time (s)": "Tempo(s) total(s)", + "Formula timer": "Temporizador de Fórmula", + "Column ID": "ID da Coluna", + "Loading timing data. Don't close this tab.": "Carregando dados de tempo. Não feche essa guia.", + "Number of Calls": "Número de chamadas", + "Table ID": "ID da tabela" } } diff --git a/static/locales/ro.client.json b/static/locales/ro.client.json index 19e75da6..6b53dd55 100644 --- a/static/locales/ro.client.json +++ b/static/locales/ro.client.json @@ -245,7 +245,8 @@ "Home Page": "Pagina principală", "Team Site": "Spaţiul echipei", "Legacy": "Versiune veche", - "Grist Templates": "Șabloane Grist" + "Grist Templates": "Șabloane Grist", + "Manage Team": "Gestionează echipa" }, "ViewAsDropdown": { "View As": "Vizualizare ca", @@ -842,7 +843,12 @@ "Python": "Python", "Python version used": "Versiunea Python folosită", "Reload": "Reîncarcă", - "For number and date formats": "Pentru formatele de număr și dată" + "For number and date formats": "Pentru formatele de număr și dată", + "Cancel": "Anulează", + "Start timing": "Începe cronometrarea", + "Manage webhooks": "Gestionează ancore Web", + "Stop timing...": "Oprește cronometrarea...", + "python3 (recommended)": "python3 (recomandat)" }, "ColumnTitle": { "Column ID copied to clipboard": "ID-ul coloanei a fost copiat în clipboard", diff --git a/static/locales/ru.client.json b/static/locales/ru.client.json index 56ba5593..5fbdc3b4 100644 --- a/static/locales/ru.client.json +++ b/static/locales/ru.client.json @@ -97,7 +97,9 @@ "Home Page": "Домашняя страница", "Legacy": "Устаревший", "Team Site": "Сайт группы", - "Grist Templates": "Шаблоны Grist" + "Grist Templates": "Шаблоны Grist", + "Billing Account": "Платежный аккаунт", + "Manage Team": "Управление командой" }, "ApiKey": { "Remove API Key": "Удалить ключ API", @@ -421,7 +423,43 @@ "Document ID copied to clipboard": "Идентификатор документа скопирован в буфер обмена", "Manage Webhooks": "Управление вебхуками", "Webhooks": "Вебхуки", - "API Console": "API-консоль" + "API Console": "API-консоль", + "Default for DateTime columns": "По умолчанию для столбцов ДатаВремя", + "Document ID": "ID Документа", + "Notify other services on doc changes": "Уведомлять другие службы об изменениях в документе", + "python2 (legacy)": "python2 (устаревший)", + "Formula timer": "Таймер формулы", + "Reload data engine": "Перезагрузка механизма обработки данных", + "Start timing": "Старт тайминга", + "Stop timing...": "Остановка тайминга...", + "Currency": "Валюта", + "Data Engine": "Механизм обработки данных", + "Hard reset of data engine": "Жесткий сброс системы обработки данных", + "python3 (recommended)": "python3 (рекомендуется)", + "Cancel": "Отмена", + "Force reload the document while timing formulas, and show the result.": "Принудительно перезагрузите документ, синхронизируя формулы, и покажите результат.", + "Time reload": "Время перезагрузки", + "Reload data engine?": "Перезагрузить механизм обработки данных?", + "Timing is on": "Тайминг включен", + "You can make changes to the document, then stop timing to see the results.": "Вы можете внести изменения в документ, а затем остановить отсчет времени, чтобы увидеть результаты.", + "Coming soon": "Скоро будет", + "Copy to clipboard": "Скопировать в буфер обмена", + "API URL copied to clipboard": "API URL скопирован в буфер обмена", + "API console": "Консоль API", + "API documentation.": "Документация по API.", + "Base doc URL: {{docApiUrl}}": "Базовый URL документа: {{docApiUrl}}", + "Find slow formulas": "Найти медленные формулы", + "For currency columns": "Для столбцов валют", + "For number and date formats": "Для форматов чисел и дат", + "Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}": "ID Документа, используется всегда при запросе REST API {{docId}}. Смотреть {{apiURL}}", + "ID for API use": "ID для использования API", + "Manage webhooks": "Управление webhook-ами", + "Locale": "Локализация", + "Reload": "Перезагрузить", + "Python": "Python", + "Python version used": "Python используемая версия", + "Time Zone": "Часовой пояс", + "Try API calls from the browser": "Попробуйте вызовы API из браузера" }, "DocPageModel": { "Add Widget to Page": "Добавить виджет на страницу", @@ -524,7 +562,8 @@ "Trash": "Корзина", "Workspaces": "Рабочие пространства", "Workspace will be moved to Trash.": "Рабочее пространство будет перемещено в корзину.", - "Tutorial": "Обучение" + "Tutorial": "Обучение", + "Terms of service": "Условия использования" }, "GridViewMenus": { "Add to sort": "Добавить в сортировку", @@ -667,7 +706,8 @@ }, "OnBoardingPopups": { "Finish": "Закончить", - "Next": "Дальше" + "Next": "Дальше", + "Previous": "Предыдущий" }, "PageWidgetPicker": { "Group by": "Группировать по", @@ -1009,7 +1049,8 @@ "Don't show tips": "Не показывать советы", "Undo to restore": "Отменить для восстановления", "Got it": "Принято", - "Don't show again": "Больше не показывать" + "Don't show again": "Больше не показывать", + "TIP": "Совет" }, "search": { "Find Next ": "Найти далее ", @@ -1059,7 +1100,9 @@ "Add conditional style": "Добавить условное форматирование", "Error in style rule": "Ошибка в правиле форматирования", "Row Style": "Форматирование строки", - "Rule must return True or False": "Правило должно возвращать значение Истина или Ложь" + "Rule must return True or False": "Правило должно возвращать значение Истина или Ложь", + "IF...": "ЕСЛИ...", + "Conditional Style": "Условный стиль" }, "CurrencyPicker": { "Invalid currency": "Неверная валюта" @@ -1493,7 +1536,21 @@ "Newer version available": "Доступна более новая версия", "OK": "OK", "unconfigured": "несконфигурированно", - "Security Settings": "Настройки безопасности" + "Security Settings": "Настройки безопасности", + "Authentication": "Аутентификация", + "Check failed.": "Проверка не удалась.", + "Check succeeded.": "Проверка прошла успешно.", + "Details": "Подробности", + "No fault detected.": "Неисправностей не обнаружено.", + "Notes": "Примечания", + "Results": "Результаты", + "Self Checks": "Самопроверки", + "Administrator Panel Unavailable": "Панель администратора недоступна", + "Current authentication method": "Текущий метод аутентификации", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist позволяет настраивать различные типы аутентификации, включая SAML и OIDC. Мы рекомендуем включить один из них, если Grist доступен по сети или доступен нескольким пользователям.", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "Или, в качестве запасного варианта, вы можете установить: {{bootKey}} в окружающей среде и посетить: {{url}}", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist позволяет настраивать различные типы аутентификации, включая SAML и OIDC. Мы рекомендуем включить один из них, если Grist доступен по сети или доступен нескольким пользователям.", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "У вас нет доступа к панели администратора.\nПожалуйста, войдите в систему как администратор." }, "CreateTeamModal": { "Billing is not supported in grist-core": "Выставление счетов в grist-core не поддерживается", @@ -1511,7 +1568,7 @@ }, "Field": { "No choices configured": "Опции отсутствуют", - "No values in show column of referenced table": "No values in show column of referenced table" + "No values in show column of referenced table": "Нет значений в столбце отображения ссылочной таблицы." }, "Columns": { "Remove Column": "Удалить столбец" @@ -1543,5 +1600,30 @@ "No choices matching condition": "Нет вариантов, соответствующих условию", "No choices to select": "Нет вариантов для выбора", "Error in dropdown condition": "Ошибка в условиях выпадающего списка" + }, + "widgetTypesMap": { + "Custom": "Кастомный", + "Form": "Форма", + "Table": "Таблица", + "Calendar": "Календарь", + "Card": "Карточка", + "Card List": "Список карточек", + "Chart": "Диаграмма" + }, + "TimingPage": { + "Table ID": "ID таблицы", + "Formula timer": "Таймер формулы", + "Average Time (s)": "Среднее время (с)", + "Loading timing data. Don't close this tab.": "Загрузка данных о времени. Не закрывайте эту вкладку.", + "Column ID": "ID столбца", + "Max Time (s)": "Макс. время (с)", + "Number of Calls": "Количество Вызовов", + "Total Time (s)": "Общее время (с)" + }, + "FormRenderer": { + "Reset": "Сброс", + "Search": "Поиск", + "Select...": "Выбрать...", + "Submit": "Отправить" } } diff --git a/test/nbrowser/DropdownConditionEditor.ts b/test/nbrowser/DropdownConditionEditor.ts index 9dff24b8..1ad75b0f 100644 --- a/test/nbrowser/DropdownConditionEditor.ts +++ b/test/nbrowser/DropdownConditionEditor.ts @@ -66,8 +66,13 @@ describe('DropdownConditionEditor', function () { 'user.A\nc\ncess\n ', ]); }); - await gu.sendKeysSlowly(['hoice not in $']); + await gu.sendKeysSlowly(['hoice not in ']); + // Attempts to reduce test flakiness by delaying input of $. Not guaranteed to do anything. + await driver.sleep(100); + await gu.sendKeys('$'); await gu.waitToPass(async () => { + // This test is sometimes flaky here. It will consistently return the wrong value, usually an array of + // empty strings. The running theory is it's an issue in Ace editor. const completions = await driver.findAll('.ace_autocomplete .ace_line', el => el.getText()); assert.deepEqual(completions, [ '$\nName\n ', diff --git a/test/nbrowser/ViewLayoutCollapse.ts b/test/nbrowser/ViewLayoutCollapse.ts index 88f10ad4..9086e921 100644 --- a/test/nbrowser/ViewLayoutCollapse.ts +++ b/test/nbrowser/ViewLayoutCollapse.ts @@ -311,7 +311,9 @@ describe("ViewLayoutCollapse", function() { // Move back and drop. await gu.getSection(COMPANIES_CHART).getRect(); - await move(getDragElement(COMPANIES_CHART)); + await move(getDragElement(COMPANIES_CHART), {x : 50}); + await driver.sleep(100); + await move(getDragElement(COMPANIES_CHART), {x : 100}); await driver.sleep(100); await move(getDragElement(COMPANIES_CHART), {x : 200}); await gu.waitToPass(async () => {