From 5fa8ebc587dca1b9b67f48e058164aa1fe64aa8d Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 18 Jun 2023 07:36:45 -0400 Subject: [PATCH 01/61] fix: Unneccessary retry of ValidateSSO task --- server/models/UserAuthentication.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/models/UserAuthentication.ts b/server/models/UserAuthentication.ts index ee3433b4f617..efab307d196a 100644 --- a/server/models/UserAuthentication.ts +++ b/server/models/UserAuthentication.ts @@ -11,7 +11,6 @@ import { Unique, } from "sequelize-typescript"; import Logger from "@server/logging/Logger"; -import { AuthenticationError } from "../errors"; import AuthenticationProvider from "./AuthenticationProvider"; import User from "./User"; import IdModel from "./base/IdModel"; @@ -121,7 +120,7 @@ class UserAuthentication extends IdModel { return true; } catch (error) { - if (error instanceof AuthenticationError) { + if (error.id === "authentication_required") { return false; } From 9cfe5da70b112943a7659025ad63217fbe2fc367 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 18 Jun 2023 07:38:57 -0400 Subject: [PATCH 02/61] fix: Error processing task in DetachDraftsFromCollectionTask --- server/queues/tasks/DetachDraftsFromCollectionTask.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/queues/tasks/DetachDraftsFromCollectionTask.ts b/server/queues/tasks/DetachDraftsFromCollectionTask.ts index 9320ff78b1ab..9f2d6ca864e8 100644 --- a/server/queues/tasks/DetachDraftsFromCollectionTask.ts +++ b/server/queues/tasks/DetachDraftsFromCollectionTask.ts @@ -26,6 +26,7 @@ export default class DetachDraftsFromCollectionTask extends BaseTask { const documents = await Document.scope("withDrafts").findAll({ where: { collectionId: props.collectionId, + template: false, publishedAt: { [Op.is]: null, }, From b3c08fdb48be2983f00ca51aeca9ead9225f4704 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 18 Jun 2023 07:43:14 -0400 Subject: [PATCH 03/61] fix: Add guard and log against corrupt zip --- server/queues/tasks/ImportMarkdownZipTask.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/queues/tasks/ImportMarkdownZipTask.ts b/server/queues/tasks/ImportMarkdownZipTask.ts index 21a6b2622f98..112a723f89d6 100644 --- a/server/queues/tasks/ImportMarkdownZipTask.ts +++ b/server/queues/tasks/ImportMarkdownZipTask.ts @@ -60,6 +60,13 @@ export default class ImportMarkdownZipTask extends ImportTask { } const zipObject = zip.files[child.path]; + if (!zipObject) { + Logger.info("task", "Zip file referenced path that doesn't exist", { + path: child.path, + }); + return; + } + const id = uuidv4(); // this is an attachment From f76f2e61d852e119550f3edacdeebe42458c10f6 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 19 Jun 2023 12:54:04 -0400 Subject: [PATCH 04/61] Increase maximum index size, closes #5426 --- server/models/Collection.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/models/Collection.ts b/server/models/Collection.ts index 418540cbeb58..18cc68f53856 100644 --- a/server/models/Collection.ts +++ b/server/models/Collection.ts @@ -167,8 +167,8 @@ class Collection extends ParanoidModel { color: string | null; @Length({ - max: 50, - msg: `index must be 50 characters or less`, + max: 100, + msg: `index must be 100 characters or less`, }) @Column index: string | null; From ba65c52e9796eb1966835faefc5502d522bf8fd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:08:21 -0700 Subject: [PATCH 05/61] chore(deps): bump semver and @types/semver (#5452) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 1768e12d8075..a918cd8d389c 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "reflect-metadata": "^0.1.13", "refractor": "^3.6.0", "request-filtering-agent": "^1.1.2", - "semver": "^7.5.1", + "semver": "^7.5.2", "sequelize": "^6.29.0", "sequelize-cli": "^6.6.0", "sequelize-encrypted": "^1.0.0", @@ -277,7 +277,7 @@ "@types/react-window": "^1.8.5", "@types/redis-info": "^3.0.0", "@types/refractor": "^3.0.2", - "@types/semver": "^7.3.10", + "@types/semver": "^7.5.0", "@types/sequelize": "^4.28.10", "@types/slug": "^5.0.3", "@types/stoppable": "^1.1.1", diff --git a/yarn.lock b/yarn.lock index 94c3713e180a..0b5121347a11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3346,10 +3346,10 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/semver@^7.3.10": - version "7.3.10" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.10.tgz#5f19ee40cbeff87d916eedc8c2bfe2305d957f73" - integrity sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw== +"@types/semver@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== "@types/sequelize@^4.28.10": version "4.28.10" @@ -11655,10 +11655,10 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.1: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== +semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== dependencies: lru-cache "^6.0.0" From 8b87ab0fd75359c5cd753998524c7b926df78ac1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:13:33 -0700 Subject: [PATCH 06/61] chore(deps): bump @babel/plugin-transform-destructuring from 7.20.7 to 7.22.5 (#5451) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a918cd8d389c..320413640470 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "dependencies": { "@babel/core": "^7.21.0", "@babel/plugin-proposal-decorators": "^7.21.0", - "@babel/plugin-transform-destructuring": "^7.20.0", + "@babel/plugin-transform-destructuring": "^7.22.5", "@babel/plugin-transform-regenerator": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/preset-react": "^7.18.6", diff --git a/yarn.lock b/yarn.lock index 0b5121347a11..ae4da89c4247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,10 +221,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" @@ -666,12 +666,12 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" -"@babel/plugin-transform-destructuring@^7.20.0", "@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== +"@babel/plugin-transform-destructuring@^7.20.2", "@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.18.6" From 127728db2922f6562e388dfe9d6cf9009d88431d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:23:55 -0700 Subject: [PATCH 07/61] chore(deps): bump prosemirror-transform from 1.7.2 to 1.7.3 (#5454) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 320413640470..9bae489ff9cc 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "prosemirror-schema-list": "^1.2.3", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.3.2", - "prosemirror-transform": "^1.7.2", + "prosemirror-transform": "^1.7.3", "prosemirror-view": "^1.31.3", "query-string": "^7.1.3", "quoted-printable": "^1.0.1", diff --git a/yarn.lock b/yarn.lock index ae4da89c4247..ffa5679e611a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10679,10 +10679,10 @@ prosemirror-tables@^1.3.2: prosemirror-transform "^1.2.1" prosemirror-view "^1.13.3" -prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.2.tgz#f3e57d8424afa6ab7c2b2319cc0ac58e75f7160b" - integrity sha512-b94lVUdA9NyaYRb2WuGSgb5YANiITa05dtew9eSK+KkYu64BCnU27WhJPE95gAWAnhV57CM3FabWXM23gri8Kg== +prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.3.tgz#cc2225cd1bf88a3c62404b9eb051ff73e9c716a6" + integrity sha512-qDapyx5lqYfxVeUWEw0xTGgeP2S8346QtE7DxkalsXlX89lpzkY6GZfulgfHyk1n4tf74sZ7CcXgcaCcGjsUtA== dependencies: prosemirror-model "^1.0.0" From 38e8a649ef8adbe7c1471f6eb65bd3a001751048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:24:07 -0700 Subject: [PATCH 08/61] chore(deps): bump @babel/core from 7.21.0 to 7.22.5 (#5453) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 231 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 149 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index 9bae489ff9cc..87beb021d119 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "> 0.25%, not dead" ], "dependencies": { - "@babel/core": "^7.21.0", + "@babel/core": "^7.22.5", "@babel/plugin-proposal-decorators": "^7.21.0", "@babel/plugin-transform-destructuring": "^7.22.5", "@babel/plugin-transform-regenerator": "^7.20.0", diff --git a/yarn.lock b/yarn.lock index ffa5679e611a..0a1530262ac2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,45 +42,50 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.22.5" "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.20.14" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== -"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" - integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== +"@babel/compat-data@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" + integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== + +"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" + integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.0" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.21.0" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.0" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.0", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" - integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== +"@babel/generator@^7.22.5", "@babel/generator@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -100,13 +105,13 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" + integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" @@ -164,6 +169,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -179,6 +189,14 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -186,6 +204,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" @@ -200,19 +225,26 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -255,6 +287,13 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" @@ -269,21 +308,38 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-split-export-declaration@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" @@ -294,16 +350,16 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== +"@babel/helpers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" + integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": +"@babel/highlight@^7.10.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== @@ -312,10 +368,19 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.4", "@babel/parser@^7.7.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" - integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.22.5", "@babel/parser@^7.7.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -1068,38 +1133,38 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" +"@babel/traverse@^7.13.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.5", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" + integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": From 51ed458ab2e34cba479d87d27181a07ccc67da06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:35:11 -0700 Subject: [PATCH 09/61] chore(deps): bump compressorjs from 1.1.1 to 1.2.1 (#5455) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 87beb021d119..eeb72bf86419 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "chalk": "^4.1.0", "class-validator": "^0.14.0", "command-score": "^0.1.2", - "compressorjs": "^1.1.1", + "compressorjs": "^1.2.1", "copy-to-clipboard": "^3.3.3", "core-js": "^3.30.2", "crypto-js": "^4.1.1", diff --git a/yarn.lock b/yarn.lock index 0a1530262ac2..beda0716f09a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4893,10 +4893,10 @@ compressible@^2.0.0: dependencies: mime-db ">= 1.43.0 < 2" -compressorjs@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/compressorjs/-/compressorjs-1.1.1.tgz#54c147cf37fb38828b08c48646d0258d52faf050" - integrity sha512-SysRuUPfmUNoq+RviE0iMFVUmoX2q/x+7PkEPUmk6NGkd85hDrmvujx0Qtp8UCGA6KMe5kuodsylPQcNaLf60w== +compressorjs@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/compressorjs/-/compressorjs-1.2.1.tgz#4dee18ef5032f8166bd0a3258f045eda2cd07671" + integrity sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ== dependencies: blueimp-canvas-to-blob "^3.29.0" is-blob "^2.1.0" From cef048572a14337f16582f073382c4a157375f43 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 19 Jun 2023 17:27:34 -0400 Subject: [PATCH 10/61] fix: Unable to click block action button on full width editor closes #5444 --- app/components/Sidebar/components/Toggle.tsx | 36 +++++++++++++++++--- shared/editor/components/Styles.ts | 2 -- shared/styles/depths.ts | 1 - 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/components/Sidebar/components/Toggle.tsx b/app/components/Sidebar/components/Toggle.tsx index 24a4c40e09c2..1d2e24ce0922 100644 --- a/app/components/Sidebar/components/Toggle.tsx +++ b/app/components/Sidebar/components/Toggle.tsx @@ -1,9 +1,10 @@ import * as React from "react"; import { useTranslation } from "react-i18next"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import breakpoint from "styled-components-breakpoint"; import { s } from "@shared/styles"; import Arrow from "~/components/Arrow"; +import useEventListener from "~/hooks/useEventListener"; type Props = { direction: "left" | "right"; @@ -14,8 +15,26 @@ type Props = { const Toggle = React.forwardRef( ({ direction = "left", onClick, style }: Props, ref) => { const { t } = useTranslation(); + const [hovering, setHovering] = React.useState(false); + const positionRef = React.useRef(null); + + // Not using CSS hover here so that we can disable pointer events on this + // div and allow click through to the editor elements behind. + useEventListener("mousemove", (event: MouseEvent) => { + if (!positionRef.current) { + return; + } + + const bound = positionRef.current.getBoundingClientRect(); + const withinBounds = + event.clientX >= bound.left && event.clientX <= bound.right; + if (withinBounds !== hovering) { + setHovering(withinBounds); + } + }); + return ( - + ` } `; -export const Positioner = styled.div` +export const Positioner = styled.div<{ $hovering: boolean }>` display: none; z-index: 2; position: absolute; @@ -64,11 +83,20 @@ export const Positioner = styled.div` bottom: 0; right: -30px; width: 30px; + pointer-events: none; - &:hover ${ToggleButton}, &:focus-within ${ToggleButton} { + &:focus-within ${ToggleButton} { opacity: 1; } + ${(props) => + props.$hovering && + css` + ${ToggleButton} { + opacity: 1; + } + `} + ${breakpoint("tablet")` display: block; `} diff --git a/shared/editor/components/Styles.ts b/shared/editor/components/Styles.ts index 16740411548f..c553d3d27fff 100644 --- a/shared/editor/components/Styles.ts +++ b/shared/editor/components/Styles.ts @@ -1,7 +1,6 @@ /* eslint-disable no-irregular-whitespace */ import { darken, lighten, transparentize } from "polished"; import styled, { DefaultTheme } from "styled-components"; -import depths from "../../styles/depths"; export type Props = { rtl: boolean; @@ -501,7 +500,6 @@ h6:not(.placeholder):before { .heading-actions { opacity: 0; - z-index: ${depths.editorHeadingActions}; background: ${props.theme.background}; margin-${props.rtl ? "right" : "left"}: -26px; flex-direction: ${props.rtl ? "row-reverse" : "row"}; diff --git a/shared/styles/depths.ts b/shared/styles/depths.ts index 57393224be5d..d87360013b7d 100644 --- a/shared/styles/depths.ts +++ b/shared/styles/depths.ts @@ -1,7 +1,6 @@ const depths = { header: 800, sidebar: 900, - editorHeadingActions: 920, editorToolbar: 925, hoverPreview: 950, // Note: editor lightbox is z-index 999 From d7ae829d9260b924df23e5fdd697a41d4b51a083 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 19 Jun 2023 18:42:29 -0400 Subject: [PATCH 11/61] Add sidebar toggle to command menu --- app/actions/definitions/navigation.tsx | 9 +++++++++ shared/i18n/locales/en_US/translation.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/actions/definitions/navigation.tsx b/app/actions/definitions/navigation.tsx index e6a57b6ecb55..234a23bdaf7d 100644 --- a/app/actions/definitions/navigation.tsx +++ b/app/actions/definitions/navigation.tsx @@ -142,6 +142,14 @@ export const openAPIDocumentation = createAction({ perform: () => window.open(developersUrl()), }); +export const toggleSidebar = createAction({ + name: ({ t }) => t("Toggle sidebar"), + analyticsName: "Toggle sidebar", + keywords: "hide show navigation", + section: NavigationSection, + perform: ({ stores }) => stores.ui.toggleCollapsedSidebar(), +}); + export const openFeedbackUrl = createAction({ name: ({ t }) => t("Send us feedback"), analyticsName: "Open feedback", @@ -217,5 +225,6 @@ export const rootNavigationActions = [ openBugReportUrl, openChangelog, openKeyboardShortcuts, + toggleSidebar, logout, ]; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 54babc3b894c..4a0ed6b3e8d1 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -65,6 +65,7 @@ "Notifications": "Notifications", "Preferences": "Preferences", "API documentation": "API documentation", + "Toggle sidebar": "Toggle sidebar", "Send us feedback": "Send us feedback", "Report a bug": "Report a bug", "Changelog": "Changelog", @@ -222,7 +223,6 @@ "Go forward": "Go forward", "Starred": "Starred", "Show more": "Show more", - "Toggle sidebar": "Toggle sidebar", "Up to date": "Up to date", "{{ releasesBehind }} versions behind": "{{ releasesBehind }} version behind", "{{ releasesBehind }} versions behind_plural": "{{ releasesBehind }} versions behind", From 25ae923130c1a09b85879bc9cbca363742315164 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 19 Jun 2023 20:20:32 -0400 Subject: [PATCH 12/61] fix: Cannot drag attachment without selecting first closes #5040 --- shared/editor/components/Widget.tsx | 2 ++ shared/editor/nodes/Attachment.tsx | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/shared/editor/components/Widget.tsx b/shared/editor/components/Widget.tsx index f2a97145154b..c6e4209d7922 100644 --- a/shared/editor/components/Widget.tsx +++ b/shared/editor/components/Widget.tsx @@ -9,6 +9,7 @@ type Props = { href: string; isSelected: boolean; children?: React.ReactNode; + onMouseDown?: React.MouseEventHandler; }; export default function Widget(props: Props & ThemeProps) { @@ -19,6 +20,7 @@ export default function Widget(props: Props & ThemeProps) { } href={props.href} rel="noreferrer nofollow" + onMouseDown={props.onMouseDown} > {props.icon} diff --git a/shared/editor/nodes/Attachment.tsx b/shared/editor/nodes/Attachment.tsx index b96c29f8a8a3..3e889e4fa9a1 100644 --- a/shared/editor/nodes/Attachment.tsx +++ b/shared/editor/nodes/Attachment.tsx @@ -1,6 +1,7 @@ import Token from "markdown-it/lib/token"; import { DownloadIcon } from "outline-icons"; import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model"; +import { NodeSelection } from "prosemirror-state"; import * as React from "react"; import { Trans } from "react-i18next"; import { bytesToHumanReadable } from "../../utils/files"; @@ -66,12 +67,23 @@ export default class Attachment extends Node { }; } - component({ isSelected, theme, node }: ComponentProps) { + handleSelect = + ({ getPos }: { getPos: () => number }) => + () => { + const { view } = this.editor; + const $pos = view.state.doc.resolve(getPos()); + const transaction = view.state.tr.setSelection(new NodeSelection($pos)); + view.dispatch(transaction); + }; + + component = (props: ComponentProps) => { + const { isSelected, theme, node } = props; return ( } href={node.attrs.href} title={node.attrs.title} + onMouseDown={this.handleSelect(props)} context={ node.attrs.href ? ( bytesToHumanReadable(node.attrs.size) @@ -87,7 +99,7 @@ export default class Attachment extends Node { {node.attrs.href && } ); - } + }; commands({ type }: { type: NodeType }) { return (attrs: Record) => toggleWrap(type, attrs); From e7861d0bb9ec9c832771ffe60033490577287364 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 19 Jun 2023 20:50:58 -0400 Subject: [PATCH 13/61] fix: New checkbox items should not be checked by default closes #3663 --- package.json | 2 +- shared/editor/nodes/CheckboxItem.ts | 4 +- yarn.lock | 95 +++++------------------------ 3 files changed, 19 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index eeb72bf86419..bd4069b8156c 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.11.0", "prosemirror-model": "^1.19.2", - "prosemirror-schema-list": "^1.2.3", + "prosemirror-schema-list": "^1.3.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.3.2", "prosemirror-transform": "^1.7.3", diff --git a/shared/editor/nodes/CheckboxItem.ts b/shared/editor/nodes/CheckboxItem.ts index 56b6cfaa07c2..0a947ca76ed5 100644 --- a/shared/editor/nodes/CheckboxItem.ts +++ b/shared/editor/nodes/CheckboxItem.ts @@ -89,7 +89,9 @@ export default class CheckboxItem extends Node { keys({ type }: { type: NodeType }) { return { - Enter: splitListItem(type), + Enter: splitListItem(type, { + checked: false, + }), Tab: sinkListItem(type), "Shift-Tab": liftListItem(type), "Mod-]": sinkListItem(type), diff --git a/yarn.lock b/yarn.lock index beda0716f09a..42ced2fb5efb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -49,12 +49,7 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== - -"@babel/compat-data@^7.22.5": +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== @@ -164,12 +159,7 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-environment-visitor@^7.22.5": +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== @@ -181,15 +171,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-function-name@^7.22.5": +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== @@ -197,14 +179,7 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-hoist-variables@^7.22.5": +"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== @@ -218,14 +193,7 @@ dependencies: "@babel/types" "^7.21.0" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.22.5": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== @@ -280,14 +248,7 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-simple-access@^7.22.5": +"@babel/helper-simple-access@^7.20.2", "@babel/helper-simple-access@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== @@ -301,14 +262,7 @@ dependencies: "@babel/types" "^7.20.0" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.22.5": +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== @@ -320,22 +274,12 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.22.5": +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helper-validator-option@^7.22.5": +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== @@ -359,16 +303,7 @@ "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/highlight@^7.10.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.5": +"@babel/highlight@^7.10.4", "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== @@ -10715,14 +10650,14 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.19.2, dependencies: orderedmap "^2.0.0" -prosemirror-schema-list@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.2.3.tgz#12e3d70cb17780980a3c28588ed7c888121d5e8d" - integrity sha512-HD8yjDOusz7JB3oBFCaMOpEN9Z9DZttLr6tcASjnvKMc0qTyX5xgAN8YiMFFEcwyhF7WZrZ2YQkAwzsn8ICVbQ== +prosemirror-schema-list@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz#05374702cf35a3ba5e7ec31079e355a488d52519" + integrity sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A== dependencies: prosemirror-model "^1.0.0" prosemirror-state "^1.0.0" - prosemirror-transform "^1.0.0" + prosemirror-transform "^1.7.3" prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.4.3: version "1.4.3" From 0e5a576439f3dfaec8567474e0a25eff28765887 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 20 Jun 2023 21:17:13 -0400 Subject: [PATCH 14/61] fix: Clear document policies when public sharing option is updated closes #5461 --- app/components/WebsocketProvider.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/components/WebsocketProvider.tsx b/app/components/WebsocketProvider.tsx index f3bdc1a63eeb..90f64c5e904a 100644 --- a/app/components/WebsocketProvider.tsx +++ b/app/components/WebsocketProvider.tsx @@ -287,6 +287,15 @@ class WebsocketProvider extends React.Component { }); this.socket.on("collections.update", (event: PartialWithId) => { + if ( + "sharing" in event && + event.sharing !== collections.get(event.id)?.sharing + ) { + documents.all.forEach((document) => { + policies.remove(document.id); + }); + } + collections.add(event); }); @@ -313,6 +322,12 @@ class WebsocketProvider extends React.Component { ); this.socket.on("teams.update", (event: PartialWithId) => { + if ("sharing" in event && event.sharing !== auth.team?.sharing) { + documents.all.forEach((document) => { + policies.remove(document.id); + }); + } + auth.team?.updateFromJson(event); }); From eb62b961a41abc00de09238f417e39ce44ab9711 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 20 Jun 2023 21:17:39 -0400 Subject: [PATCH 15/61] feat: Add option to not include attachments in exported data (#5463) --- app/components/ExportDialog.tsx | 30 +++++++++++++++++-- app/models/Collection.ts | 3 +- app/stores/CollectionsStore.ts | 3 +- server/commands/collectionExporter.ts | 3 ++ ...-add-include-attachments-file-operation.js | 15 ++++++++++ server/models/FileOperation.ts | 3 ++ server/queues/tasks/ExportDocumentTreeTask.ts | 11 +++++-- server/queues/tasks/ExportHTMLZipTask.ts | 7 +++-- server/queues/tasks/ExportJSONTask.ts | 26 +++++++++++----- server/queues/tasks/ExportMarkdownZipTask.ts | 7 +++-- server/queues/tasks/ExportTask.ts | 4 ++- server/routes/api/collections.ts | 15 ++++++++-- shared/i18n/locales/en_US/translation.json | 2 ++ 13 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 server/migrations/20230621004649-add-include-attachments-file-operation.js diff --git a/app/components/ExportDialog.tsx b/app/components/ExportDialog.tsx index 7b58210b9f8d..fc51b92432fa 100644 --- a/app/components/ExportDialog.tsx +++ b/app/components/ExportDialog.tsx @@ -21,6 +21,8 @@ function ExportDialog({ collection, onSubmit }: Props) { const [format, setFormat] = React.useState( FileOperationFormat.MarkdownZip ); + const [includeAttachments, setIncludeAttachments] = + React.useState(true); const user = useCurrentUser(); const { showToast } = useToasts(); const { collections } = useStores(); @@ -34,11 +36,18 @@ function ExportDialog({ collection, onSubmit }: Props) { [] ); + const handleIncludeAttachmentsChange = React.useCallback( + (ev: React.ChangeEvent) => { + setIncludeAttachments(ev.target.checked); + }, + [] + ); + const handleSubmit = async () => { if (collection) { - await collection.export(format); + await collection.export(format, includeAttachments); } else { - await collections.export(format); + await collections.export(format, includeAttachments); } onSubmit(); showToast(t("Export started"), { type: "success" }); @@ -107,6 +116,23 @@ function ExportDialog({ collection, onSubmit }: Props) { ))} +
+ ); } diff --git a/app/models/Collection.ts b/app/models/Collection.ts index 79b05631e4a2..dc4350d353f9 100644 --- a/app/models/Collection.ts +++ b/app/models/Collection.ts @@ -266,9 +266,10 @@ export default class Collection extends ParanoidModel { @action unstar = async () => this.store.unstar(this); - export = (format: FileOperationFormat) => + export = (format: FileOperationFormat, includeAttachments: boolean) => client.post("/collections.export", { id: this.id, format, + includeAttachments, }); } diff --git a/app/stores/CollectionsStore.ts b/app/stores/CollectionsStore.ts index 553c274bad62..0cc0d7e15304 100644 --- a/app/stores/CollectionsStore.ts +++ b/app/stores/CollectionsStore.ts @@ -236,8 +236,9 @@ export default class CollectionsStore extends BaseStore { this.rootStore.documents.fetchRecentlyViewed(); }; - export = (format: FileOperationFormat) => + export = (format: FileOperationFormat, includeAttachments: boolean) => client.post("/collections.export_all", { format, + includeAttachments, }); } diff --git a/server/commands/collectionExporter.ts b/server/commands/collectionExporter.ts index 80da27e7dc02..1e884082a9e0 100644 --- a/server/commands/collectionExporter.ts +++ b/server/commands/collectionExporter.ts @@ -13,6 +13,7 @@ type Props = { team: Team; user: User; format?: FileOperationFormat; + includeAttachments?: boolean; ip: string; transaction: Transaction; }; @@ -22,6 +23,7 @@ async function collectionExporter({ team, user, format = FileOperationFormat.MarkdownZip, + includeAttachments = true, ip, transaction, }: Props) { @@ -36,6 +38,7 @@ async function collectionExporter({ url: null, size: 0, collectionId, + includeAttachments, userId: user.id, teamId: user.teamId, }, diff --git a/server/migrations/20230621004649-add-include-attachments-file-operation.js b/server/migrations/20230621004649-add-include-attachments-file-operation.js new file mode 100644 index 000000000000..3249c5f0c5db --- /dev/null +++ b/server/migrations/20230621004649-add-include-attachments-file-operation.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + async up (queryInterface, Sequelize) { + await queryInterface.addColumn("file_operations", "includeAttachments", { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true, + }); + }, + + async down (queryInterface, Sequelize) { + await queryInterface.removeColumn("file_operations", "includeAttachments"); + } +}; diff --git a/server/models/FileOperation.ts b/server/models/FileOperation.ts index 8ed6345048be..43ee872617e5 100644 --- a/server/models/FileOperation.ts +++ b/server/models/FileOperation.ts @@ -58,6 +58,9 @@ class FileOperation extends IdModel { @Column(DataType.BIGINT) size: number; + @Column(DataType.BOOLEAN) + includeAttachments: boolean; + /** * Mark the current file operation as expired and remove the file from storage. */ diff --git a/server/queues/tasks/ExportDocumentTreeTask.ts b/server/queues/tasks/ExportDocumentTreeTask.ts index b3e97cc1684b..c61bd63fff80 100644 --- a/server/queues/tasks/ExportDocumentTreeTask.ts +++ b/server/queues/tasks/ExportDocumentTreeTask.ts @@ -25,12 +25,14 @@ export default abstract class ExportDocumentTreeTask extends ExportTask { pathInZip, documentId, format = FileOperationFormat.MarkdownZip, + includeAttachments, pathMap, }: { zip: JSZip; pathInZip: string; documentId: string; format: FileOperationFormat; + includeAttachments: boolean; pathMap: Map; }) { Logger.debug("task", `Adding document to archive`, { documentId }); @@ -44,7 +46,9 @@ export default abstract class ExportDocumentTreeTask extends ExportTask { ? await DocumentHelper.toHTML(document, { centered: true }) : await DocumentHelper.toMarkdown(document); - const attachmentIds = parseAttachmentIds(document.text); + const attachmentIds = includeAttachments + ? parseAttachmentIds(document.text) + : []; const attachments = attachmentIds.length ? await Attachment.findAll({ where: { @@ -117,13 +121,15 @@ export default abstract class ExportDocumentTreeTask extends ExportTask { * @param zip The JSZip instance to add files to * @param collections The collections to export * @param format The format to export in + * @param includeAttachments Whether to include attachments in the export * * @returns The path to the zip file in tmp. */ protected async addCollectionsToArchive( zip: JSZip, collections: Collection[], - format: FileOperationFormat + format: FileOperationFormat, + includeAttachments = true ) { const pathMap = this.createPathMap(collections, format); Logger.debug( @@ -139,6 +145,7 @@ export default abstract class ExportDocumentTreeTask extends ExportTask { zip, pathInZip, documentId, + includeAttachments, format, pathMap, }); diff --git a/server/queues/tasks/ExportHTMLZipTask.ts b/server/queues/tasks/ExportHTMLZipTask.ts index fc6a84ef927b..2d16cf597a22 100644 --- a/server/queues/tasks/ExportHTMLZipTask.ts +++ b/server/queues/tasks/ExportHTMLZipTask.ts @@ -1,16 +1,17 @@ import JSZip from "jszip"; import { FileOperationFormat } from "@shared/types"; -import { Collection } from "@server/models"; +import { Collection, FileOperation } from "@server/models"; import ExportDocumentTreeTask from "./ExportDocumentTreeTask"; export default class ExportHTMLZipTask extends ExportDocumentTreeTask { - public async export(collections: Collection[]) { + public async export(collections: Collection[], fileOperation: FileOperation) { const zip = new JSZip(); return await this.addCollectionsToArchive( zip, collections, - FileOperationFormat.HTMLZip + FileOperationFormat.HTMLZip, + fileOperation.includeAttachments ); } } diff --git a/server/queues/tasks/ExportJSONTask.ts b/server/queues/tasks/ExportJSONTask.ts index dcb8d254dc2c..8ee6e64d5cbd 100644 --- a/server/queues/tasks/ExportJSONTask.ts +++ b/server/queues/tasks/ExportJSONTask.ts @@ -25,7 +25,11 @@ export default class ExportJSONTask extends ExportTask { // serial to avoid overloading, slow and steady wins the race for (const collection of collections) { - await this.addCollectionToArchive(zip, collection); + await this.addCollectionToArchive( + zip, + collection, + fileOperation.includeAttachments + ); } await this.addMetadataToArchive(zip, fileOperation); @@ -52,7 +56,11 @@ export default class ExportJSONTask extends ExportTask { ); } - private async addCollectionToArchive(zip: JSZip, collection: Collection) { + private async addCollectionToArchive( + zip: JSZip, + collection: Collection, + includeAttachments: boolean + ) { const output: CollectionJSONExport = { collection: { ...omit(presentCollection(collection), ["url"]), @@ -75,12 +83,14 @@ export default class ExportJSONTask extends ExportTask { continue; } - const attachments = await Attachment.findAll({ - where: { - teamId: document.teamId, - id: parseAttachmentIds(document.text), - }, - }); + const attachments = includeAttachments + ? await Attachment.findAll({ + where: { + teamId: document.teamId, + id: parseAttachmentIds(document.text), + }, + }) + : []; await Promise.all( attachments.map(async (attachment) => { diff --git a/server/queues/tasks/ExportMarkdownZipTask.ts b/server/queues/tasks/ExportMarkdownZipTask.ts index 6ea69eefe5e2..8da8b5530259 100644 --- a/server/queues/tasks/ExportMarkdownZipTask.ts +++ b/server/queues/tasks/ExportMarkdownZipTask.ts @@ -1,16 +1,17 @@ import JSZip from "jszip"; import { FileOperationFormat } from "@shared/types"; -import { Collection } from "@server/models"; +import { Collection, FileOperation } from "@server/models"; import ExportDocumentTreeTask from "./ExportDocumentTreeTask"; export default class ExportMarkdownZipTask extends ExportDocumentTreeTask { - public async export(collections: Collection[]) { + public async export(collections: Collection[], fileOperation: FileOperation) { const zip = new JSZip(); return await this.addCollectionsToArchive( zip, collections, - FileOperationFormat.MarkdownZip + FileOperationFormat.MarkdownZip, + fileOperation.includeAttachments ); } } diff --git a/server/queues/tasks/ExportTask.ts b/server/queues/tasks/ExportTask.ts index b59790699b6d..159168bed7aa 100644 --- a/server/queues/tasks/ExportTask.ts +++ b/server/queues/tasks/ExportTask.ts @@ -41,7 +41,9 @@ export default abstract class ExportTask extends BaseTask { }); try { - Logger.info("task", `ExportTask processing data for ${fileOperationId}`); + Logger.info("task", `ExportTask processing data for ${fileOperationId}`, { + includeAttachments: fileOperation.includeAttachments, + }); await this.updateFileOperation(fileOperation, { state: FileOperationState.Creating, diff --git a/server/routes/api/collections.ts b/server/routes/api/collections.ts index 16ef9b33f4fd..5f72978ced1b 100644 --- a/server/routes/api/collections.ts +++ b/server/routes/api/collections.ts @@ -49,6 +49,7 @@ import { assertHexColor, assertIndexCharacters, assertCollectionPermission, + assertBoolean, } from "@server/validation"; import pagination from "./middlewares/pagination"; @@ -562,10 +563,14 @@ router.post( auth(), async (ctx: APIContext) => { const { id } = ctx.request.body; - const { format = FileOperationFormat.MarkdownZip } = ctx.request.body; + const { + format = FileOperationFormat.MarkdownZip, + includeAttachments = true, + } = ctx.request.body; assertUuid(id, "id is required"); assertIn(format, Object.values(FileOperationFormat), "Invalid format"); + assertBoolean(includeAttachments, "includeAttachments must be a boolean"); const { user } = ctx.state.auth; const team = await Team.findByPk(user.teamId); @@ -582,6 +587,7 @@ router.post( user, team, format, + includeAttachments, ip: ctx.request.ip, transaction, }) @@ -601,18 +607,23 @@ router.post( rateLimiter(RateLimiterStrategy.FivePerHour), auth(), async (ctx: APIContext) => { - const { format = FileOperationFormat.MarkdownZip } = ctx.request.body; + const { + format = FileOperationFormat.MarkdownZip, + includeAttachments = true, + } = ctx.request.body; const { user } = ctx.state.auth; const team = await Team.findByPk(user.teamId); authorize(user, "createExport", team); assertIn(format, Object.values(FileOperationFormat), "Invalid format"); + assertBoolean(includeAttachments, "includeAttachments must be a boolean"); const fileOperation = await sequelize.transaction(async (transaction) => collectionExporter({ user, team, format, + includeAttachments, ip: ctx.request.ip, transaction, }) diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 4a0ed6b3e8d1..007796dee069 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -181,6 +181,8 @@ "Export": "Export", "Exporting the collection {{collectionName}} may take some time.": "Exporting the collection {{collectionName}} may take some time.", "You will receive an email when it's complete.": "You will receive an email when it's complete.", + "Include attachments": "Include attachments", + "Including uploaded images and files in the exported data": "Including uploaded images and files in the exported data", "{{ count }} member": "{{ count }} member", "{{ count }} member_plural": "{{ count }} members", "Group members": "Group members", From 6d556c7a55297b45aa7d958497881db3f6a373a3 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 20 Jun 2023 23:11:48 -0400 Subject: [PATCH 16/61] Include collectionId in revisions.create webhook payload --- .../server/tasks/DeliverWebhookTask.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/plugins/webhooks/server/tasks/DeliverWebhookTask.ts b/plugins/webhooks/server/tasks/DeliverWebhookTask.ts index c1d4740bd948..fc4027e8696c 100644 --- a/plugins/webhooks/server/tasks/DeliverWebhookTask.ts +++ b/plugins/webhooks/server/tasks/DeliverWebhookTask.ts @@ -515,16 +515,26 @@ export default class DeliverWebhookTask extends BaseTask { subscription: WebhookSubscription, event: RevisionEvent ): Promise { - const model = await Revision.findByPk(event.modelId, { - paranoid: false, - }); + const [model, document] = await Promise.all([ + Revision.findByPk(event.modelId, { + paranoid: false, + }), + Document.findByPk(event.documentId, { + paranoid: false, + }), + ]); + + const data = { + ...(model ? await presentRevision(model) : {}), + collectionId: document ? document.collectionId : undefined, + }; await this.sendWebhook({ event, subscription, payload: { id: event.modelId, - model: model && (await presentRevision(model)), + model: data, }, }); } From 8d69de1be06ec1a185d0af86b44286a23ad233e9 Mon Sep 17 00:00:00 2001 From: Apoorv Mishra Date: Wed, 21 Jun 2023 10:38:38 +0530 Subject: [PATCH 17/61] chore: request validation for searches (#5460) --- server/routes/api/searches/index.ts | 1 + server/routes/api/searches/schema.ts | 14 +++ server/routes/api/searches/searches.test.ts | 114 +++++++++++++++++++ server/routes/api/{ => searches}/searches.ts | 43 +++---- server/test/factories.ts | 33 +++++- 5 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 server/routes/api/searches/index.ts create mode 100644 server/routes/api/searches/schema.ts create mode 100644 server/routes/api/searches/searches.test.ts rename server/routes/api/{ => searches}/searches.ts (57%) diff --git a/server/routes/api/searches/index.ts b/server/routes/api/searches/index.ts new file mode 100644 index 000000000000..5c9c41a506ee --- /dev/null +++ b/server/routes/api/searches/index.ts @@ -0,0 +1 @@ +export { default } from "./searches"; diff --git a/server/routes/api/searches/schema.ts b/server/routes/api/searches/schema.ts new file mode 100644 index 000000000000..c99fc4973628 --- /dev/null +++ b/server/routes/api/searches/schema.ts @@ -0,0 +1,14 @@ +import { isEmpty } from "lodash"; +import { z } from "zod"; +import BaseSchema from "../BaseSchema"; + +export const SearchesDeleteSchema = BaseSchema.extend({ + body: z.object({ + id: z.string().uuid().optional(), + query: z.string().optional(), + }), +}).refine((req) => !(isEmpty(req.body.id) && isEmpty(req.body.query)), { + message: "id or query is required", +}); + +export type SearchesDeleteReq = z.infer; diff --git a/server/routes/api/searches/searches.test.ts b/server/routes/api/searches/searches.test.ts new file mode 100644 index 000000000000..37d8a88b0c94 --- /dev/null +++ b/server/routes/api/searches/searches.test.ts @@ -0,0 +1,114 @@ +import { SearchQuery, User } from "@server/models"; +import { buildSearchQuery, buildUser } from "@server/test/factories"; +import { getTestServer } from "@server/test/support"; + +const server = getTestServer(); + +describe("#searches.list", () => { + let user: User; + + beforeEach(async () => { + user = await buildUser(); + + await Promise.all([ + buildSearchQuery({ + userId: user.id, + teamId: user.teamId, + }), + buildSearchQuery({ + userId: user.id, + teamId: user.teamId, + query: "foo", + }), + buildSearchQuery({ + userId: user.id, + teamId: user.teamId, + query: "bar", + }), + ]); + }); + + it("should succeed with status 200 ok returning results", async () => { + const res = await server.post("/api/searches.list", { + body: { + token: user.getJwtToken(), + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toHaveLength(3); + const queries = body.data.map((d: any) => d.query); + expect(queries).toContain("query"); + expect(queries).toContain("foo"); + expect(queries).toContain("bar"); + }); +}); + +describe("#searches.delete", () => { + let user: User; + let searchQuery: SearchQuery; + + beforeEach(async () => { + user = await buildUser(); + + searchQuery = await buildSearchQuery({ + userId: user.id, + teamId: user.teamId, + }); + }); + + it("should fail with status 400 bad request when no id or query is provided", async () => { + const res = await server.post("/api/searches.delete", { + body: { + token: user.getJwtToken(), + }, + }); + + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("id or query is required"); + }); + + it("should fail with status 400 bad request when an invalid id is provided", async () => { + const res = await server.post("/api/searches.delete", { + body: { + token: user.getJwtToken(), + id: "id", + }, + }); + + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("id: Invalid uuid"); + }); + + it("should succeed with status 200 ok and successfully delete the query", async () => { + let searchQueries = await SearchQuery.findAll({ + where: { + userId: user.id, + teamId: user.teamId, + }, + }); + expect(searchQueries).toHaveLength(1); + + const res = await server.post("/api/searches.delete", { + body: { + token: user.getJwtToken(), + id: searchQuery.id, + }, + }); + + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.success).toEqual(true); + + searchQueries = await SearchQuery.findAll({ + where: { + userId: user.id, + teamId: user.teamId, + }, + }); + expect(searchQueries).toHaveLength(0); + }); +}); diff --git a/server/routes/api/searches.ts b/server/routes/api/searches/searches.ts similarity index 57% rename from server/routes/api/searches.ts rename to server/routes/api/searches/searches.ts index 5caf588cc663..1126ffd2fc2c 100644 --- a/server/routes/api/searches.ts +++ b/server/routes/api/searches/searches.ts @@ -1,10 +1,11 @@ import Router from "koa-router"; import auth from "@server/middlewares/authentication"; +import validate from "@server/middlewares/validate"; import { SearchQuery } from "@server/models"; import { presentSearchQuery } from "@server/presenters"; import { APIContext } from "@server/types"; -import { assertPresent, assertUuid } from "@server/validation"; -import pagination from "./middlewares/pagination"; +import pagination from "../middlewares/pagination"; +import * as T from "./schema"; const router = new Router(); @@ -26,24 +27,26 @@ router.post("searches.list", auth(), pagination(), async (ctx: APIContext) => { }; }); -router.post("searches.delete", auth(), async (ctx: APIContext) => { - const { id, query } = ctx.request.body; - assertPresent(id || query, "id or query is required"); - if (id) { - assertUuid(id, "id is must be a uuid"); +router.post( + "searches.delete", + auth(), + validate(T.SearchesDeleteSchema), + async (ctx: APIContext) => { + const { id, query } = ctx.input.body; + + const { user } = ctx.state.auth; + + await SearchQuery.destroy({ + where: { + ...(id ? { id } : { query }), + userId: user.id, + }, + }); + + ctx.body = { + success: true, + }; } - - const { user } = ctx.state.auth; - await SearchQuery.destroy({ - where: { - ...(id ? { id } : { query }), - userId: user.id, - }, - }); - - ctx.body = { - success: true, - }; -}); +); export default router; diff --git a/server/test/factories.ts b/server/test/factories.ts index bb4b67318e64..c3ad2c93a074 100644 --- a/server/test/factories.ts +++ b/server/test/factories.ts @@ -1,4 +1,4 @@ -import { isNull } from "lodash"; +import { isNil, isNull } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { CollectionPermission, @@ -28,6 +28,7 @@ import { ApiKey, Subscription, Notification, + SearchQuery, } from "@server/models"; let count = 1; @@ -517,3 +518,33 @@ export async function buildNotification( return Notification.create(overrides); } + +export async function buildSearchQuery( + overrides: Partial = {} +): Promise { + if (!overrides.teamId) { + const team = await buildTeam(); + overrides.teamId = team.id; + } + + if (!overrides.userId) { + const user = await buildUser({ + teamId: overrides.teamId, + }); + overrides.userId = user.id; + } + + if (!overrides.source) { + overrides.source = "app"; + } + + if (isNil(overrides.query)) { + overrides.query = "query"; + } + + if (isNil(overrides.results)) { + overrides.results = 1; + } + + return SearchQuery.create(overrides); +} From a0940873429a8e33bc2cfae132efe5e6fdf66ba7 Mon Sep 17 00:00:00 2001 From: Tobi Kremer Date: Wed, 21 Jun 2023 12:47:34 +0200 Subject: [PATCH 18/61] Remove temporary files after processing (#5456) Co-authored-by: Tom Moor --- server/queues/tasks/ExportTask.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/queues/tasks/ExportTask.ts b/server/queues/tasks/ExportTask.ts index 159168bed7aa..906b2d15b735 100644 --- a/server/queues/tasks/ExportTask.ts +++ b/server/queues/tasks/ExportTask.ts @@ -40,6 +40,8 @@ export default abstract class ExportTask extends BaseTask { }, }); + let filePath: string | undefined; + try { Logger.info("task", `ExportTask processing data for ${fileOperationId}`, { includeAttachments: fileOperation.includeAttachments, @@ -49,7 +51,7 @@ export default abstract class ExportTask extends BaseTask { state: FileOperationState.Creating, }); - const filePath = await this.export(collections, fileOperation); + filePath = await this.export(collections, fileOperation); Logger.info("task", `ExportTask uploading data for ${fileOperationId}`); @@ -95,6 +97,12 @@ export default abstract class ExportTask extends BaseTask { }).schedule(); } throw error; + } finally { + if (filePath) { + void fs.promises.unlink(filePath).catch((error) => { + Logger.error(`Failed to delete temporary file ${filePath}`, error); + }); + } } } From d96bf5106d5bfbe37c12d4b2b6df99112f68b88d Mon Sep 17 00:00:00 2001 From: Apoorv Mishra Date: Thu, 22 Jun 2023 15:57:00 +0530 Subject: [PATCH 19/61] chore: request validation for pins (#5465) --- server/routes/api/pins.ts | 158 ----------- server/routes/api/pins/index.ts | 1 + server/routes/api/pins/pins.test.ts | 410 ++++++++++++++++++++++++++++ server/routes/api/pins/pins.ts | 168 ++++++++++++ server/routes/api/pins/schema.ts | 52 ++++ server/test/factories.ts | 24 ++ 6 files changed, 655 insertions(+), 158 deletions(-) delete mode 100644 server/routes/api/pins.ts create mode 100644 server/routes/api/pins/index.ts create mode 100644 server/routes/api/pins/pins.test.ts create mode 100644 server/routes/api/pins/pins.ts create mode 100644 server/routes/api/pins/schema.ts diff --git a/server/routes/api/pins.ts b/server/routes/api/pins.ts deleted file mode 100644 index 7cc452b7b476..000000000000 --- a/server/routes/api/pins.ts +++ /dev/null @@ -1,158 +0,0 @@ -import Router from "koa-router"; -import { Sequelize, Op } from "sequelize"; -import pinCreator from "@server/commands/pinCreator"; -import pinDestroyer from "@server/commands/pinDestroyer"; -import pinUpdater from "@server/commands/pinUpdater"; -import auth from "@server/middlewares/authentication"; -import { Collection, Document, Pin } from "@server/models"; -import { authorize } from "@server/policies"; -import { - presentPin, - presentDocument, - presentPolicies, -} from "@server/presenters"; -import { APIContext } from "@server/types"; -import { assertUuid, assertIndexCharacters } from "@server/validation"; -import pagination from "./middlewares/pagination"; - -const router = new Router(); - -router.post("pins.create", auth(), async (ctx: APIContext) => { - const { documentId, collectionId } = ctx.request.body; - const { index } = ctx.request.body; - assertUuid(documentId, "documentId is required"); - - const { user } = ctx.state.auth; - const document = await Document.findByPk(documentId, { - userId: user.id, - }); - authorize(user, "read", document); - - if (collectionId) { - const collection = await Collection.scope({ - method: ["withMembership", user.id], - }).findByPk(collectionId); - authorize(user, "update", collection); - authorize(user, "pin", document); - } else { - authorize(user, "pinToHome", document); - } - - if (index) { - assertIndexCharacters(index); - } - - const pin = await pinCreator({ - user, - documentId, - collectionId, - ip: ctx.request.ip, - index, - }); - - ctx.body = { - data: presentPin(pin), - policies: presentPolicies(user, [pin]), - }; -}); - -router.post("pins.list", auth(), pagination(), async (ctx: APIContext) => { - const { collectionId } = ctx.request.body; - const { user } = ctx.state.auth; - - const [pins, collectionIds] = await Promise.all([ - Pin.findAll({ - where: { - ...(collectionId - ? { collectionId } - : { collectionId: { [Op.is]: null } }), - teamId: user.teamId, - }, - order: [ - Sequelize.literal('"pin"."index" collate "C"'), - ["updatedAt", "DESC"], - ], - offset: ctx.state.pagination.offset, - limit: ctx.state.pagination.limit, - }), - user.collectionIds(), - ]); - - const documents = await Document.defaultScopeWithUser(user.id).findAll({ - where: { - id: pins.map((pin) => pin.documentId), - collectionId: collectionIds, - }, - }); - - const policies = presentPolicies(user, [...documents, ...pins]); - - ctx.body = { - pagination: ctx.state.pagination, - data: { - pins: pins.map(presentPin), - documents: await Promise.all( - documents.map((document: Document) => presentDocument(document)) - ), - }, - policies, - }; -}); - -router.post("pins.update", auth(), async (ctx: APIContext) => { - const { id, index } = ctx.request.body; - assertUuid(id, "id is required"); - - assertIndexCharacters(index); - - const { user } = ctx.state.auth; - let pin = await Pin.findByPk(id, { rejectOnEmpty: true }); - - const document = await Document.findByPk(pin.documentId, { - userId: user.id, - }); - - if (pin.collectionId) { - authorize(user, "pin", document); - } else { - authorize(user, "update", pin); - } - - pin = await pinUpdater({ - user, - pin, - ip: ctx.request.ip, - index, - }); - - ctx.body = { - data: presentPin(pin), - policies: presentPolicies(user, [pin]), - }; -}); - -router.post("pins.delete", auth(), async (ctx: APIContext) => { - const { id } = ctx.request.body; - assertUuid(id, "id is required"); - - const { user } = ctx.state.auth; - const pin = await Pin.findByPk(id, { rejectOnEmpty: true }); - - const document = await Document.findByPk(pin.documentId, { - userId: user.id, - }); - - if (pin.collectionId) { - authorize(user, "unpin", document); - } else { - authorize(user, "delete", pin); - } - - await pinDestroyer({ user, pin, ip: ctx.request.ip }); - - ctx.body = { - success: true, - }; -}); - -export default router; diff --git a/server/routes/api/pins/index.ts b/server/routes/api/pins/index.ts new file mode 100644 index 000000000000..09411e0e48d0 --- /dev/null +++ b/server/routes/api/pins/index.ts @@ -0,0 +1 @@ +export { default } from "./pins"; diff --git a/server/routes/api/pins/pins.test.ts b/server/routes/api/pins/pins.test.ts new file mode 100644 index 000000000000..f1936a0d3538 --- /dev/null +++ b/server/routes/api/pins/pins.test.ts @@ -0,0 +1,410 @@ +import { Collection, Document, Pin, User } from "@server/models"; +import { + buildAdmin, + buildCollection, + buildDocument, + buildDraftDocument, + buildPin, + buildUser, +} from "@server/test/factories"; +import { getTestServer } from "@server/test/support"; + +const server = getTestServer(); + +describe("#pins.create", () => { + let admin: User; + let user: User; + let anotherUser: User; + let document: Document; + let collection: Collection; + + beforeEach(async () => { + admin = await buildAdmin(); + [user, anotherUser] = await Promise.all([ + buildUser({ teamId: admin.teamId }), + buildUser(), + ]); + collection = await buildCollection({ + createdById: admin.id, + teamId: admin.teamId, + }); + document = await buildDocument({ + createdById: admin.id, + teamId: admin.teamId, + collectionId: collection.id, + }); + }); + + it("should fail with status 401 unauthorized when user token is missing", async () => { + const res = await server.post("/api/pins.create", { + body: { + documentId: "foo", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(401); + expect(body.message).toEqual("Authentication required"); + }); + + it("should fail with status 400 bad request when documentId is not suppled", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: user.getJwtToken(), + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("documentId: required"); + }); + + it("should fail with status 400 bad request when documentId is invalid", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: user.getJwtToken(), + documentId: "foo", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("documentId: must be uuid or url slug"); + }); + + it("should fail with status 400 bad request when index is invalid", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: user.getJwtToken(), + documentId: "foo1234567", + index: "😀", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("index: must be between x20 to x7E ASCII"); + }); + + it("should fail with status 403 forbidden when user is disallowed to read the document", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: anotherUser.getJwtToken(), + documentId: document.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should fail with status 403 forbidden when user is disallowed to update the collection", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: user.getJwtToken(), + documentId: document.id, + collectionId: collection.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should fail with status 403 forbidden when user is disallowed to pin the document", async () => { + const draft = await buildDraftDocument({ + createdById: admin.id, + teamId: admin.teamId, + collectionId: collection.id, + }); + const res = await server.post("/api/pins.create", { + body: { + token: admin.getJwtToken(), + // A draft document cannot be pinned, neither by a member nor by an admin + documentId: draft.id, + collectionId: collection.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should fail with status 403 forbidden when user is disallowed to pin the document to home page", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: user.getJwtToken(), + documentId: document.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should succeed with status 200 ok when user is allowed to pin", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: admin.getJwtToken(), + documentId: document.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toBeTruthy(); + expect(body.data.documentId).toEqual(document.id); + expect(body.data.collectionId).toBeNull(); + }); + + it("should succeed with status 200 ok when valid collectionId is supplied", async () => { + const res = await server.post("/api/pins.create", { + body: { + token: admin.getJwtToken(), + documentId: document.id, + collectionId: collection.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toBeTruthy(); + expect(body.data.documentId).toEqual(document.id); + expect(body.data.collectionId).toEqual(collection.id); + }); +}); + +describe("#pins.list", () => { + let user: User; + let pins: Pin[]; + let docs: Document[]; + let collection: Collection; + + beforeEach(async () => { + user = await buildUser(); + collection = await buildCollection({ + teamId: user.teamId, + createdById: user.id, + }); + docs = await Promise.all([ + buildDocument({ + teamId: user.teamId, + collectionId: collection.id, + }), + buildDocument({ + teamId: user.teamId, + collectionId: collection.id, + }), + ]); + pins = await Promise.all([ + buildPin({ + createdById: user.id, + documentId: docs[0].id, + teamId: user.teamId, + }), + buildPin({ + createdById: user.id, + documentId: docs[1].id, + teamId: user.teamId, + }), + ]); + }); + + it("should fail with status 401 unauthorized when user token is missing", async () => { + const res = await server.post("/api/pins.list", { + body: {}, + }); + const body = await res.json(); + expect(res.status).toEqual(401); + expect(body.message).toEqual("Authentication required"); + }); + + it("should succeed with status 200 ok returning pinned documents", async () => { + const res = await server.post("/api/pins.list", { + body: { + token: user.getJwtToken(), + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toBeTruthy(); + expect(body.data.pins).toBeTruthy(); + expect(body.data.pins).toHaveLength(2); + const pinIds = body.data.pins.map((p: any) => p.id); + expect(pinIds).toContain(pins[0].id); + expect(pinIds).toContain(pins[1].id); + const docIds = body.data.documents.map((d: any) => d.id); + expect(docIds).toContain(docs[0].id); + expect(docIds).toContain(docs[1].id); + }); + + it("should succeed with status 200 ok returning pinned documents filtered by collectionId supplied", async () => { + const res = await server.post("/api/pins.list", { + body: { + token: user.getJwtToken(), + collectionId: collection.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toBeTruthy(); + expect(body.data.pins).toBeTruthy(); + expect(body.data.pins).toHaveLength(0); + }); +}); + +describe("#pins.update", () => { + let user: User; + let admin: User; + let pin: Pin; + + beforeEach(async () => { + user = await buildUser(); + admin = await buildAdmin(); + const collection = await buildCollection({ + createdById: admin.id, + teamId: admin.teamId, + }); + const doc = await buildDocument({ + createdById: admin.id, + teamId: admin.teamId, + collectionId: collection.id, + }); + pin = await buildPin({ + teamId: admin.teamId, + createdById: admin.id, + documentId: doc.id, + index: "a", + }); + }); + + it("should fail with status 401 unauthorized when user token is missing", async () => { + const res = await server.post("/api/pins.update", { + body: { + id: pin.id, + index: "i", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(401); + expect(body.message).toEqual("Authentication required"); + }); + + it("should fail with status 400 bad request when id is missing", async () => { + const res = await server.post("/api/pins.update", { + body: { + token: admin.getJwtToken(), + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("id: Required"); + }); + + it("should fail with status 400 bad request when index is missing", async () => { + const res = await server.post("/api/pins.update", { + body: { + token: admin.getJwtToken(), + id: pin.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("index: Required"); + }); + + it("should fail with status 400 bad request when an invalid index is sent", async () => { + const res = await server.post("/api/pins.update", { + body: { + token: admin.getJwtToken(), + id: pin.id, + index: "😀", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("index: must be between x20 to x7E ASCII"); + }); + + it("should fail with status 403 forbidden when user is disallowed to update the pin", async () => { + const res = await server.post("/api/pins.update", { + body: { + token: user.getJwtToken(), + id: pin.id, + index: "b", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should succeed with status 200 ok and when user is allowed to update the pin", async () => { + const res = await server.post("/api/pins.update", { + body: { + token: admin.getJwtToken(), + id: pin.id, + index: "b", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data).toBeTruthy(); + expect(body.data.id).toEqual(pin.id); + expect(body.data.index).toEqual("b"); + }); +}); + +describe("#pins.delete", () => { + let admin: User; + let pin: Pin; + + beforeEach(async () => { + admin = await buildAdmin(); + pin = await buildPin({ + teamId: admin.teamId, + createdById: admin.id, + }); + }); + + it("should fail with status 401 unauthorized when user token is missing", async () => { + const res = await server.post("/api/pins.delete", { + body: {}, + }); + const body = await res.json(); + expect(res.status).toEqual(401); + expect(body.message).toEqual("Authentication required"); + }); + + it("should fail with status 400 bad request when id is missing", async () => { + const res = await server.post("/api/pins.delete", { + body: { + token: admin.getJwtToken(), + }, + }); + const body = await res.json(); + expect(res.status).toEqual(400); + expect(body.message).toEqual("id: Required"); + }); + + it("should fail with status 403 forbidden when user is disallowed to delete the pin", async () => { + const user = await buildUser({ + teamId: admin.teamId, + }); + const res = await server.post("/api/pins.delete", { + body: { + token: user.getJwtToken(), + id: pin.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(403); + expect(body.message).toEqual("Authorization error"); + }); + + it("should succeed with status 200 ok when user is allowed to delete the pin", async () => { + const res = await server.post("/api/pins.delete", { + body: { + token: admin.getJwtToken(), + id: pin.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.success).toEqual(true); + }); +}); diff --git a/server/routes/api/pins/pins.ts b/server/routes/api/pins/pins.ts new file mode 100644 index 000000000000..3f34af262b6c --- /dev/null +++ b/server/routes/api/pins/pins.ts @@ -0,0 +1,168 @@ +import Router from "koa-router"; +import { Sequelize, Op } from "sequelize"; +import pinCreator from "@server/commands/pinCreator"; +import pinDestroyer from "@server/commands/pinDestroyer"; +import pinUpdater from "@server/commands/pinUpdater"; +import auth from "@server/middlewares/authentication"; +import validate from "@server/middlewares/validate"; +import { Collection, Document, Pin } from "@server/models"; +import { authorize } from "@server/policies"; +import { + presentPin, + presentDocument, + presentPolicies, +} from "@server/presenters"; +import { APIContext } from "@server/types"; +import pagination from "../middlewares/pagination"; +import * as T from "./schema"; + +const router = new Router(); + +router.post( + "pins.create", + auth(), + validate(T.PinsCreateSchema), + async (ctx: APIContext) => { + const { documentId, collectionId, index } = ctx.input.body; + const { user } = ctx.state.auth; + const document = await Document.findByPk(documentId, { + userId: user.id, + }); + authorize(user, "read", document); + + if (collectionId) { + const collection = await Collection.scope({ + method: ["withMembership", user.id], + }).findByPk(collectionId); + authorize(user, "update", collection); + authorize(user, "pin", document); + } else { + authorize(user, "pinToHome", document); + } + + const pin = await pinCreator({ + user, + documentId, + collectionId, + ip: ctx.request.ip, + index, + }); + + ctx.body = { + data: presentPin(pin), + policies: presentPolicies(user, [pin]), + }; + } +); + +router.post( + "pins.list", + auth(), + validate(T.PinsListSchema), + pagination(), + async (ctx: APIContext) => { + const { collectionId } = ctx.input.body; + const { user } = ctx.state.auth; + + const [pins, collectionIds] = await Promise.all([ + Pin.findAll({ + where: { + ...(collectionId + ? { collectionId } + : { collectionId: { [Op.is]: null } }), + teamId: user.teamId, + }, + order: [ + Sequelize.literal('"pin"."index" collate "C"'), + ["updatedAt", "DESC"], + ], + offset: ctx.state.pagination.offset, + limit: ctx.state.pagination.limit, + }), + user.collectionIds(), + ]); + + const documents = await Document.defaultScopeWithUser(user.id).findAll({ + where: { + id: pins.map((pin) => pin.documentId), + collectionId: collectionIds, + }, + }); + + const policies = presentPolicies(user, [...documents, ...pins]); + + ctx.body = { + pagination: ctx.state.pagination, + data: { + pins: pins.map(presentPin), + documents: await Promise.all( + documents.map((document: Document) => presentDocument(document)) + ), + }, + policies, + }; + } +); + +router.post( + "pins.update", + auth(), + validate(T.PinsUpdateSchema), + async (ctx: APIContext) => { + const { id, index } = ctx.input.body; + const { user } = ctx.state.auth; + let pin = await Pin.findByPk(id, { rejectOnEmpty: true }); + + const document = await Document.findByPk(pin.documentId, { + userId: user.id, + }); + + if (pin.collectionId) { + authorize(user, "pin", document); + } else { + authorize(user, "update", pin); + } + + pin = await pinUpdater({ + user, + pin, + ip: ctx.request.ip, + index, + }); + + ctx.body = { + data: presentPin(pin), + policies: presentPolicies(user, [pin]), + }; + } +); + +router.post( + "pins.delete", + auth(), + validate(T.PinsDeleteSchema), + async (ctx: APIContext) => { + const { id } = ctx.input.body; + + const { user } = ctx.state.auth; + const pin = await Pin.findByPk(id, { rejectOnEmpty: true }); + + const document = await Document.findByPk(pin.documentId, { + userId: user.id, + }); + + if (pin.collectionId) { + authorize(user, "unpin", document); + } else { + authorize(user, "delete", pin); + } + + await pinDestroyer({ user, pin, ip: ctx.request.ip }); + + ctx.body = { + success: true, + }; + } +); + +export default router; diff --git a/server/routes/api/pins/schema.ts b/server/routes/api/pins/schema.ts new file mode 100644 index 000000000000..9f6808773cf2 --- /dev/null +++ b/server/routes/api/pins/schema.ts @@ -0,0 +1,52 @@ +import isUUID from "validator/lib/isUUID"; +import { z } from "zod"; +import { SLUG_URL_REGEX } from "@shared/utils/urlHelpers"; +import BaseSchema from "../BaseSchema"; + +export const PinsCreateSchema = BaseSchema.extend({ + body: z.object({ + documentId: z + .string({ + required_error: "required", + }) + .refine((val) => isUUID(val) || SLUG_URL_REGEX.test(val), { + message: "must be uuid or url slug", + }), + collectionId: z.string().uuid().nullish(), + index: z + .string() + .regex(new RegExp("^[\x20-\x7E]+$"), { + message: "must be between x20 to x7E ASCII", + }) + .optional(), + }), +}); + +export type PinsCreateReq = z.infer; + +export const PinsListSchema = BaseSchema.extend({ + body: z.object({ + collectionId: z.string().uuid().nullish(), + }), +}); + +export type PinsListReq = z.infer; + +export const PinsUpdateSchema = BaseSchema.extend({ + body: z.object({ + id: z.string().uuid(), + index: z.string().regex(new RegExp("^[\x20-\x7E]+$"), { + message: "must be between x20 to x7E ASCII", + }), + }), +}); + +export type PinsUpdateReq = z.infer; + +export const PinsDeleteSchema = BaseSchema.extend({ + body: z.object({ + id: z.string().uuid(), + }), +}); + +export type PinsDeleteReq = z.infer; diff --git a/server/test/factories.ts b/server/test/factories.ts index c3ad2c93a074..5cafc31adbb9 100644 --- a/server/test/factories.ts +++ b/server/test/factories.ts @@ -29,6 +29,7 @@ import { Subscription, Notification, SearchQuery, + Pin, } from "@server/models"; let count = 1; @@ -548,3 +549,26 @@ export async function buildSearchQuery( return SearchQuery.create(overrides); } + +export async function buildPin(overrides: Partial = {}): Promise { + if (!overrides.teamId) { + const team = await buildTeam(); + overrides.teamId = team.id; + } + + if (!overrides.createdById) { + const user = await buildUser({ + teamId: overrides.teamId, + }); + overrides.createdById = user.id; + } + + if (!overrides.documentId) { + const document = await buildDocument({ + teamId: overrides.teamId, + }); + overrides.documentId = document.id; + } + + return Pin.create(overrides); +} From 831318d94137331b382d3dd9710dee62c9ae67e3 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 22 Jun 2023 09:17:25 -0400 Subject: [PATCH 20/61] fix: Invalid LOG_LEVEL in environment results in server crash with no displayed error message Related: https://github.com/outline/outline/discussions/5466 --- server/logging/Logger.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/logging/Logger.ts b/server/logging/Logger.ts index 116029d1031f..0e3a93647e2e 100644 --- a/server/logging/Logger.ts +++ b/server/logging/Logger.ts @@ -30,7 +30,20 @@ class Logger { public constructor() { this.output = winston.createLogger({ - level: env.LOG_LEVEL, + // The check for log level validity is here in addition to the ENV validation + // as entering an incorrect LOG_LEVEL in env could otherwise prevent the + // related error message from being displayed. + level: [ + "error", + "warn", + "info", + "http", + "verbose", + "debug", + "silly", + ].includes(env.LOG_LEVEL) + ? env.LOG_LEVEL + : "info", }); this.output.add( new winston.transports.Console({ From 08601a9f84b24b102b14f21efc1201258e1bf00c Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 24 Jun 2023 13:48:17 -0400 Subject: [PATCH 21/61] fix: Error when importing collections with longer descriptions than 1000 chars related #5472 --- server/queues/tasks/ImportTask.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/server/queues/tasks/ImportTask.ts b/server/queues/tasks/ImportTask.ts index a585981227b7..c92d7446a522 100644 --- a/server/queues/tasks/ImportTask.ts +++ b/server/queues/tasks/ImportTask.ts @@ -308,6 +308,12 @@ export default abstract class ImportTask extends BaseTask { } } + const truncatedDescription = description + ? truncate(description, { + length: CollectionValidation.maxDescriptionLength, + }) + : null; + // check if collection with name exists const response = await Collection.findOrCreate({ where: { @@ -317,11 +323,7 @@ export default abstract class ImportTask extends BaseTask { defaults: { ...options, id: item.id, - description: description - ? truncate(description, { - length: CollectionValidation.maxDescriptionLength, - }) - : null, + description: truncatedDescription, createdById: fileOperation.userId, permission: CollectionPermission.ReadWrite, importId: fileOperation.id, @@ -341,7 +343,7 @@ export default abstract class ImportTask extends BaseTask { { ...options, id: item.id, - description, + description: truncatedDescription, color: item.color, icon: item.icon, sort: item.sort, From 25e8c32b8403042b6813e2a6861f4cd16586ed69 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 24 Jun 2023 13:56:37 -0400 Subject: [PATCH 22/61] fix: allow-storage-access-by-user-activation on embeds closes #5471 --- shared/editor/components/Frame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/editor/components/Frame.tsx b/shared/editor/components/Frame.tsx index e6dcd7a1f9c6..203204d0a6c9 100644 --- a/shared/editor/components/Frame.tsx +++ b/shared/editor/components/Frame.tsx @@ -75,7 +75,7 @@ class Frame extends React.Component {