From d44c16664f041e0959e8a8d4ae8e5ce43ebc6669 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Fri, 24 Mar 2023 10:32:27 +0100 Subject: [PATCH 01/14] Fix migration tool not running queue for previews --- .../node/src/cli/cmds/migration_cmds/drive.ts | 1 + .../php-drive-file/drive-migrator-service.ts | 14 +++++++++++++- .../php-drive-file/php-drive-file-entity.ts | 2 +- .../php-drive-file-version-entity.ts | 2 +- .../platform/services/message-queue/amqp/index.ts | 2 -- .../platform/services/message-queue/factory.ts | 2 +- .../core/platform/services/message-queue/index.ts | 4 ---- .../node/src/services/files/services/preview.ts | 4 ++-- 8 files changed, 19 insertions(+), 12 deletions(-) diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts index e5dba126e3..eb17607c73 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts @@ -18,6 +18,7 @@ const services = [ "tracker", "websocket", "email-pusher", + "files", ]; const command: yargs.CommandModule = { diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts index 2c28b2a133..b5e98da1e0 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts @@ -80,7 +80,13 @@ class DriveMigrator { logger.info(`Migrating company ${company.id}`); const companyAdminOrOwnerId = await this.getCompanyOwnerOrAdminId(company.id, context); + if (!companyAdminOrOwnerId) { + return; + } const workspaceList = await globalResolver.services.workspaces.getAllForCompany(company.id); + if (!workspaceList || workspaceList.length === 0) { + return; + } for (const workspace of workspaceList) { const wsContext = { @@ -252,6 +258,9 @@ class DriveMigrator { context, ); + logger.info( + `Migrating version ${version.id} of item ${item.id}... (downloading then uploading...)`, + ); const file = await this.phpDriveService.migrate( version.file_id, item.workspace_id, @@ -331,6 +340,9 @@ class DriveMigrator { { pagination }, context, ); + if (!companyUsers.getEntities().length && !pagination?.page_token) { + return null; + } pagination = companyUsers.nextPage as Pagination; @@ -341,7 +353,7 @@ class DriveMigrator { if (companyAdminOrOwner) { companyOwnerOrAdminId = companyAdminOrOwner.id; } - } while (pagination && !companyOwnerOrAdminId); + } while (pagination?.page_token && !companyOwnerOrAdminId); return companyOwnerOrAdminId; }; diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-entity.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-entity.ts index 05b3268017..74b873017f 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-entity.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-entity.ts @@ -12,7 +12,7 @@ export const TYPE = "drive_file"; }) export class PhpDriveFile { @Type(() => String) - @Column("workspace_id", "timeuuid") + @Column("workspace_id", "string") workspace_id: string; @Type(() => String) diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-version-entity.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-version-entity.ts index 69ecdd052b..b722fb0502 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-version-entity.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/php-drive-file-version-entity.ts @@ -16,7 +16,7 @@ export class PhpDriveFileVersion { id: string; @Type(() => String) - @Column("file_id", "string") + @Column("file_id", "timeuuid") file_id: string; @Type(() => String) diff --git a/twake/backend/node/src/core/platform/services/message-queue/amqp/index.ts b/twake/backend/node/src/core/platform/services/message-queue/amqp/index.ts index 4c14a20d61..36dccbaddb 100644 --- a/twake/backend/node/src/core/platform/services/message-queue/amqp/index.ts +++ b/twake/backend/node/src/core/platform/services/message-queue/amqp/index.ts @@ -9,7 +9,6 @@ import { } from "../api"; import MessageQueueProxy from "../proxy"; import { AMQPMessageQueueManager } from "./manager"; -import { SkipCLI } from "../../../framework/decorators/skip"; const logger = rootLogger.child({ component: "twake.core.platform.services.message-queue.amqp", @@ -28,7 +27,6 @@ export class AMQPMessageQueueService implements MessageQueueAdapter { this.clientProxy = new MessageQueueProxy(); } - @SkipCLI() async init(): Promise { logger.info("Initializing message-queue service implementation with urls %o", this.urls); await this.manager.createClient(this.urls); diff --git a/twake/backend/node/src/core/platform/services/message-queue/factory.ts b/twake/backend/node/src/core/platform/services/message-queue/factory.ts index fbf39f9a11..5f87fa057e 100644 --- a/twake/backend/node/src/core/platform/services/message-queue/factory.ts +++ b/twake/backend/node/src/core/platform/services/message-queue/factory.ts @@ -17,7 +17,7 @@ export class MessageQueueAdapterFactory { logger.info("Building Adapter %o", type); switch (type) { - case "local": + case "local" || process.env.NODE_ENV === "cli": return new LocalMessageQueueService(); case "amqp": let urls: string[] = configuration.get("amqp.urls", [DEFAULT_AMQP_URL]); diff --git a/twake/backend/node/src/core/platform/services/message-queue/index.ts b/twake/backend/node/src/core/platform/services/message-queue/index.ts index cb5393e31b..d32c60e2cb 100644 --- a/twake/backend/node/src/core/platform/services/message-queue/index.ts +++ b/twake/backend/node/src/core/platform/services/message-queue/index.ts @@ -9,7 +9,6 @@ import { import { eventBus } from "./bus"; import { Processor } from "./processor"; import adapterFactory from "./factory"; -import { SkipCLI } from "../../framework/decorators/skip"; import config from "../../../../core/config"; const logger = rootLogger.child({ @@ -61,7 +60,6 @@ export class MessageQueueService implements MessageQueueServiceAPI { this.processor = new Processor(this); } - @SkipCLI() async init(): Promise { logger.info("Initializing message-queue adapter %o", this.adapter.type); await this.adapter?.init?.(); @@ -69,7 +67,6 @@ export class MessageQueueService implements MessageQueueServiceAPI { return this; } - @SkipCLI() async start(): Promise { logger.info("Starting message-queue adapter %o", this.adapter.type); await this.adapter?.start?.(); @@ -78,7 +75,6 @@ export class MessageQueueService implements MessageQueueServiceAPI { return this; } - @SkipCLI() async stop(): Promise { logger.info("Stopping message-queue adapter %o", this.adapter.type); await this.adapter?.stop?.(); diff --git a/twake/backend/node/src/services/files/services/preview.ts b/twake/backend/node/src/services/files/services/preview.ts index ec808eae1c..39d4ca6f6d 100644 --- a/twake/backend/node/src/services/files/services/preview.ts +++ b/twake/backend/node/src/services/files/services/preview.ts @@ -30,7 +30,7 @@ export class PreviewFinishedProcessor name = "FilePreviewProcessor"; validate(message: PreviewMessageQueueCallback): boolean { - return !!(message && message.document && message.thumbnails); + return !!(message && message.document); } async process(message: PreviewMessageQueueCallback, context?: ExecutionContext): Promise { @@ -46,7 +46,7 @@ export class PreviewFinishedProcessor return; } - entity.thumbnails = message.thumbnails.map((thumb, index) => { + entity.thumbnails = (message.thumbnails || []).map((thumb, index) => { return { index, id: thumb.path.split("/").pop(), From 599f397561a771251dfc7cafb8cecda5ab22b8b3 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Sat, 25 Mar 2023 16:50:43 +0100 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=9B=A0=20Fix=20drive=20migration=20?= =?UTF-8?q?(#2784)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🛠 Fix Drive migration * Move timeout --- .../php-drive-file/drive-migrator-service.ts | 290 +++++++++--------- 1 file changed, 150 insertions(+), 140 deletions(-) diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts index b5e98da1e0..c9f087d79d 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts @@ -65,6 +65,8 @@ class DriveMigrator { company: { id: company.id }, }); } + + console.log("Loop over companies...", page.page_token); } while (page.page_token); }; @@ -159,158 +161,166 @@ class DriveMigrator { logger.info(`Migrating php drive item ${item.id} - parent: ${parentId ?? "root"}`); try { - const migrationRecord = await this.phpDriveService.getMigrationRecord( - item.id, - context.company.id, - ); + await new Promise(async (resolve, reject) => { + const migrationRecord = await this.phpDriveService.getMigrationRecord( + item.id, + context.company.id, + ); - const newDriveItem = getDefaultDriveItem( - { - name: item.name || item.id, - extension: item.extension, - added: item.added.toString(), - content_keywords: - item.content_keywords && item.content_keywords.length - ? item.content_keywords.join(",") - : "", - creator: item.creator || context.user.id, - is_directory: item.isdirectory, - is_in_trash: item.isintrash, - description: item.description, - tags: item.tags || [], - parent_id: parentId, - company_id: context.company.id, - access_info: access, - }, - context, - ); + const newDriveItem = getDefaultDriveItem( + { + name: item.name || item.id, + extension: item.extension, + added: item.added.toString(), + content_keywords: + item.content_keywords && item.content_keywords.length + ? item.content_keywords.join(",") + : "", + creator: item.creator || context.user.id, + is_directory: item.isdirectory, + is_in_trash: item.isintrash, + description: item.description, + tags: item.tags || [], + parent_id: parentId, + company_id: context.company.id, + access_info: access, + }, + context, + ); - if (migrationRecord && migrationRecord.company_id === context.company.id) { - console.debug(`${item.id} is already migrated`); - } else { - await this.nodeRepository.save(newDriveItem); - } + if (migrationRecord && migrationRecord.company_id === context.company.id) { + console.debug(`${item.id} is already migrated`); + } else { + await this.nodeRepository.save(newDriveItem); + } - if (item.isdirectory) { - const newParentId = - migrationRecord && migrationRecord.company_id === context.company.id - ? migrationRecord.new_id - : newDriveItem.id; - - let page: Pagination = { limitStr: "100" }; - - do { - const directoryChildren = await this.phpDriveService.listDirectory( - page, - item.id, - context.workspace_id, - ); - page = directoryChildren.nextPage as Pagination; - - for (const child of directoryChildren.getEntities()) { - try { - await this.migrateDriveFile(child, newParentId, access, context); - } catch (error) { - logger.error(`Failed to migrate drive item ${child.id}`); - console.error(`Failed to migrate drive item ${child.id}`); + if (item.isdirectory) { + const newParentId = + migrationRecord && migrationRecord.company_id === context.company.id + ? migrationRecord.new_id + : newDriveItem.id; + + let page: Pagination = { limitStr: "100" }; + + do { + const directoryChildren = await this.phpDriveService.listDirectory( + page, + item.id, + context.workspace_id, + ); + page = directoryChildren.nextPage as Pagination; + + for (const child of directoryChildren.getEntities()) { + try { + await this.migrateDriveFile(child, newParentId, access, context); + } catch (error) { + logger.error(`Failed to migrate drive item ${child.id}`); + console.error(`Failed to migrate drive item ${child.id}`); + } } + } while (page.page_token); + } else { + let versionPage: Pagination = { limitStr: "100" }; + if ( + migrationRecord && + migrationRecord.item_id === item.id && + migrationRecord.company_id === context.company.id + ) { + logger.info(`item is already migrated - ${item.id} - skipping`); + console.log(`item is already migrated - ${item.id} - skipping`); + return; } - } while (page.page_token); - } else { - let versionPage: Pagination = { limitStr: "100" }; - if ( - migrationRecord && - migrationRecord.item_id === item.id && - migrationRecord.company_id === context.company.id - ) { - logger.info(`item is already migrated - ${item.id} - skipping`); - console.log(`item is already migrated - ${item.id} - skipping`); - return; - } - const mime = mimes[item.extension]; - - let createdVersions = 0; - - do { - const itemVersions = await this.phpDriveService.listItemVersions( - versionPage, - item.id, - context, - ); - versionPage = itemVersions.nextPage as Pagination; - - for (const version of itemVersions.getEntities()) { - try { - const newVersion = getDefaultDriveItemVersion( - { - creator_id: version.creator_id || context.user.id, - data: version.data, - date_added: +version.date_added, - drive_item_id: newDriveItem.id, - file_size: version.file_size, - filename: version.filename, - key: version.key, - provider: version.provider, - realname: version.realname, - mode: version.mode, - }, - context, - ); - - logger.info( - `Migrating version ${version.id} of item ${item.id}... (downloading then uploading...)`, - ); - const file = await this.phpDriveService.migrate( - version.file_id, - item.workspace_id, - version.id, - { - filename: version.filename, - userId: version.creator_id || context.user.id, - totalSize: version.file_size, - waitForThumbnail: true, - chunkNumber: 1, - totalChunks: 1, - type: mime, - }, - context, - ); - - if (!file) { - throw Error("cannot download file version"); + const mime = mimes[item.extension]; + + let createdVersions = 0; + + const timeout = setTimeout(() => reject("Timeout"), 60000); + + do { + const itemVersions = await this.phpDriveService.listItemVersions( + versionPage, + item.id, + context, + ); + versionPage = itemVersions.nextPage as Pagination; + + for (const version of itemVersions.getEntities()) { + try { + const newVersion = getDefaultDriveItemVersion( + { + creator_id: version.creator_id || context.user.id, + data: version.data, + date_added: +version.date_added, + drive_item_id: newDriveItem.id, + file_size: version.file_size, + filename: version.filename, + key: version.key, + provider: version.provider, + realname: version.realname, + mode: version.mode, + }, + context, + ); + + logger.info( + `Migrating version ${version.id} of item ${item.id}... (downloading then uploading...)`, + ); + const file = await this.phpDriveService.migrate( + version.file_id, + item.workspace_id, + version.id, + { + filename: version.filename, + userId: version.creator_id || context.user.id, + totalSize: version.file_size, + waitForThumbnail: true, + chunkNumber: 1, + totalChunks: 1, + type: mime, + }, + context, + ); + + if (!file) { + throw Error("cannot download file version"); + } + + newVersion.file_metadata = { + external_id: file.id, + mime: file.metadata.mime, + name: file.metadata.name || version.filename, + size: file.upload_data.size || version.file_size, + }; + + await globalResolver.services.documents.documents.createVersion( + newDriveItem.id, + newVersion, + context, + ); + + createdVersions++; + } catch (error) { + logger.error(`Failed to migrate version ${version.id} for drive item ${item.id}`); + console.error(`Failed to migrate version ${version.id} for drive item ${item.id}`); } - - newVersion.file_metadata = { - external_id: file.id, - mime: file.metadata.mime, - name: file.metadata.name || version.filename, - size: file.upload_data.size || version.file_size, - }; - - await globalResolver.services.documents.documents.createVersion( - newDriveItem.id, - newVersion, - context, - ); - - createdVersions++; - } catch (error) { - logger.error(`Failed to migrate version ${version.id} for drive item ${item.id}`); - console.error(`Failed to migrate version ${version.id} for drive item ${item.id}`); } + } while (versionPage.page_token); + + clearTimeout(timeout); + + if (createdVersions === 0) { + await this.nodeRepository.remove(newDriveItem); + return; } - } while (versionPage.page_token); + } - if (createdVersions === 0) { - await this.nodeRepository.remove(newDriveItem); - return; + if (!migrationRecord) { + await this.phpDriveService.markAsMigrated(item.id, newDriveItem.id, context.company.id); } - } - if (!migrationRecord) { - await this.phpDriveService.markAsMigrated(item.id, newDriveItem.id, context.company.id); - } + resolve(true); + }); } catch (error) { logger.error( `Failed to migrate Drive item ${item.id} / workspace ${item.workspace_id} / company_id: ${context.company.id}`, From 4f00b568c1566539552cab14f2c56fff9d419df9 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Mon, 27 Mar 2023 22:47:36 +0200 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=93=82Fix=20public=20link=20backend?= =?UTF-8?q?=20(#2786)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix public link backend * Fix return without resolve --- README.md | 2 +- .../migration_cmds/php-drive-file/drive-migrator-service.ts | 2 ++ twake/backend/node/src/services/documents/utils.ts | 4 ++-- twake/frontend/src/app/views/applications/drive/browser.tsx | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8b2af4f28a..092a092794 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Twake offers all the features for collaboration : - Video call and conferencing - Real time document collaboration - + ## Demo diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts index c9f087d79d..8d0c19f076 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts @@ -228,6 +228,7 @@ class DriveMigrator { ) { logger.info(`item is already migrated - ${item.id} - skipping`); console.log(`item is already migrated - ${item.id} - skipping`); + resolve(true); return; } @@ -311,6 +312,7 @@ class DriveMigrator { if (createdVersions === 0) { await this.nodeRepository.remove(newDriveItem); + resolve(true); return; } } diff --git a/twake/backend/node/src/services/documents/utils.ts b/twake/backend/node/src/services/documents/utils.ts index c622606ff1..b9204d3136 100644 --- a/twake/backend/node/src/services/documents/utils.ts +++ b/twake/backend/node/src/services/documents/utils.ts @@ -319,7 +319,7 @@ export const checkAccess = async ( repository: Repository, context: CompanyExecutionContext & { public_token?: string; twake_tab_token?: string }, ): Promise => { - if (context.user.server_request) { + if (context.user?.server_request) { return true; } @@ -674,7 +674,7 @@ export const getFileMetadata = async ( company_id: context.company.id, }, context, - { ...(context.user.server_request ? {} : { waitForThumbnail: true }) }, + { ...(context.user?.server_request ? {} : { waitForThumbnail: true }) }, ); if (!file) { diff --git a/twake/frontend/src/app/views/applications/drive/browser.tsx b/twake/frontend/src/app/views/applications/drive/browser.tsx index 75f03fdc1a..4f413e8dba 100644 --- a/twake/frontend/src/app/views/applications/drive/browser.tsx +++ b/twake/frontend/src/app/views/applications/drive/browser.tsx @@ -86,7 +86,7 @@ export default memo( //In case we are kicked out of the current folder, we need to reset the parent id useEffect(() => { - if (!loading && !path?.length) setParentId('root'); + if (!loading && !path?.length && !inPublicSharing) setParentId('root'); }, [path, loading, setParentId]); useEffect(() => { From 71580abbdc02e6801987ad0f91ff20b0b1c58d15 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Tue, 28 Mar 2023 11:16:20 +0200 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=93=81=20Add=20options=20to=20drive?= =?UTF-8?q?=20mirgation=20script=20(#2787)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add options to drive mirgation script * Fix tests * Fix code --- .../node/src/cli/cmds/migration_cmds/drive.ts | 21 +++++++++++++++---- .../php-drive-file/drive-migrator-service.ts | 21 ++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts index eb17607c73..89ae6a654a 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts @@ -24,13 +24,26 @@ const services = [ const command: yargs.CommandModule = { command: "drive", describe: "migrate php drive items to node", - builder: {}, - handler: async _argv => { - console.log("test"); + builder: { + from: { + default: null, + type: "string", + description: "Start migration from this company ID", + }, + onlyCompany: { + default: null, + type: "string", + description: "Migrate only this company ID", + }, + }, + handler: async argv => { + const from = argv.from as string | null; + const onlyCompany = argv.onlyCompany as string | null; + const spinner = ora({ text: "Migrating php drive - " }).start(); const platform = await twake.run(services); await globalResolver.doInit(platform); - const migrator = new DriveMigrator(platform); + const migrator = new DriveMigrator(platform, { fromCompany: from, onlyCompany }); await migrator.run(); diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts index 8d0c19f076..65a794747a 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts @@ -30,8 +30,19 @@ interface WorkspaceExecutionContext extends CompanyExecutionContext { class DriveMigrator { private phpDriveService: PhpDriveFileService; private nodeRepository: Repository; + private options: { + fromCompany?: string; + onlyCompany?: string; + }; - constructor(readonly _platform: TwakePlatform) { + constructor( + readonly _platform: TwakePlatform, + options?: { + fromCompany?: string; + onlyCompany?: string; + }, + ) { + this.options = options; this.phpDriveService = new PhpDriveFileService(); } @@ -59,7 +70,15 @@ class DriveMigrator { const companyListResult = await globalResolver.services.companies.getCompanies(page); page = companyListResult.nextPage as Pagination; + let didPassFromCompany = false; + for (const company of companyListResult.getEntities()) { + if (this.options.onlyCompany && this.options.onlyCompany !== company.id) continue; + if (this.options.fromCompany && this.options.fromCompany === company.id) { + didPassFromCompany = true; + } + if (this.options.fromCompany && !didPassFromCompany) continue; + await this.migrateCompany(company, { ...context, company: { id: company.id }, From 9907678fc56d549d4c55838ce0228d8ed40c2547 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Fri, 31 Mar 2023 13:56:16 +0200 Subject: [PATCH 05/14] =?UTF-8?q?=F0=9F=9B=A0=20Fix=20drive=20migrator=20(?= =?UTF-8?q?#2788)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix migrator * Fix tests --- .../node/src/cli/cmds/migration_cmds/drive.ts | 28 ++++++++- .../php-drive-file/drive-migrator-service.ts | 58 ++++++++++++++----- .../node/src/services/files/services/index.ts | 40 +++++++------ .../backend/node/src/services/files/types.ts | 1 + 4 files changed, 90 insertions(+), 37 deletions(-) diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts index 89ae6a654a..d72f85e724 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/drive.ts @@ -35,15 +35,39 @@ const command: yargs.CommandModule = { type: "string", description: "Migrate only this company ID", }, + ignoreThumbnails: { + default: false, + type: "boolean", + description: "Ignore thumbnails", + }, + fromItem: { + default: null, + type: "string", + description: "Start migration from this item ID", + }, + fromWorkspace: { + default: null, + type: "string", + description: "Start migration from this workspace ID", + }, }, handler: async argv => { - const from = argv.from as string | null; + const fromCompany = argv.from as string | null; const onlyCompany = argv.onlyCompany as string | null; + const ignoreThumbnails = argv.ignoreThumbnails as boolean; + const fromItem = argv.fromItem as string | null; + const fromWorkspace = argv.fromWorkspace as string | null; const spinner = ora({ text: "Migrating php drive - " }).start(); const platform = await twake.run(services); await globalResolver.doInit(platform); - const migrator = new DriveMigrator(platform, { fromCompany: from, onlyCompany }); + const migrator = new DriveMigrator(platform, { + fromCompany, + onlyCompany, + ignoreThumbnails, + fromItem, + fromWorkspace, + }); await migrator.run(); diff --git a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts index 65a794747a..91104bf0b7 100644 --- a/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts +++ b/twake/backend/node/src/cli/cmds/migration_cmds/php-drive-file/drive-migrator-service.ts @@ -17,6 +17,9 @@ import mimes from "../../../../utils/mime"; import WorkspaceUser from "../../../../services/workspaces/entities/workspace_user"; import CompanyUser from "src/services/user/entities/company_user"; +let didPassFromWorkspace = false; +let didPassFromItem = false; + interface CompanyExecutionContext extends ExecutionContext { company: { id: string; @@ -31,6 +34,9 @@ class DriveMigrator { private phpDriveService: PhpDriveFileService; private nodeRepository: Repository; private options: { + ignoreThumbnails?: boolean; + fromItem?: string; + fromWorkspace?: string; fromCompany?: string; onlyCompany?: string; }; @@ -38,6 +44,9 @@ class DriveMigrator { constructor( readonly _platform: TwakePlatform, options?: { + ignoreThumbnails?: boolean; + fromItem?: string; + fromWorkspace?: string; fromCompany?: string; onlyCompany?: string; }, @@ -66,27 +75,29 @@ class DriveMigrator { }, }; + let didPassFromCompany = false; do { const companyListResult = await globalResolver.services.companies.getCompanies(page); page = companyListResult.nextPage as Pagination; - let didPassFromCompany = false; - - for (const company of companyListResult.getEntities()) { + const companies = companyListResult.getEntities(); + for (let i = 0; i < companies.length; i++) { + const company = companies[i]; if (this.options.onlyCompany && this.options.onlyCompany !== company.id) continue; if (this.options.fromCompany && this.options.fromCompany === company.id) { didPassFromCompany = true; } if (this.options.fromCompany && !didPassFromCompany) continue; + console.log(`Migrating company ${company.id} (next will be ${companies[i + 1]?.id})`); await this.migrateCompany(company, { ...context, company: { id: company.id }, }); } - - console.log("Loop over companies...", page.page_token); } while (page.page_token); + + console.log("Migration done"); }; /** @@ -98,8 +109,6 @@ class DriveMigrator { company: Company, context: CompanyExecutionContext, ): Promise => { - logger.info(`Migrating company ${company.id}`); - const companyAdminOrOwnerId = await this.getCompanyOwnerOrAdminId(company.id, context); if (!companyAdminOrOwnerId) { return; @@ -108,8 +117,13 @@ class DriveMigrator { if (!workspaceList || workspaceList.length === 0) { return; } + for (let i = 0; i < workspaceList.length; i++) { + const workspace = workspaceList[i]; + if (this.options.fromWorkspace && this.options.fromWorkspace === workspace.id) { + didPassFromWorkspace = true; + } + if (this.options.fromWorkspace && !didPassFromWorkspace) continue; - for (const workspace of workspaceList) { const wsContext = { ...context, workspace_id: workspace.id, @@ -117,6 +131,11 @@ class DriveMigrator { }; const access = await this.getWorkspaceAccess(workspace, company, wsContext); + console.log( + `Migrating workspace ${workspace.id} root folder (next will be ${ + workspaceList[i + 1]?.id + })`, + ); await this.migrateWorkspace(workspace, access, wsContext); } }; @@ -133,9 +152,6 @@ class DriveMigrator { ): Promise => { let page: Pagination = { limitStr: "100" }; - console.debug(`Migrating workspace ${workspace.id} of company ${context.company.id}`); - logger.info(`Migrating workspace ${workspace.id} root folder`); - const workspaceFolder = await this.createWorkspaceFolder(workspace, access, context); // Migrate the root folder. do { @@ -147,7 +163,19 @@ class DriveMigrator { ); page = phpDriveFiles.nextPage as Pagination; - for (const phpDriveFile of phpDriveFiles.getEntities()) { + const driveFiles = phpDriveFiles.getEntities(); + for (let i = 0; i < driveFiles.length; i++) { + const phpDriveFile = driveFiles[i]; + if (this.options.fromItem && this.options.fromItem === phpDriveFile.id) { + didPassFromItem = true; + } + if (this.options.fromItem && !didPassFromItem) continue; + + logger.info( + `Migrating php drive item ${phpDriveFile.id} - parent: ${ + workspaceFolder.id ?? "root" + } (next php file will be ${driveFiles[i + 1]?.id})`, + ); await this.migrateDriveFile(phpDriveFile, workspaceFolder.id, access, context); } } while (page.page_token); @@ -177,8 +205,6 @@ class DriveMigrator { access: AccessInformation, context: WorkspaceExecutionContext, ): Promise => { - logger.info(`Migrating php drive item ${item.id} - parent: ${parentId ?? "root"}`); - try { await new Promise(async (resolve, reject) => { const migrationRecord = await this.phpDriveService.getMigrationRecord( @@ -188,7 +214,7 @@ class DriveMigrator { const newDriveItem = getDefaultDriveItem( { - name: item.name || item.id, + name: item.name || item.added.toString()?.split(" ")[0] || "Untitled", extension: item.extension, added: item.added.toString(), content_keywords: @@ -246,7 +272,6 @@ class DriveMigrator { migrationRecord.company_id === context.company.id ) { logger.info(`item is already migrated - ${item.id} - skipping`); - console.log(`item is already migrated - ${item.id} - skipping`); resolve(true); return; } @@ -298,6 +323,7 @@ class DriveMigrator { chunkNumber: 1, totalChunks: 1, type: mime, + ignoreThumbnails: this.options.ignoreThumbnails || false, }, context, ); diff --git a/twake/backend/node/src/services/files/services/index.ts b/twake/backend/node/src/services/files/services/index.ts index 3cb1c35408..90b2d83cff 100644 --- a/twake/backend/node/src/services/files/services/index.ts +++ b/twake/backend/node/src/services/files/services/index.ts @@ -143,29 +143,31 @@ export class FileServiceImpl { entity.metadata.thumbnails_status = "waiting"; await this.repository.save(entity, context); - try { - await gr.platformServices.messageQueue.publish( - "services:preview", - { - data: { document, output }, - }, - ); - - if (options.waitForThumbnail) { - entity = await gr.services.files.getFile( + if (!options?.ignoreThumbnails) { + try { + await gr.platformServices.messageQueue.publish( + "services:preview", { - id: entity.id, - company_id: context.company.id, + data: { document, output }, }, - context, - { waitForThumbnail: true }, ); - } - } catch (err) { - entity.metadata.thumbnails_status = "error"; - await this.repository.save(entity, context); - logger.warn({ err }, "Previewing - Error while sending "); + if (options.waitForThumbnail) { + entity = await gr.services.files.getFile( + { + id: entity.id, + company_id: context.company.id, + }, + context, + { waitForThumbnail: true }, + ); + } + } catch (err) { + entity.metadata.thumbnails_status = "error"; + await this.repository.save(entity, context); + + logger.warn({ err }, "Previewing - Error while sending "); + } } } diff --git a/twake/backend/node/src/services/files/types.ts b/twake/backend/node/src/services/files/types.ts index b9190e1324..bcd35edc71 100644 --- a/twake/backend/node/src/services/files/types.ts +++ b/twake/backend/node/src/services/files/types.ts @@ -5,4 +5,5 @@ export type UploadOptions = { totalChunks: number; chunkNumber: number; waitForThumbnail: boolean; + ignoreThumbnails?: boolean; }; From aeacc54d0079b07c2de536d2b5a1d68fe4805b7d Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Tue, 4 Apr 2023 14:57:36 +0200 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=A9=B9=20Update=20channel-members-m?= =?UTF-8?q?odal.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/channel-members-list/channel-members-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twake/frontend/src/app/components/channel-members-list/channel-members-modal.tsx b/twake/frontend/src/app/components/channel-members-list/channel-members-modal.tsx index 1a524bb948..9f77312c0c 100644 --- a/twake/frontend/src/app/components/channel-members-list/channel-members-modal.tsx +++ b/twake/frontend/src/app/components/channel-members-list/channel-members-modal.tsx @@ -68,7 +68,7 @@ export const ChannelMembersListModal = (): JSX.Element => { /> )} -
+
Date: Wed, 5 Apr 2023 16:11:27 +0200 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=A9=B9=20Patch=203=20(#2790)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix unable to preview migrated files, fix error when switching workspaces * Fix missing events and tasks, remove email invitation calendar * Put back link-files --- .../icons-colored/assets/file-type-link.svg | 11 +++ .../src/app/atoms/icons-colored/index.tsx | 3 + .../CollectionsV1/Collections/Collection.js | 3 +- .../features/drive/hooks/use-drive-preview.ts | 7 +- .../files/api/file-upload-api-client.ts | 4 +- .../app/features/files/utils/type-mimes.ts | 2 + .../global/services/websocket-service.ts | 2 +- .../app/features/viewer/hooks/use-viewer.ts | 3 +- .../calendar/modals/Part/Participants.jsx | 1 - .../drive/documents/document-row.tsx | 3 + .../drive/modals/create/create-link.tsx | 80 +++++++++++++++++++ .../drive/modals/create/index.tsx | 21 ++++- .../app/views/applications/viewer/display.tsx | 10 ++- .../applications/viewer/drive-display.tsx | 3 + .../applications/viewer/link/display.tsx | 71 ++++++++++++++++ 15 files changed, 214 insertions(+), 10 deletions(-) create mode 100644 twake/frontend/src/app/atoms/icons-colored/assets/file-type-link.svg create mode 100644 twake/frontend/src/app/views/applications/drive/modals/create/create-link.tsx create mode 100644 twake/frontend/src/app/views/applications/viewer/link/display.tsx diff --git a/twake/frontend/src/app/atoms/icons-colored/assets/file-type-link.svg b/twake/frontend/src/app/atoms/icons-colored/assets/file-type-link.svg new file mode 100644 index 0000000000..90548bccab --- /dev/null +++ b/twake/frontend/src/app/atoms/icons-colored/assets/file-type-link.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/twake/frontend/src/app/atoms/icons-colored/index.tsx b/twake/frontend/src/app/atoms/icons-colored/index.tsx index f20d231185..bdd6178fd1 100644 --- a/twake/frontend/src/app/atoms/icons-colored/index.tsx +++ b/twake/frontend/src/app/atoms/icons-colored/index.tsx @@ -9,6 +9,7 @@ import { ReactComponent as FileTypeSpreadsheetSvg } from './assets/file-type-spr import { ReactComponent as FileTypeUnknownSvg } from './assets/file-type-unknown.svg'; import { ReactComponent as FileTypeMediaSvg } from './assets/file-type-media.svg'; import { ReactComponent as FileTypeSlidesSvg } from './assets/file-type-slides.svg'; +import { ReactComponent as FileTypeLinkSvg } from './assets/file-type-link.svg'; import { ReactComponent as RemoveSvg } from './assets/remove.svg'; import { ReactComponent as SentSvg } from './assets/sent.svg'; @@ -34,6 +35,8 @@ export const FileTypeSlidesIcon = (props: ComponentProps<'svg'>) => ( ); +export const FileTypeLinkIcon = (props: ComponentProps<'svg'>) => ; + export const RemoveIcon = (props: ComponentProps<'svg'>) => ; export const SentIcon = (props: ComponentProps<'svg'>) => ; diff --git a/twake/frontend/src/app/deprecated/CollectionsV1/Collections/Collection.js b/twake/frontend/src/app/deprecated/CollectionsV1/Collections/Collection.js index 6a09bd434b..e8d334dae9 100755 --- a/twake/frontend/src/app/deprecated/CollectionsV1/Collections/Collection.js +++ b/twake/frontend/src/app/deprecated/CollectionsV1/Collections/Collection.js @@ -153,7 +153,8 @@ export default class Collection extends Observable { if ( routes.length === 0 || (options.http_options || {})._http_force_load || - !waiting_one_route + !waiting_one_route || + true //Keep it working after making it highly deprecated ) { initHttp(); } diff --git a/twake/frontend/src/app/features/drive/hooks/use-drive-preview.ts b/twake/frontend/src/app/features/drive/hooks/use-drive-preview.ts index bf7d3ff75e..cd6a691582 100644 --- a/twake/frontend/src/app/features/drive/hooks/use-drive-preview.ts +++ b/twake/frontend/src/app/features/drive/hooks/use-drive-preview.ts @@ -11,7 +11,10 @@ export const useDrivePreviewModal = () => { const [status, setStatus] = useRecoilState(DriveViewerState); const open: (item: DriveItem) => void = (item: DriveItem) => { - if (item.last_version_cache?.file_metadata?.source === 'internal') { + if ( + !item.last_version_cache?.file_metadata?.source || + item.last_version_cache?.file_metadata?.source === 'internal' + ) { setStatus({ item, loading: true }); } }; @@ -79,5 +82,5 @@ export const useDrivePreviewDisplayData = () => { fileId: status.details?.item.last_version_cache.file_metadata.external_id || '', }); - return { download, id, name, type, extension }; + return { download, id, name, type, extension, size: status.details?.item.size }; }; diff --git a/twake/frontend/src/app/features/files/api/file-upload-api-client.ts b/twake/frontend/src/app/features/files/api/file-upload-api-client.ts index 5fcb7d1205..f2e69b1e9f 100644 --- a/twake/frontend/src/app/features/files/api/file-upload-api-client.ts +++ b/twake/frontend/src/app/features/files/api/file-upload-api-client.ts @@ -105,7 +105,8 @@ class FileUploadAPIClient { } public mimeToType(mime: string, extension?: string): FileTypes { - const { archives, images, pdf, slides, audio, spreadsheets, videos, documents } = fileTypeMimes; + const { archives, images, pdf, slides, audio, spreadsheets, videos, documents, links } = + fileTypeMimes; if (images.includes(mime)) return 'image'; if (videos.includes(mime)) return 'video'; @@ -115,6 +116,7 @@ class FileUploadAPIClient { if (archives.includes(mime)) return 'archive'; if (spreadsheets.includes(mime)) return 'spreadsheet'; if (documents.includes(mime)) return 'document'; + if (links.includes(mime)) return 'link'; if (extension && AceModeList.getMode(extension) !== 'text') { return 'code'; diff --git a/twake/frontend/src/app/features/files/utils/type-mimes.ts b/twake/frontend/src/app/features/files/utils/type-mimes.ts index 42a5fb1a4c..0a18dd456c 100644 --- a/twake/frontend/src/app/features/files/utils/type-mimes.ts +++ b/twake/frontend/src/app/features/files/utils/type-mimes.ts @@ -151,6 +151,7 @@ export const fileTypeMimes: FileTypeMimes = { 'text/csv', 'application/vnd.ms-excel.sheet.macroEnabled.12', ], + links: ['text/uri-list'], }; export type FileTypeMimes = { @@ -162,4 +163,5 @@ export type FileTypeMimes = { archives: string[]; pdf: string[]; documents: string[]; + links: string[]; }; diff --git a/twake/frontend/src/app/features/global/services/websocket-service.ts b/twake/frontend/src/app/features/global/services/websocket-service.ts index 02547a0d41..b39a2bb1fc 100644 --- a/twake/frontend/src/app/features/global/services/websocket-service.ts +++ b/twake/frontend/src/app/features/global/services/websocket-service.ts @@ -207,7 +207,7 @@ class WebSocketService extends EventEmitter { * @param tag */ public leave(path: string, tag: string) { - const name = path.replace(/\/$/, ''); + const name = path ? path.replace(/\/$/, '') : ''; this.wsListeners[name] = this.wsListeners[name] || {}; delete this.wsListeners[name][tag]; diff --git a/twake/frontend/src/app/features/viewer/hooks/use-viewer.ts b/twake/frontend/src/app/features/viewer/hooks/use-viewer.ts index f43d6782c4..91e9fbb19b 100644 --- a/twake/frontend/src/app/features/viewer/hooks/use-viewer.ts +++ b/twake/frontend/src/app/features/viewer/hooks/use-viewer.ts @@ -24,7 +24,8 @@ export const useFileViewerModal = () => { return { open: (file: MessageFileType) => { - if (file.metadata?.source === 'internal') setStatus({ file, loading: true }); + if (!file.metadata?.source || file.metadata?.source === 'internal') + setStatus({ file, loading: true }); }, close: () => setStatus({ file: null, loading: true }), isOpen: !!status?.file, diff --git a/twake/frontend/src/app/views/applications/calendar/modals/Part/Participants.jsx b/twake/frontend/src/app/views/applications/calendar/modals/Part/Participants.jsx index a9e4d5b59f..0121aa495a 100755 --- a/twake/frontend/src/app/views/applications/calendar/modals/Part/Participants.jsx +++ b/twake/frontend/src/app/views/applications/calendar/modals/Part/Participants.jsx @@ -26,7 +26,6 @@ export default class Participants extends Component { return { id: participant.user_id_or_mail }; })} scope="workspace" - allowMails onUpdate={ids_mails => { this.props.onChange && this.props.onChange( diff --git a/twake/frontend/src/app/views/applications/drive/documents/document-row.tsx b/twake/frontend/src/app/views/applications/drive/documents/document-row.tsx index e721330d62..4f95b1f1f0 100644 --- a/twake/frontend/src/app/views/applications/drive/documents/document-row.tsx +++ b/twake/frontend/src/app/views/applications/drive/documents/document-row.tsx @@ -3,6 +3,7 @@ import { Button } from 'app/atoms/button/button'; import { FileTypeArchiveIcon, FileTypeDocumentIcon, + FileTypeLinkIcon, FileTypeMediaIcon, FileTypePdfIcon, FileTypeSlidesIcon, @@ -89,6 +90,8 @@ export const DocumentRow = ({ ) : fileType === 'slides' ? ( + ) : fileType === 'link' ? ( + ) : ( )} diff --git a/twake/frontend/src/app/views/applications/drive/modals/create/create-link.tsx b/twake/frontend/src/app/views/applications/drive/modals/create/create-link.tsx new file mode 100644 index 0000000000..ae89905990 --- /dev/null +++ b/twake/frontend/src/app/views/applications/drive/modals/create/create-link.tsx @@ -0,0 +1,80 @@ +import { Button } from 'app/atoms/button/button'; +import { Input } from 'app/atoms/input/input-text'; +import { Info } from 'app/atoms/text'; +import { useDriveActions } from 'app/features/drive/hooks/use-drive-actions'; +import { useState } from 'react'; +import { useRecoilState } from 'recoil'; +import { CreateModalAtom } from '.'; +import FileUploadService from 'features/files/services/file-upload-service'; + +export const CreateLink = () => { + const [name, setName] = useState(''); + const [link, setLink] = useState(''); + const [loading] = useState(false); + const [state, setState] = useRecoilState(CreateModalAtom); + const { create } = useDriveActions(); + + const createLink = async () => { + let finalLink = link.trim(); + if (!/^https?:\/\//i.test(finalLink)) finalLink = 'http://' + finalLink; + const file = new File(['[InternetShortcut]\nURL=' + finalLink], name?.trim() + '.url', { + type: 'text/uri-list', + }); + + await FileUploadService.upload([file], { + context: { + parentId: state.parent_id, + }, + callback: (file, context) => { + if (file) + create( + { name, parent_id: context.parentId, size: file.upload_data?.size }, + { + provider: 'internal', + application_id: '', + file_metadata: { + name: file.metadata?.name, + size: file.upload_data?.size, + mime: file.metadata?.mime, + thumbnails: file?.thumbnails, + source: 'internal', + external_id: file.id, + }, + }, + ); + }, + }); + }; + + return ( + <> + Create a link + + setName(e.target.value)} + /> + + setLink(e.target.value)} + /> + + + + ); +}; diff --git a/twake/frontend/src/app/views/applications/drive/modals/create/index.tsx b/twake/frontend/src/app/views/applications/drive/modals/create/index.tsx index a68a9d038a..5053ee419f 100644 --- a/twake/frontend/src/app/views/applications/drive/modals/create/index.tsx +++ b/twake/frontend/src/app/views/applications/drive/modals/create/index.tsx @@ -1,17 +1,17 @@ import { Transition } from '@headlessui/react'; import { ChevronLeftIcon, DesktopComputerIcon } from '@heroicons/react/outline'; -import { FolderIcon } from '@heroicons/react/solid'; +import { FolderIcon, LinkIcon } from '@heroicons/react/solid'; import Avatar from 'app/atoms/avatar'; import A from 'app/atoms/link'; import { Modal, ModalContent } from 'app/atoms/modal'; import { Base } from 'app/atoms/text'; -import { useApplications } from 'app/features/applications/hooks/use-applications'; import { useCompanyApplications } from 'app/features/applications/hooks/use-company-applications'; import { Application } from 'app/features/applications/types/application'; import { ReactNode } from 'react'; import { atom, useRecoilState } from 'recoil'; import { slideXTransition, slideXTransitionReverted } from 'src/utils/transitions'; import { CreateFolder } from './create-folder'; +import { CreateLink } from './create-link'; export type CreateModalAtomType = { open: boolean; @@ -81,6 +81,11 @@ export const CreateModal = ({ text="Upload document from device" onClick={() => selectFromDevice()} /> + } + text="Create a link file" + onClick={() => setState({ ...state, type: 'link' })} + /> {(applications || []) .filter(app => app.display?.twake?.files?.editor?.empty_files?.length) @@ -136,6 +141,18 @@ export const CreateModal = ({ > + + + +
diff --git a/twake/frontend/src/app/views/applications/viewer/display.tsx b/twake/frontend/src/app/views/applications/viewer/display.tsx index f31ad90019..236b5dd43e 100644 --- a/twake/frontend/src/app/views/applications/viewer/display.tsx +++ b/twake/frontend/src/app/views/applications/viewer/display.tsx @@ -20,7 +20,7 @@ export default () => { } if (type === 'image') { - return ; + return ; } if (type === 'video' || type === 'audio') { @@ -39,6 +39,14 @@ export default () => { return ; } + if (type === 'link') { + return ( +
+ Opening link... +
+ ); + } + // /* Uncomment after https://github.com/linagora/Twake/issues/2453 is done if (type) { return ; diff --git a/twake/frontend/src/app/views/applications/viewer/drive-display.tsx b/twake/frontend/src/app/views/applications/viewer/drive-display.tsx index 58277012cf..ea4698058c 100644 --- a/twake/frontend/src/app/views/applications/viewer/drive-display.tsx +++ b/twake/frontend/src/app/views/applications/viewer/drive-display.tsx @@ -9,6 +9,7 @@ import PdfDisplay from './pdf/display'; import CodeDisplay from './code/display'; import ArchiveDisplay from './archive/display'; import OtherDisplay from './other/display'; +import LinkDisplay from './link/display'; export default (): React.ReactElement => { const { download, type, name, id } = useDrivePreviewDisplayData(); @@ -39,6 +40,8 @@ export default (): React.ReactElement => { return ; case 'pdf': return ; + case 'link': + return ; default: return ; } diff --git a/twake/frontend/src/app/views/applications/viewer/link/display.tsx b/twake/frontend/src/app/views/applications/viewer/link/display.tsx new file mode 100644 index 0000000000..16be26bf09 --- /dev/null +++ b/twake/frontend/src/app/views/applications/viewer/link/display.tsx @@ -0,0 +1,71 @@ +import { Button } from 'app/atoms/button/button'; +import { useDrivePreviewDisplayData } from 'app/features/drive/hooks/use-drive-preview'; +import { useEffect, useState } from 'react'; + +export default (props: { download: string; name: string }) => { + const { size } = useDrivePreviewDisplayData(); + const [error, setError] = useState(false); + const [loading, setLoading] = useState(true); + + const openLink = async () => { + if ((size || 10000000) < 10000) { + setLoading(true); + //Download file content and extract link from url props.download + try { + const response = await fetch(props.download); + const blob = await response.blob(); + const reader = new FileReader(); + reader.readAsText(blob); + reader.onloadend = () => { + const result = reader.result as string; + const link = result.match(/URL=(.*)/); + if (link && link[1]) { + if (!link[1].match(/^(http|https):\/\//)) throw new Error('Invalid link'); + window.open(link[1], '_blank'); + } else { + setError(true); + } + setLoading(false); + }; + } catch (e) { + setError(true); + setLoading(false); + } + return; + } + setError(true); + }; + + useEffect(() => { + openLink(); + }, []); + + if (loading) { + return ( +
+ Opening link... +
+ ); + } + + if (error) { + return ( +
+ + We can't open '{props.name}' as a link. You can download it instead. + +
+ ); + } + + return ( +
+ + Link was open on another tab. +
+
+ +
+
+ ); +}; From 0770da3b184b5d5e71fee8251a5847a04c7cb9bc Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Wed, 5 Apr 2023 20:11:30 +0200 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=A9=B9=20Patch=204=20-=20Fix=20gues?= =?UTF-8?q?t=20invitation=20with=20new=20console=20documentation=20(#2791)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/services/console/clients/remote.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/twake/backend/node/src/services/console/clients/remote.ts b/twake/backend/node/src/services/console/clients/remote.ts index 14e8ab06da..ec0ffbf73e 100644 --- a/twake/backend/node/src/services/console/clients/remote.ts +++ b/twake/backend/node/src/services/console/clients/remote.ts @@ -57,15 +57,28 @@ export class ConsoleRemoteClient implements ConsoleServiceClient { if (user.skipInvite && user.name && user.email && user.password) { return this.client - .post(`/api/companies/${company.code}/users`, user, { - auth: this.auth(), - headers: { - "Content-Type": "application/json", + .post( + `/api/companies/${company.code}/users`, + { + ...user, + ...(isNewConsole + ? { + name: user.lastName ? user.firstName : user.name, + surname: user.lastName, + applicationCodes: ["twake"], + } + : {}), }, - params: { - skipInvite: user.skipInvite, + { + auth: this.auth(), + headers: { + "Content-Type": "application/json", + }, + params: { + skipInvite: user.skipInvite, + }, }, - }) + ) .then(({ data }) => data); } else { const invitationData = { From f827ae4e800941f4a292e05952f953d03531a916 Mon Sep 17 00:00:00 2001 From: Romaric Mourgues Date: Mon, 15 May 2023 13:57:57 +0200 Subject: [PATCH 09/14] =?UTF-8?q?=F0=9F=9B=A0=20Fix=20drive=20public=20lin?= =?UTF-8?q?ks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/views/applications/drive/context-menu.tsx | 4 ++-- twake/frontend/src/app/views/applications/drive/index.tsx | 4 ++++ twake/frontend/src/app/views/applications/drive/shared.tsx | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/twake/frontend/src/app/views/applications/drive/context-menu.tsx b/twake/frontend/src/app/views/applications/drive/context-menu.tsx index ab09dc29ad..3a5565fabd 100644 --- a/twake/frontend/src/app/views/applications/drive/context-menu.tsx +++ b/twake/frontend/src/app/views/applications/drive/context-menu.tsx @@ -80,7 +80,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s text: 'Copy public link', hide: !item.access_info.public?.level || item.access_info.public?.level === 'none', onClick: () => { - copyToClipboard(getPublicLink(item)); + copyToClipboard(getPublicLink(item || parent?.item)); ToasterService.success('Public link copied to clipboard'); }, }, @@ -244,7 +244,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s !parent?.item?.access_info?.public?.level || parent?.item?.access_info?.public?.level === 'none', onClick: () => { - copyToClipboard(getPublicLink(item)); + copyToClipboard(getPublicLink(item || parent?.item)); ToasterService.success('Public link copied to clipboard'); }, }, diff --git a/twake/frontend/src/app/views/applications/drive/index.tsx b/twake/frontend/src/app/views/applications/drive/index.tsx index 2855146e57..c594930c8e 100644 --- a/twake/frontend/src/app/views/applications/drive/index.tsx +++ b/twake/frontend/src/app/views/applications/drive/index.tsx @@ -1,6 +1,7 @@ import Browser from './browser'; import { SelectorModal } from './modals/selector'; import TwakeTabConfiguration from './twake-tab-configuration'; +import { useCompanyApplications } from 'app/features/applications/hooks/use-company-applications'; export type EmbedContext = { companyId?: string; @@ -18,6 +19,9 @@ export default ({ context?: EmbedContext; inPublicSharing?: boolean; }) => { + //Preload applications for shared view + useCompanyApplications(); + return ( <> diff --git a/twake/frontend/src/app/views/applications/drive/shared.tsx b/twake/frontend/src/app/views/applications/drive/shared.tsx index 22760bd411..3a0f60d1e5 100755 --- a/twake/frontend/src/app/views/applications/drive/shared.tsx +++ b/twake/frontend/src/app/views/applications/drive/shared.tsx @@ -42,7 +42,7 @@ export default () => { } return ( -
+
{group.logo && ( @@ -65,7 +65,7 @@ export default () => {
-
+
From e9f6f95770b82113a0b7d92548ac5b71bc4ca608 Mon Sep 17 00:00:00 2001 From: Khaled FERJANI Date: Thu, 7 Mar 2024 09:51:42 +0100 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Add?= =?UTF-8?q?=20debug=20helper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- twake/backend/node/src/services/channels/web/routes.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/twake/backend/node/src/services/channels/web/routes.ts b/twake/backend/node/src/services/channels/web/routes.ts index 3e40beb75c..0957a24585 100644 --- a/twake/backend/node/src/services/channels/web/routes.ts +++ b/twake/backend/node/src/services/channels/web/routes.ts @@ -69,6 +69,12 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next) handler: channelsController.list.bind(channelsController), }); + fastify.route({ + method: "GET", + url: `${channelsUrl}debug`, + handler: channelsController.list.bind(channelsController), + }); + fastify.route({ method: "GET", url: `${channelsUrl}/:id`, From dc10ba2a0e815e62e39ad805f126f638063a72fb Mon Sep 17 00:00:00 2001 From: Khaled FERJANI Date: Thu, 7 Mar 2024 14:20:37 +0100 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=90=9B=20Debug:=20added=20channels?= =?UTF-8?q?=20list=20debug=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channels/web/controllers/channel.ts | 51 +++++++++++++++++++ .../node/src/services/channels/web/routes.ts | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/twake/backend/node/src/services/channels/web/controllers/channel.ts b/twake/backend/node/src/services/channels/web/controllers/channel.ts index 43091f79e1..b80f66ac64 100644 --- a/twake/backend/node/src/services/channels/web/controllers/channel.ts +++ b/twake/backend/node/src/services/channels/web/controllers/channel.ts @@ -386,6 +386,57 @@ export class ChannelCrudController }; } + async listDebug( + request: FastifyRequest<{ + Querystring: ChannelListQueryParameters; + Params: BaseChannelsParameters; + }>, + ): Promise> { + const context = getExecutionContext(request); + // TODO: remove when debug is done. + context.user.server_request = true; + context.user.id = "12d4bb30-7274-11ea-a178-0242ac120004"; + context.user.email = "kferjani@linagora.com"; + context.user.identity_provider_id = "5ffd789d5010fa00196f360d"; + + const list = await gr.services.channels.channels.list( + new Pagination(request.query.page_token, request.query.limit), + { ...request.query }, + context, + ); + + let entities = []; + if (request.query.include_users) { + entities = []; + for (const e of list.getEntities()) { + entities.push( + await gr.services.channels.channels.includeUsersInDirectChannel(e, context.user.id), + ); + } + } else { + entities = list.getEntities(); + } + + const resources = entities.map(a => ChannelObject.mapTo(a)); + + await gr.services.channels.channels.completeWithStatistics(resources); + + return { + ...{ + resources: resources, + }, + ...(request.query.websockets && { + websockets: gr.platformServices.realtime.sign( + getWorkspaceRooms(request.params, request.currentUser), + request.currentUser.id, + ), + }), + ...(list.page_token && { + next_page_token: list.page_token, + }), + }; + } + async delete( request: FastifyRequest<{ Params: ChannelParameters }>, reply: FastifyReply, diff --git a/twake/backend/node/src/services/channels/web/routes.ts b/twake/backend/node/src/services/channels/web/routes.ts index 0957a24585..0c45972e6f 100644 --- a/twake/backend/node/src/services/channels/web/routes.ts +++ b/twake/backend/node/src/services/channels/web/routes.ts @@ -72,7 +72,7 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next) fastify.route({ method: "GET", url: `${channelsUrl}debug`, - handler: channelsController.list.bind(channelsController), + handler: channelsController.listDebug.bind(channelsController), }); fastify.route({ From 753bdbef5b41ea5281077aecb7480e541fd1fd2c Mon Sep 17 00:00:00 2001 From: Khaled FERJANI Date: Thu, 7 Mar 2024 15:50:38 +0100 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20added=20debug=20con?= =?UTF-8?q?text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/services/channels/web/controllers/channel.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/twake/backend/node/src/services/channels/web/controllers/channel.ts b/twake/backend/node/src/services/channels/web/controllers/channel.ts index b80f66ac64..f38e839404 100644 --- a/twake/backend/node/src/services/channels/web/controllers/channel.ts +++ b/twake/backend/node/src/services/channels/web/controllers/channel.ts @@ -394,10 +394,12 @@ export class ChannelCrudController ): Promise> { const context = getExecutionContext(request); // TODO: remove when debug is done. - context.user.server_request = true; - context.user.id = "12d4bb30-7274-11ea-a178-0242ac120004"; - context.user.email = "kferjani@linagora.com"; - context.user.identity_provider_id = "5ffd789d5010fa00196f360d"; + context.user = { + server_request: true, + id: "12d4bb30-7274-11ea-a178-0242ac120004", + email: "kferjani@linagora.com", + identity_provider_id: "5ffd789d5010fa00196f360d", + }; const list = await gr.services.channels.channels.list( new Pagination(request.query.page_token, request.query.limit), From 53be17a3c2b6057a838424182d929c7b7cb5f1ab Mon Sep 17 00:00:00 2001 From: Khaled FERJANI Date: Mon, 11 Mar 2024 11:47:05 +0100 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20chore:=20removed?= =?UTF-8?q?=20debug=20helper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channels/web/controllers/channel.ts | 53 ------------------- .../node/src/services/channels/web/routes.ts | 6 --- 2 files changed, 59 deletions(-) diff --git a/twake/backend/node/src/services/channels/web/controllers/channel.ts b/twake/backend/node/src/services/channels/web/controllers/channel.ts index f38e839404..43091f79e1 100644 --- a/twake/backend/node/src/services/channels/web/controllers/channel.ts +++ b/twake/backend/node/src/services/channels/web/controllers/channel.ts @@ -386,59 +386,6 @@ export class ChannelCrudController }; } - async listDebug( - request: FastifyRequest<{ - Querystring: ChannelListQueryParameters; - Params: BaseChannelsParameters; - }>, - ): Promise> { - const context = getExecutionContext(request); - // TODO: remove when debug is done. - context.user = { - server_request: true, - id: "12d4bb30-7274-11ea-a178-0242ac120004", - email: "kferjani@linagora.com", - identity_provider_id: "5ffd789d5010fa00196f360d", - }; - - const list = await gr.services.channels.channels.list( - new Pagination(request.query.page_token, request.query.limit), - { ...request.query }, - context, - ); - - let entities = []; - if (request.query.include_users) { - entities = []; - for (const e of list.getEntities()) { - entities.push( - await gr.services.channels.channels.includeUsersInDirectChannel(e, context.user.id), - ); - } - } else { - entities = list.getEntities(); - } - - const resources = entities.map(a => ChannelObject.mapTo(a)); - - await gr.services.channels.channels.completeWithStatistics(resources); - - return { - ...{ - resources: resources, - }, - ...(request.query.websockets && { - websockets: gr.platformServices.realtime.sign( - getWorkspaceRooms(request.params, request.currentUser), - request.currentUser.id, - ), - }), - ...(list.page_token && { - next_page_token: list.page_token, - }), - }; - } - async delete( request: FastifyRequest<{ Params: ChannelParameters }>, reply: FastifyReply, diff --git a/twake/backend/node/src/services/channels/web/routes.ts b/twake/backend/node/src/services/channels/web/routes.ts index 0c45972e6f..3e40beb75c 100644 --- a/twake/backend/node/src/services/channels/web/routes.ts +++ b/twake/backend/node/src/services/channels/web/routes.ts @@ -69,12 +69,6 @@ const routes: FastifyPluginCallback = (fastify: FastifyInstance, options, next) handler: channelsController.list.bind(channelsController), }); - fastify.route({ - method: "GET", - url: `${channelsUrl}debug`, - handler: channelsController.listDebug.bind(channelsController), - }); - fastify.route({ method: "GET", url: `${channelsUrl}/:id`, From 39f533e5e838ef5c7b89168ee6349585a9ec950c Mon Sep 17 00:00:00 2001 From: artembru <146178981+artembru@users.noreply.github.com> Date: Fri, 5 Jul 2024 14:59:20 +0200 Subject: [PATCH 14/14] Update README.md (#2826) Updated README to make it clear that this repository is no longer supported --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 092a092794..f54648f48e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,23 @@ -![](https://twake.app/medias/Twake-long.png) +![Frame 1321317563](https://github.com/linagora/Twake/assets/146178981/0010bde1-4f60-4eaf-841f-0abf542c7d11) -# Twake - The Open Digital Workplace +## :warning: Repository Deprecated + +This repository, Twake, is no longer actively maintained and has been officially deprecated. + + :arrow_right: For current and future updates, please visit our new repository: +🔗 Twake-workplace + +We encourage all users to migrate to Twake-workplace for the latest features and ongoing support. Thank you for your continued support and contribution to the Twake community! + + +--- +*** +___ + +## Archive README + + +Twake - The Open Digital Workplace ![update-saas-backend](https://github.com/TwakeApp/Twake/workflows/update-saas-backend/badge.svg?branch=main&style=flat) ![update-saas-frontend](https://github.com/TwakeApp/Twake/workflows/update-saas-frontend/badge.svg?branch=main&style=flat)