From e84c420a37223c192f42e77421b5d9d70d0a698c Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 18 Mar 2024 14:49:42 +0100 Subject: [PATCH 1/9] Don't require running docker with `--init` to stop with ^C (#892) --- app/server/lib/shutdown.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/server/lib/shutdown.js b/app/server/lib/shutdown.js index 4133580b..104a227e 100644 --- a/app/server/lib/shutdown.js +++ b/app/server/lib/shutdown.js @@ -6,6 +6,8 @@ var log = require('app/server/lib/log'); var Promise = require('bluebird'); +var os = require('os'); + var cleanupHandlers = []; var signalsHandled = {}; @@ -65,8 +67,8 @@ function runCleanupHandlers() { } /** - * Internal helper to exit on a signal. It runs the cleanup handlers, and then re-sends the same - * signal, which will no longer get caught. + * Internal helper to exit on a signal. It runs the cleanup handlers, and then + * exits propagating the same signal code than the one caught. */ function signalExit(signal) { var prog = 'grist[' + process.pid + ']'; @@ -81,7 +83,12 @@ function signalExit(signal) { log.info("Server %s exiting on %s", prog, signal); process.removeListener(signal, dup); delete signalsHandled[signal]; - process.kill(process.pid, signal); + // Exit with the expected exit code for being killed by this signal. + // Unlike re-sending the same signal, the explicit exit works even + // in a situation when Grist is the init (pid 1) process in a container + // See https://github.com/gristlabs/grist-core/pull/830 (and #892) + const signalNumber = os.constants.signals[signal]; + process.exit(process.pid, 128 + signalNumber); }); } From 0cc6135005bd42b2643f9c798de8cf0a5e0d3390 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: Sun, 17 Mar 2024 18:28:48 +0000 Subject: [PATCH 2/9] Translated using Weblate (Russian) Currently translated at 99.6% (1121 of 1125 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/ru/ --- static/locales/ru.client.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/static/locales/ru.client.json b/static/locales/ru.client.json index 190f0935..c15d23b3 100644 --- a/static/locales/ru.client.json +++ b/static/locales/ru.client.json @@ -268,7 +268,9 @@ "You do not have write access to this site": "У вас нет права записи для этого сайта", "Remove all data but keep the structure to use as a template": "Удалить все данные, но сохранить структуру для использования в качестве шаблона.", "Download full document and history": "Скачать полный документ и историю", - "Remove document history (can significantly reduce file size)": "Удалить историю документа (может значительно уменьшить размер файла)" + "Remove document history (can significantly reduce file size)": "Удалить историю документа (может значительно уменьшить размер файла)", + "Download": "Скачать", + "Download document": "Скачать документ" }, "ShareMenu": { "Back to Current": "Вернуться к текущему", @@ -1375,5 +1377,15 @@ "FormSuccessPage": { "Form Submitted": "Форма отправлена", "Thank you! Your response has been recorded.": "Спасибо! Ваш ответ учтен." + }, + "DateRangeOptions": { + "Today": "Сегодня", + "Last 30 days": "Последние 30 дней", + "Last 7 days": "Последние 7 дней", + "Last Week": "Последняя неделя", + "Next 7 days": "Следующие 7 дней", + "This month": "Этот месяц", + "This week": "Эта неделя", + "This year": "Текущий год" } } From 8b267c1e000ac5c2795a6682224064d0d319107f Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 20 Mar 2024 14:21:38 +0100 Subject: [PATCH 3/9] Remove unused var GRIST_HOME_INCLUDE_STATIC (#902) The README document referenced a `GRIST_HOME_INCLUDE_STATIC` env variable, which seem to be used nowhere. Also seem to be confusing with GRIST_SERVERS which can be set to `home,static`. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a49e0b30..04db086a 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,6 @@ GRIST_DOMAIN | in hosted Grist, Grist is served from subdomains of this d 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`. If a part also exists in GRIST_UI_FEATURES, it will still be disabled. -GRIST_HOME_INCLUDE_STATIC | if set, home server also serves static resources 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}*. From f3f320a1934c0d8f7dbb0aaf16ce8ebf54f398dd Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Wed, 20 Mar 2024 14:34:09 +0100 Subject: [PATCH 4/9] Feat: rename all column label from a given row with right click (#848) --- app/client/components/GridView.js | 37 ++++++++++++++++++++++++++++ app/client/components/commandList.ts | 5 ++++ app/client/ui/RowContextMenu.ts | 7 ++++++ static/locales/en.client.json | 3 ++- test/nbrowser/RowMenu.ts | 15 +++++++++-- 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/app/client/components/GridView.js b/app/client/components/GridView.js index cafc71ef..73970b2b 100644 --- a/app/client/components/GridView.js +++ b/app/client/components/GridView.js @@ -55,6 +55,7 @@ const {NEW_FILTER_JSON} = require('app/client/models/ColumnFilter'); const {CombinedStyle} = require("app/client/models/Styles"); const {buildRenameColumn} = require('app/client/ui/ColumnTitle'); const {makeT} = require('app/client/lib/localization'); +const { isList } = require('app/common/gristTypes'); const t = makeT('GridView'); @@ -345,6 +346,7 @@ GridView.gridCommands = { this.insertColumn(null, {index: this.cursor.fieldIndex() + 1}); } }, + makeHeadersFromRow: function() { this.makeHeadersFromRow(this.getSelection()); }, renameField: function() { this.renameColumn(this.cursor.fieldIndex()); }, hideFields: function() { this.hideFields(this.getSelection()); }, deleteFields: function() { @@ -902,6 +904,38 @@ GridView.prototype.insertColumn = async function(colId = null, options = {}) { return newColInfo; }; +GridView.prototype.makeHeadersFromRow = async function(selection) { + if (this._getCellContextMenuOptions().disableMakeHeadersFromRow){ + return; + } + const record = this.tableModel.tableData.getRecord(selection.rowIds[0]); + const actions = this.viewSection.viewFields().peek().reduce((acc, field) => { + const col = field.column(); + const colId = col.colId.peek(); + let formatter = field.formatter(); + let newColLabel = record[colId]; + // Manage column that are references + if (col.refTable()) { + const refTableDisplayCol = this.gristDoc.docModel.columns.getRowModel(col.displayCol()); + newColLabel = record[refTableDisplayCol.colId()]; + formatter = field.visibleColFormatter(); + } + // Manage column that are lists + if (isList(newColLabel)) { + newColLabel = newColLabel[1]; + } + if (typeof newColLabel === 'string') { + newColLabel = newColLabel.trim(); + } + // Check value is not empty but accept 0 and false as valid values + if (newColLabel !== null && newColLabel !== undefined && newColLabel !== "") { + return [...acc, ['ModifyColumn', colId, {"label": formatter.formatAny(newColLabel)}]]; + } + return acc + }, []); + this.tableModel.sendTableActions(actions, "Use as table headers"); +}; + GridView.prototype.renameColumn = function(index) { this.currentEditingColumnIndex(index); }; @@ -1974,6 +2008,9 @@ GridView.prototype._getCellContextMenuOptions = function() { this.viewSection.disableAddRemoveRows() || this.getSelection().onlyAddRowSelected() ), + disableMakeHeadersFromRow: Boolean ( + this.gristDoc.isReadonly.get() || this.getSelection().rowIds.length !== 1 || this.getSelection().onlyAddRowSelected() + ), isViewSorted: this.viewSection.activeSortSpec.peek().length > 0, numRows: this.getSelection().rowIds.length, }; diff --git a/app/client/components/commandList.ts b/app/client/components/commandList.ts index d943e9ec..c34fc16e 100644 --- a/app/client/components/commandList.ts +++ b/app/client/components/commandList.ts @@ -84,6 +84,7 @@ export type CommandName = | 'deleteRecords' | 'insertFieldBefore' | 'insertFieldAfter' + | 'makeHeadersFromRow' | 'renameField' | 'hideFields' | 'hideCardFields' @@ -562,6 +563,10 @@ export const groups: CommendGroupDef[] = [{ name: 'insertFieldAfter', keys: ['Alt+='], desc: 'Insert a new column, after the currently selected one' + }, { + name: 'makeHeadersFromRow', + keys: ['Mod+Shift+H'], + desc: 'Use currently selected line as table headers' }, { name: 'renameField', keys: ['Ctrl+m'], diff --git a/app/client/ui/RowContextMenu.ts b/app/client/ui/RowContextMenu.ts index 365387ce..7421880a 100644 --- a/app/client/ui/RowContextMenu.ts +++ b/app/client/ui/RowContextMenu.ts @@ -8,6 +8,7 @@ const t = makeT('RowContextMenu'); export interface IRowContextMenu { disableInsert: boolean; disableDelete: boolean; + disableMakeHeadersFromRow: boolean; disableShowRecordCard: boolean; isViewSorted: boolean; numRows: number; @@ -16,6 +17,7 @@ export interface IRowContextMenu { export function RowContextMenu({ disableInsert, disableDelete, + disableMakeHeadersFromRow, disableShowRecordCard, isViewSorted, numRows @@ -51,6 +53,11 @@ export function RowContextMenu({ menuItemCmd(allCommands.duplicateRows, t('Duplicate rows', { count: numRows }), dom.cls('disabled', disableInsert || numRows === 0)), ); + result.push( + menuDivider(), + menuItemCmd(allCommands.makeHeadersFromRow, t("Use as table headers"), + dom.cls('disabled', disableMakeHeadersFromRow)), + ); result.push( menuDivider(), // TODO: should show `Delete ${num} rows` when multiple are selected diff --git a/static/locales/en.client.json b/static/locales/en.client.json index bec87294..90460109 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -665,7 +665,8 @@ "Insert row": "Insert row", "Insert row above": "Insert row above", "Insert row below": "Insert row below", - "View as card": "View as card" + "View as card": "View as card", + "Use as table headers": "Use as table headers" }, "SelectionSummary": { "Copied to clipboard": "Copied to clipboard" diff --git a/test/nbrowser/RowMenu.ts b/test/nbrowser/RowMenu.ts index f4c4e552..6ee539d8 100644 --- a/test/nbrowser/RowMenu.ts +++ b/test/nbrowser/RowMenu.ts @@ -72,11 +72,22 @@ describe('RowMenu', function() { assert.isFalse(await driver.find('.grist-floating-menu').isPresent()); }); + it('can rename headers from the selected line', async function() { + assert.notEqual(await gu.getColumnHeader({col: 0}).getText(), await gu.getCell(0, 1).getText()); + assert.notEqual(await gu.getColumnHeader({col: 1}).getText(), await gu.getCell(1, 1).getText()); + await (await gu.openRowMenu(1)).findContent('li', /Use as table headers/).click(); + await gu.waitForServer(); + assert.equal(await gu.getColumnHeader({col: 0}).getText(), await gu.getCell(0, 1).getText()); + assert.equal(await gu.getColumnHeader({col: 1}).getText(), await gu.getCell(1, 1).getText()); + }); + it('should work even when no columns are visible', async function() { // Previously, a bug would cause an error to be thrown instead. - await gu.openColumnMenu('A', 'Hide column'); - await gu.openColumnMenu('B', 'Hide column'); + await gu.openColumnMenu({col: 0}, 'Hide column'); + // After hiding the first column, the second one will be the new first column. + await gu.openColumnMenu({col: 0}, 'Hide column'); await assertRowMenuOpensAndCloses(); await assertRowMenuOpensWithRightClick(); }); + }); From e944de5f6c0b0173d1165622a08cd6c26d9c6724 Mon Sep 17 00:00:00 2001 From: Sylvain Boulade Date: Wed, 20 Mar 2024 17:56:04 +0100 Subject: [PATCH 5/9] Add README entry for GRIST_INCLUDE_CUSTOM_SCRIPT_URL variable (#906) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 04db086a..2af173f6 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ 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 `