Merge branch 'release/rc-v1.2.0' into bugfix/images-static-versioned

This commit is contained in:
Alexey Safronov 2023-01-27 15:37:59 +03:00
commit 676ecb379c
70 changed files with 639 additions and 476 deletions

View File

@ -132,7 +132,6 @@ public class NotifyHelper
Actions.RestoreCompletedV115,
new IRecipient[] { user },
new[] { StudioNotifyService.EMailSenderName },
null,
TagValues.GreenButton(greenButtonText, confirmationUrl));
}
}

View File

@ -186,9 +186,8 @@ public class RestoreProgressItem : BaseBackupProgressItem
}
_tenantManager.SaveTenant(restoredTenant);
_tenantManager.SetCurrentTenant(restoredTenant);
// sleep until tenants cache expires
Thread.Sleep(TimeSpan.FromMinutes(2));
_tenantManager.SetCurrentTenant(restoredTenant);
TenantId = restoredTenant.Id;
_notifyHelper.SendAboutRestoreCompleted(restoredTenant, Notify);
}
@ -199,13 +198,14 @@ public class RestoreProgressItem : BaseBackupProgressItem
File.Delete(tempFile);
Percentage = 100;
PublishChanges();
Percentage = 100;
Status = DistributedTaskStatus.Completed;
}
catch (Exception error)
{
_logger.ErrorRestoreProgressItem(error);
Exception = error;
Exception = error;
Status = DistributedTaskStatus.Failted;
if (tenant != null)
{

View File

@ -57,7 +57,7 @@ public class TenantsModuleSpecifics : ModuleSpecificsBase
{
new RelationInfo("tenants_tenants", "id", "tenants_quota", "tenant"),
new RelationInfo("tenants_tenants", "id", "tenants_tariff", "tenant"),
new RelationInfo("tenants_tenants", "id", "tenants_tariff", "tariff", x => Convert.ToInt32(x["tariff"]) > 0),
new RelationInfo("tenants_tenants", "id", "tenants_tariff", "tariff"),
new RelationInfo("core_user", "id", "tenants_tenants", "owner_id", null, null, RelationImportance.Low)
};

View File

@ -197,6 +197,7 @@ public class BackupController : ControllerBase
/// <category>Backup</category>
/// <returns>Restore Progress</returns>
[HttpGet("getrestoreprogress")] //NOTE: this method doesn't check payment!!!
[AllowAnonymous]
[AllowNotPayment]
public BackupProgress GetRestoreProgress()
{

View File

@ -43,7 +43,7 @@ global using ASC.Web.Api.Routing;
global using ASC.Web.Studio.Core.Backup;
global using ASC.Web.Studio.Core.Notify;
global using ASC.Web.Studio.Utility;
global using Microsoft.AspNetCore.Authorization;
global using Autofac;
global using Microsoft.AspNetCore.Http.Features;

View File

@ -107,7 +107,8 @@
"web": {
"api": "api/2.0",
"alias": {
"min": ""
"min": 3,
"max": 50
},
"images": "images",
"hide-settings": "Monitoring,LdapSettings,DocService,MailService,PublicPortal,ProxyHttpContent,SpamSubscription,FullTextSearch",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "Bu bölmə sizə istifadəçiləri portala təhlükəsiz və rahat giriş imkanı ilə təmin etmək imkanı verir",
"PortalNameEmpty": "Hesab adı boşdur",
"PortalNameIncorrect": "Yanlış hesab adı",
"PortalNameLength": "Hesab adı ən az 3, ən çox 50 işarədən ibarət ola bilər",
"PortalNameLength": "Hesab adı ən az {{minLength}}, ən çox {{maxLength}} işarədən ibarət ola bilər",
"PortalRenaming": "Portalın Adının dəyişdirilməsi",
"PortalRenamingDescription": "Burada portal ünvanınızı dəyişə bilərsiniz.",
"PortalRenamingLabelText": "Yeni portal adı",

View File

@ -72,7 +72,7 @@
"PortalAccessSubTitle": "Този раздел Ви позволява да предоставите на потребителите безопасни и удобни начини за достъп до портала.",
"PortalNameEmpty": "Името на профила е празно",
"PortalNameIncorrect": "Неправилно име на профила",
"PortalNameLength": "Името на профила трябва да бъде дълго между 3 и 50 знака",
"PortalNameLength": "Името на профила трябва да бъде дълго между {{minLength}} и {{maxLength}} знака",
"PortalRenaming": "Преименуване на портала",
"PortalRenamingDescription": "Тук можете да промените адреса на вашия портал.",
"PortalRenamingLabelText": "Ново име на портала",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Tato sekce umožňuje poskytnout uživatelům bezpečný a pohodlný způsob přístupu k portálu.",
"PortalNameEmpty": "Název účtu je prázdný",
"PortalNameIncorrect": "Nesprávný název účtu",
"PortalNameLength": "Název účtu musí mít délku od 3 do 50 znaků",
"PortalNameLength": "Název účtu musí mít délku od {{minLength}} do {{maxLength}} znaků",
"PortalRenaming": "Přejmenování portálu",
"PortalRenamingDescription": "Zde můžete změnit Vaši adresu portálu.",
"PortalRenamingLabelText": "Nový název portálu",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "In diesem Bereich können Sie den Benutzern einen sicheren und nahtlosen Zugang zum Portal gewähren.",
"PortalNameEmpty": "Der Kontoname ist leer",
"PortalNameIncorrect": "Inkorrekter Kontoname",
"PortalNameLength": "Der Kontoname muss zwischen 3 und 50 Zeichen lang sein",
"PortalNameLength": "Der Kontoname muss zwischen {{minLength}} und {{maxLength}} Zeichen lang sein",
"PortalRenaming": "Portalumbenennung",
"PortalRenamingDescription": "Hier können Sie Ihre Portaladresse ändern.",
"PortalRenamingLabelText": "Neuer Portalname",

View File

@ -68,7 +68,7 @@
"PortalAccessSubTitle": "Αυτή η ενότητα σάς επιτρέπει να παρέχετε στους χρήστες ασφαλείς και βολικούς τρόπους πρόσβασης στην πύλη.",
"PortalNameEmpty": "Το όνομα λογαριασμού είναι κενό",
"PortalNameIncorrect": "Λανθασμένο όνομα λογαριασμού",
"PortalNameLength": "Το όνομα του λογαριασμού πρέπει να έχει μήκος μεταξύ 3 και 50 χαρακτήρων",
"PortalNameLength": "Το όνομα του λογαριασμού πρέπει να έχει μήκος μεταξύ {{minLength}} και {{maxLength}} χαρακτήρων",
"PortalRenaming": "Μετονομασία πύλης",
"PortalRenamingDescription": "Εδώ μπορείτε να αλλάξετε τη διεύθυνση της πύλης σας.",
"PortalRenamingLabelText": "Νέο όνομα πύλης",

View File

@ -137,7 +137,7 @@
"PortalIntegration": "Space integration",
"PortalNameEmpty": "Account name is empty",
"PortalNameIncorrect": "Incorrect account name",
"PortalNameLength": "The account name must be between 3 and 50 characters long",
"PortalNameLength": "The account name must be between {{minLength}} and {{maxLength}} characters long",
"PortalRenaming": "Space Renaming",
"PortalRenamingDescription": "Here you can change your space address.",
"PortalRenamingLabelText": "New space name",

View File

@ -1,7 +1,7 @@
{
"ChangeEmailTitle": "Change email",
"Desc": "Please set up the DocSpace registration data.",
"Domain": "Domain:",
"Domain": "Domain",
"ErrorEmail": "Invalid email address",
"ErrorInitWizard": "The service is currently unavailable, please try again later.",
"ErrorInitWizardButton": "Try again",
@ -16,7 +16,7 @@
"License": "Accept the terms of the ",
"LicenseLink": "License agreements",
"PlaceholderLicense": "Your license file",
"Timezone": "Time zone:",
"Timezone": "Timezone",
"WelcomeTitle": "Welcome to your DocSpace!",
"WizardTitle": "Set up your DocSpace"
}

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "Esta sección le permite ofrecer a los usuarios formas seguras y cómodas de acceder al portal.",
"PortalNameEmpty": "Nombre de cuenta está vacío",
"PortalNameIncorrect": "Nombre de cuenta incorrecto",
"PortalNameLength": "El nombre de cuenta debe tener entre 3 y 50 caracteres de longitud",
"PortalNameLength": "El nombre de cuenta debe tener entre {{minLength}} y {{maxLength}} caracteres de longitud",
"PortalRenaming": "Cambio del nombre de portal",
"PortalRenamingDescription": "Puede cambiar la dirección de su portal aquí.",
"PortalRenamingLabelText": "Nombre de portal nuevo",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Tämän osan avulla voit tarjota käyttäjille turvallisia ja käteviä tapoja käyttää portaalia.",
"PortalNameEmpty": "Tilin nimi on tyhjä",
"PortalNameIncorrect": "Virheellinen tilin nimi",
"PortalNameLength": "Tilin nimen on oltava 3-50 merkkiä pitkä",
"PortalNameLength": "Tilin nimen on oltava {{minLength}}-{{maxLength}} merkkiä pitkä",
"PortalRenaming": "Portaalin uudelleennimeäminen",
"PortalRenamingDescription": "Tässä voit vaihtaa portaalisi osoitteen.",
"PortalRenamingLabelText": "Uusi portaalin nimi",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "Cette section vous permet de fournir aux utilisateurs des moyens sûrs et pratiques d'accéder au portail.",
"PortalNameEmpty": "Nom de compte est vide",
"PortalNameIncorrect": "Nom de compte incorrect",
"PortalNameLength": "Le nom du compte doit comporter entre 3 et 50 caractères",
"PortalNameLength": "Le nom du compte doit comporter entre {{minLength}} et {{maxLength}} caractères",
"PortalRenaming": "Changement de nom du portail",
"PortalRenamingDescription": "Ici vous pouvez changer l'adresse du portail.",
"PortalRenamingLabelText": "Nouveau nom du portail",

View File

@ -68,7 +68,7 @@
"PortalAccessSubTitle": "Այս բաժինը թույլ է տալիս օգտվողներին տրամադրել կայքէջ մուտք գործելու անվտանգ և հարմար եղանակներ:",
"PortalNameEmpty": "Հաշվի անունը դատարկ է",
"PortalNameIncorrect": "Հաշվի սխալ անուն",
"PortalNameLength": "Հաշվի անունը պետք է լինի 3-ից 50 նիշ",
"PortalNameLength": "Հաշվի անունը պետք է լինի {{minLength}}-ից {{maxLength}} նիշ",
"PortalRenaming": "Կայքէջի վերանվանում",
"PortalRenamingDescription": "Այստեղ Դուք կարող եք փոխել Ձեր կայքէջի հասցեն:",
"PortalRenamingLabelText": "Նոր կայքէջի անուն",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "Questa sezione consente di fornire agli utenti modi sicuri e convenienti per accedere al portale.",
"PortalNameEmpty": "Nome account vuoto",
"PortalNameIncorrect": "Nome account errato",
"PortalNameLength": "Il nome dell'account deve contenere da 3 a 50 caratteri",
"PortalNameLength": "Il nome dell'account deve contenere da {{minLength}} a {{maxLength}} caratteri",
"PortalRenaming": "Ridenominazione del portale",
"PortalRenamingDescription": "Qui tu puoi cambiare il tuo indirizzo portale.",
"PortalRenamingLabelText": "Nome del portale nuovo",

View File

@ -72,7 +72,7 @@
"PortalAccessSubTitle": "このセクションでは、ユーザーがポータルにアクセスするための安全で便利な方法を提供することができます。",
"PortalNameEmpty": "アカウント名が空です。",
"PortalNameIncorrect": "アカウント名が無効です。",
"PortalNameLength": "アカウント名は350文字がある必要です。",
"PortalNameLength": "アカウント名は{{minLength}}{{maxLength}}文字がある必要です。",
"PortalRenaming": "ポータル改名",
"PortalRenamingDescription": "ここではポータルアドレスを変更できます。",
"PortalRenamingLabelText": "新のポータル名",

View File

@ -68,7 +68,7 @@
"PortalAccessSubTitle": "이 섹션에서는 사용자에게 안전하고 편리한 포털 액세스 방법을 제공할 수 있습니다.",
"PortalNameEmpty": "계정 이름이 비어 있습니다",
"PortalNameIncorrect": "계정 이름이 잘못되었습니다",
"PortalNameLength": "계정 이름은 3~50글자여야 합니다",
"PortalNameLength": "계정 이름은 {{minLength}}~{{maxLength}}글자여야 합니다",
"PortalRenaming": "포털 이름 변경",
"PortalRenamingDescription": "여기에서 포털 주소를 변경할 수 있습니다.",
"PortalRenamingLabelText": "새 포털 이름",

View File

@ -66,7 +66,7 @@
"PortalAccessSubTitle": "ພາກນີ້ຊ່ວຍໃຫ້ທ່ານສາມາດໃຫ້ຜູ້ໃຊ້ມີວິທີທີ່ປອດໄພແລະສະດວກໃນການເຂົ້າເຖິງປະຕູ. ",
"PortalNameEmpty": "ບັນຊີ ຊື່ ແມ່ນ ເປົ່າ",
"PortalNameIncorrect": "ບໍ່ຖືກຕ້ອງ ບັນຊີ ຊື່ ",
"PortalNameLength": "ຊື່ບັນຊີຕ້ອງມີຄວາມຍາວລະຫວ່າງ 3 ຫາ 50 ຕົວອັກສອນ",
"PortalNameLength": "ຊື່ບັນຊີຕ້ອງມີຄວາມຍາວລະຫວ່າງ {{minLength}} ຫາ {{maxLength}} ຕົວອັກສອນ",
"PortalRenaming": "ປະຕູ ການ ປ່ຽນຊື່ ",
"PortalRenamingDescription": "ທີ່ນີ້ທ່ານສາມາດປ່ຽນທີ່ຢູ່ປະຕູຂອງເຈົ້າໄດ້.",
"PortalRenamingLabelText": "ໃໝ່ ປະຕູ ຊື່ ",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Šī sadaļa ļauj lietotājiem nodrošināt drošus un ērtus veidus, kā piekļūt portālam.",
"PortalNameEmpty": "Konta nosaukums ir tukšs",
"PortalNameIncorrect": "Nepareizs konta nosaukums",
"PortalNameLength": "Konta nosaukumam ir jābūt no 3 līdz 50 rakstzīmēm garam",
"PortalNameLength": "Konta nosaukumam ir jābūt no {{minLength}} līdz {{maxLength}} rakstzīmēm garam",
"PortalRenaming": "Portāla pārdēvēšana",
"PortalRenamingDescription": "Šeit jūs varat mainīt savu portāla adresi.",
"PortalRenamingLabelText": "Jauns portāla nosaukums",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "In dit gedeelte kunt u gebruikers veilige en handige manieren bieden om toegang te krijgen tot het portaal.",
"PortalNameEmpty": "Accountnaam is leeg",
"PortalNameIncorrect": "Onjuiste accountnaam",
"PortalNameLength": "De naam van de account moet tussen 3 en 50 tekens lang zijn",
"PortalNameLength": "De naam van de account moet tussen {{minLength}} en {{maxLength}} tekens lang zijn",
"PortalRenaming": "Hernoemen Portaal",
"PortalRenamingDescription": "Hier kunt u uw portaaladres veranderen.",
"PortalRenamingLabelText": "Nieuwe portaalnaam",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Dana sekcja umożliwia zapewnienie użytkownikom bezpiecznych i wygodnych metod dostępu do portalu.",
"PortalNameEmpty": "Nazwa konta jest pusta",
"PortalNameIncorrect": "Nieprawidłowa nazwa konta",
"PortalNameLength": "Nazwa konta musi zawierać od 3 do 50 znaków",
"PortalNameLength": "Nazwa konta musi zawierać od {{minLength}} do {{maxLength}} znaków",
"PortalRenaming": "Zmiana nazwy portalu",
"PortalRenamingDescription": "Tutaj możesz zmienić adres portalu.",
"PortalRenamingLabelText": "Nowa nazwa portalu",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "Esta seção permite que você forneça aos usuários formas seguras e convenientes de acessar o portal.",
"PortalNameEmpty": "Nome da conta está vazio",
"PortalNameIncorrect": "Nome da conta incorreto",
"PortalNameLength": "O nome da conta deve ter entre 3 e 50 caracteres",
"PortalNameLength": "O nome da conta deve ter entre {{minLength}} e {{maxLength}} caracteres",
"PortalRenaming": "Renomear portal",
"PortalRenamingDescription": "Aqui, você pode alterar o endereço do seu portal.",
"PortalRenamingLabelText": "Nome do novo portal",

View File

@ -68,7 +68,7 @@
"PortalAccessSubTitle": "Esta secção permite-lhe fornecer aos utilizadores formas seguras e convenientes de aceder ao portal.",
"PortalNameEmpty": "Não colocou um nome para a conta",
"PortalNameIncorrect": "Nome de conta incorreto",
"PortalNameLength": "O nome da conta tem de ter entre 3 e 50 caracteres",
"PortalNameLength": "O nome da conta tem de ter entre {{minLength}} e {{maxLength}} caracteres",
"PortalRenaming": "Renomeação do Portal",
"PortalRenamingDescription": "Aqui pode mudar o endereço do seu portal.",
"PortalRenamingLabelText": "Novo nome de portal",

View File

@ -68,7 +68,7 @@
"PortalAccessSubTitle": "Această secțiune va permite să le oferă utilizatorilor accesul sigur și convenabil la portal.",
"PortalNameEmpty": "Denumirea contului este necompletată",
"PortalNameIncorrect": "Denumirea contului incorectă",
"PortalNameLength": "Denumirea contului trebuie să conțină de la 3 până la 50 de caractere",
"PortalNameLength": "Denumirea contului trebuie să conțină de la {{minLength}} până la {{maxLength}} de caractere",
"PortalRenaming": "Redenumirea portalului",
"PortalRenamingDescription": "Aici puteți schimba adresa de portal dvs.",
"PortalRenamingLabelText": "O nouă denumire de portal",

View File

@ -136,7 +136,7 @@
"PortalIntegration": "Интеграция с порталом",
"PortalNameEmpty": "Поле имя аккаунта не заполнено",
"PortalNameIncorrect": "Неверное имя портала",
"PortalNameLength": "Имя учетной записи должно быть от 3 до 50 символов",
"PortalNameLength": "Имя учетной записи должно быть от {{minLength}} до {{maxLength}} символов",
"PortalRenaming": "Изменение имени портала",
"PortalRenamingDescription": "Здесь вы можете изменить адрес портала.",
"PortalRenamingLabelText": "Новое имя портала",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Tu môžete vytvoriť pre používateľov bezpečné a pohodlné spôsoby prístupu na portál.",
"PortalNameEmpty": "Názov účtu je prázdny",
"PortalNameIncorrect": "Nesprávny názov účtu",
"PortalNameLength": "Názov účtu musí mať 3 až 50 znakov",
"PortalNameLength": "Názov účtu musí mať {{minLength}} až {{maxLength}} znakov",
"PortalRenaming": "Premenovanie portálu",
"PortalRenamingDescription": "Tu môžete zmeniť adresu portálu.",
"PortalRenamingLabelText": "Nový názov portálu",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Ta razdelek omogoča uporabnikom varne in udobne načine za dostop do portala.",
"PortalNameEmpty": "Ime računa je prazno",
"PortalNameIncorrect": "Napačno ime računa",
"PortalNameLength": "Ime računa mora imeti dolžino od 3 do 50 znakov",
"PortalNameLength": "Ime računa mora imeti dolžino od {{minLength}} do {{maxLength}} znakov",
"PortalRenaming": "Preimenovanje portala",
"PortalRenamingDescription": "Tukaj lahko spremenite naslov vašega portala.",
"PortalRenamingLabelText": "Novo ime portala",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Bu bölüm, kullanıcılara portala erişmeleri için güvenli ve uygun yollar sağlamanıza olanak tanır.",
"PortalNameEmpty": "Hesap adı boş",
"PortalNameIncorrect": "Yanlış hesap adı",
"PortalNameLength": "Hesap adı 3 ila 50 karakter uzunluğunda olmalıdır",
"PortalNameLength": "Hesap adı {{minLength}} ila {{maxLength}} karakter uzunluğunda olmalıdır",
"PortalRenaming": "Portal Yeniden Adlandırma",
"PortalRenamingDescription": "Buradan portal adresinizi değiştirebilirsiniz.",
"PortalRenamingLabelText": "Yeni portal adı",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "У цьому розділі можна надати користувачам безпечні та зручні способи доступу до порталу.",
"PortalNameEmpty": "Ім'я облікового запису порожнє",
"PortalNameIncorrect": "Неправильне ім'я облікового запису",
"PortalNameLength": "Довжина імені облікового запису має бути від 3 до 50 символів",
"PortalNameLength": "Довжина імені облікового запису має бути від {{minLength}} до {{maxLength}} символів",
"PortalRenaming": "Перейменування порталу",
"PortalRenamingDescription": "Тут ви можете змінити адресу вашого порталу.",
"PortalRenamingLabelText": "Нове ім'я порталу",

View File

@ -71,7 +71,7 @@
"PortalAccessSubTitle": "Phần này cho phép bạn cung cấp cho người dùng những cách an toàn và thuận tiện để truy cập cổng thông tin.",
"PortalNameEmpty": "Tên tài khoản trống",
"PortalNameIncorrect": "Tên tài khoản không chính xác",
"PortalNameLength": "Tên tài khoản phải dài từ 3 đến 50 ký tự",
"PortalNameLength": "Tên tài khoản phải dài từ {{minLength}} đến {{maxLength}} ký tự",
"PortalRenaming": "Đang đổi tên cổng",
"PortalRenamingDescription": "Ở đây bạn có thể thay đổi địa chỉ cổng thông tin của bạn.",
"PortalRenamingLabelText": "Tên cổng thông tin mới",

View File

@ -73,7 +73,7 @@
"PortalAccessSubTitle": "这一部分允许您为用户提供安全又方便的方式来访问门户网站。",
"PortalNameEmpty": "账户名称为空",
"PortalNameIncorrect": "账户名称不正确",
"PortalNameLength": "账户名称的长度必须在3至50个字符之间",
"PortalNameLength": "账户名称的长度必须在{{minLength}}至{{maxLength}}个字符之间",
"PortalRenaming": "门户重命名",
"PortalRenamingDescription": "在此处,您可以更改您的门户地址。",
"PortalRenamingLabelText": "新门户名称",

View File

@ -488,7 +488,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
path={"/portal-settings"}
component={PortalSettingsRoute}
/>
<PrivateRoute
<PublicRoute
path={"/preparation-portal"}
component={PreparationPortalRoute}
/>

View File

@ -25,6 +25,7 @@ import {
} from "../dialogs";
import ConvertPasswordDialog from "../dialogs/ConvertPasswordDialog";
import ArchiveDialog from "../dialogs/ArchiveDialog";
import PreparationPortalDialog from "../dialogs/PreparationPortalDialog";
const Panels = (props) => {
const {
@ -53,6 +54,7 @@ const Panels = (props) => {
restoreAllPanelVisible,
archiveDialogVisible,
inviteUsersWarningDialogVisible,
preparationPortalDialogVisible,
} = props;
const { t } = useTranslation(["Translations", "Common"]);
@ -119,11 +121,14 @@ const Panels = (props) => {
inviteUsersWarningDialogVisible && (
<InviteUsersWarningDialog key="invite-users-warning-dialog" />
),
preparationPortalDialogVisible && (
<PreparationPortalDialog key="preparation-portal-dialog" />
),
];
};
export default inject(
({ auth, dialogsStore, uploadDataStore, versionHistoryStore }) => {
({ auth, dialogsStore, uploadDataStore, versionHistoryStore, backup }) => {
const {
sharingPanelVisible,
ownerPanelVisible,
@ -151,11 +156,14 @@ export default inject(
inviteUsersWarningDialogVisible,
} = dialogsStore;
const { preparationPortalDialogVisible } = backup;
const { uploadPanelVisible } = uploadDataStore;
const { isVisible: versionHistoryPanelVisible } = versionHistoryStore;
const { hotkeyPanelVisible } = auth.settingsStore;
return {
preparationPortalDialogVisible,
sharingPanelVisible,
uploadPanelVisible,
ownerPanelVisible,

View File

@ -139,7 +139,6 @@ class NavMenu extends React.Component {
asideContent,
history,
isDesktop,
preparationPortalDialogVisible,
isFrame,
showHeader,
} = this.props;
@ -188,7 +187,6 @@ class NavMenu extends React.Component {
{asideContent}
</Aside>
)}
{preparationPortalDialogVisible && <PreparationPortalDialog />}
</StyledContainer>
)}
</LayoutContextConsumer>
@ -220,16 +218,16 @@ NavMenu.defaultProps = {
isDesktop: false,
};
const NavMenuWrapper = inject(({ auth, backup }) => {
const NavMenuWrapper = inject(({ auth }) => {
const { settingsStore, isAuthenticated, isLoaded, language } = auth;
const { isDesktopClient: isDesktop, frameConfig, isFrame } = settingsStore;
const { preparationPortalDialogVisible } = backup;
return {
isAuthenticated,
isLoaded,
isDesktop,
language,
preparationPortalDialogVisible,
showHeader: frameConfig?.showHeader,
isFrame,
};

View File

@ -7,7 +7,7 @@ const StyledPreparationPortalDialog = styled.div`
margin-bottom: 40px;
}
.preparation-portal_body-wrapper {
margin-bottom: 16px;
margin-bottom: 0;
}
`;

View File

@ -15,10 +15,9 @@ const PreparationPortalDialog = (props) => {
isLoading={!tReady}
visible={preparationPortalVisible}
onClose={onClose}
contentHeight="388px"
contentWidth="520px"
displayType="modal"
withoutCloseButton
isCloseable={false}
isLarge
>
<ModalDialog.Header>{t("PortalRestoring")}</ModalDialog.Header>
<ModalDialog.Body>

View File

@ -538,7 +538,7 @@ class SharingPanelComponent extends React.Component {
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={true}
isCloseable={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
@ -554,7 +554,7 @@ class SharingPanelComponent extends React.Component {
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={true}
isCloseable={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
@ -572,7 +572,6 @@ class SharingPanelComponent extends React.Component {
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}
@ -633,7 +632,6 @@ class SharingPanelComponent extends React.Component {
<ModalDialog
displayType="modal"
visible={visible}
withoutCloseButton={false}
withoutBodyScroll={true}
scale={true}
onClose={this.onClose}

View File

@ -31,6 +31,7 @@ const PortalRenaming = (props) => {
tenantAlias,
initSettings,
setIsLoaded,
getAllSettings,
} = props;
const portalNameFromSessionStorage = getFromSessionStorage("portalName");
@ -71,6 +72,14 @@ const PortalRenaming = (props) => {
const [isCustomizationView, setIsCustomizationView] = useState(false);
const [domainValidator, setDomainValidator] = useState(null);
useEffect(() => {
getAllSettings().then((res) => {
setDomainValidator(res.domainValidator);
});
}, []);
useEffect(() => {
setDocumentTitle(t("PortalRenaming"));
if (!isLoaded) initSettings().then(() => setIsLoaded(true));
@ -174,16 +183,28 @@ const PortalRenaming = (props) => {
};
const onValidateInput = (value) => {
const validDomain = new RegExp("^[a-z0-9]([a-z0-9-]){1,98}[a-z0-9]$", "i");
const validDomain = new RegExp(domainValidator.regex);
switch (true) {
case value === "":
setErrorValue(t("PortalNameEmpty"));
saveToSessionStorage("errorValue", t("PortalNameEmpty"));
break;
case value.length < 3 || value.length > 50:
setErrorValue(t("PortalNameLength"));
saveToSessionStorage("errorValue", t("PortalNameLength"));
case value.length < domainValidator.minLength ||
value.length > domainValidator.maxLength:
setErrorValue(
t("PortalNameLength", {
minLength: domainValidator.minLength,
maxLength: domainValidator.maxLength,
})
);
saveToSessionStorage(
"errorValue",
t("PortalNameLength", {
minLength: domainValidator.minLength,
maxLength: domainValidator.maxLength,
})
);
break;
case !validDomain.test(value):
setErrorValue(t("PortalNameIncorrect"));
@ -305,7 +326,7 @@ const PortalRenaming = (props) => {
export default inject(({ auth, setup, common }) => {
const { theme, tenantAlias } = auth.settingsStore;
const { setPortalRename } = setup;
const { setPortalRename, getAllSettings } = setup;
const {
isLoaded,
setIsLoadedPortalRenaming,
@ -320,6 +341,7 @@ export default inject(({ auth, setup, common }) => {
tenantAlias,
initSettings,
setIsLoaded,
getAllSettings,
};
})(
withLoading(withTranslation(["Settings", "Common"])(observer(PortalRenaming)))

View File

@ -8,7 +8,7 @@ import { isMobileOnly } from "react-device-detect";
import { mobile } from "@docspace/components/utils/device";
const linkColor = globalColors.black;
const borderColor = globalColors.grayLightMid;
const INPUT_LENGTH = "350px";
const TEXT_LENGTH = "700px";
const commonStyles = css`
@ -452,40 +452,11 @@ const StyledBackupList = styled.div`
margin-right: 16px;
color: ${(props) => props.theme.client.settings.backup.textColor};
}
#backup-list_help {
display: flex;
background-color: ${(props) => props.theme.backgroundColor};
margin-bottom: 16px;
}
.backup-list_tooltip {
margin-left: 8px;
}
.backup-list_content {
display: grid;
height: calc(100% - 32px);
height: 100%;
grid-template-rows: max-content auto max-content;
.backup-list_agreement-text {
user-select: none;
div:first-child {
display: inline-block;
}
}
.backup-list_footer {
padding: 16px 16px 0 16px;
${(props) => !props.isEmpty && `border-top: 1px solid ${borderColor}`};
margin-left: -16px;
margin-right: -16px;
.restore_dialog-button {
display: flex;
button:first-child {
margin-right: 10px;
width: 50%;
}
button:last-child {
width: 50%;
}
}
}
}
`;
const StyledSettingsHeader = styled.div`

View File

@ -8,7 +8,7 @@ import RadioButton from "@docspace/components/radio-button";
import toastr from "@docspace/components/toast/toastr";
import { startRestore } from "@docspace/common/api/portal";
import { combineUrl } from "@docspace/common/utils";
import { BackupStorageType } from "@docspace/common/constants";
import { BackupStorageType, TenantStatus } from "@docspace/common/constants";
import { request } from "@docspace/common/api/client";
import { StyledRestoreBackup } from "./../StyledBackup";
import BackupListModalDialog from "./sub-components/backup-list";
@ -39,10 +39,10 @@ class RestoreBackup extends React.Component {
isNotify: true,
isVisibleDialog: false,
isPanelVisible: false,
isCheckedDocuments: true,
isCheckedDocuments: false,
isCheckedThirdParty: false,
isCheckedThirdPartyStorage: false,
isCheckedLocalFile: false,
isCheckedLocalFile: true,
selectedFileId: "",
selectedFile: "",
@ -187,7 +187,12 @@ class RestoreBackup extends React.Component {
isCheckedThirdPartyStorage,
isCheckedThirdParty,
} = this.state;
const { history, socketHelper, getStorageParams } = this.props;
const {
history,
socketHelper,
getStorageParams,
setTenantStatus,
} = this.props;
if (!this.canRestore()) {
this.setState({
@ -251,6 +256,7 @@ class RestoreBackup extends React.Component {
}
startRestore(backupId, storageType, storageParams, isNotify)
.then(() => setTenantStatus(TenantStatus.PortalRestore))
.then(() => {
socketHelper.emit({
command: "restore-backup",
@ -324,6 +330,16 @@ class RestoreBackup extends React.Component {
{t("RestoreBackupDescription")}
</Text>
</div>
<RadioButton
label={t("LocalFile")}
name={"isCheckedLocalFile"}
key={4}
isChecked={isCheckedLocalFile}
isDisabled={!isEnableRestore}
{...commonRadioButtonProps}
/>
<RadioButton
label={t("RoomsModule")}
name={"isCheckedDocuments"}
@ -351,15 +367,6 @@ class RestoreBackup extends React.Component {
{...commonRadioButtonProps}
/>
<RadioButton
label={t("LocalFile")}
name={"isCheckedLocalFile"}
key={4}
isChecked={isCheckedLocalFile}
isDisabled={!isEnableRestore}
{...commonRadioButtonProps}
/>
<div className="restore-backup_modules">
{isCheckedDocuments && (
<RoomsModule
@ -480,7 +487,7 @@ class RestoreBackup extends React.Component {
export default inject(({ auth, backup }) => {
const { settingsStore, currentQuotaStore } = auth;
const { socketHelper, theme, isTabletView } = settingsStore;
const { socketHelper, theme, isTabletView, setTenantStatus } = settingsStore;
const {
downloadingProgress,
getProgress,
@ -495,6 +502,7 @@ export default inject(({ auth, backup }) => {
const buttonSize = isTabletView ? "normal" : "small";
const { isRestoreAndAutoBackupAvailable } = currentQuotaStore;
return {
setTenantStatus,
isEnableRestore: isRestoreAndAutoBackupAvailable,
setStorageRegions,
setThirdPartyStorage,

View File

@ -21,6 +21,40 @@ import HelpButton from "@docspace/components/help-button";
import config from "PACKAGE_FILE";
import { StyledBackupList } from "../../../StyledBackup";
import BackupListBody from "./BackupListBody";
import { TenantStatus } from "@docspace/common/constants";
import styled from "styled-components";
const StyledModalDialog = styled(ModalDialog)`
.restore_footer {
width: 100%;
.restore_dialog-button {
display: flex;
button:first-child {
margin-right: 10px;
width: 50%;
}
button:last-child {
width: 50%;
}
}
#backup-list_help {
display: flex;
background-color: ${(props) => props.theme.backgroundColor};
margin-bottom: 16px;
}
.backup-list_agreement-text {
user-select: none;
div:first-child {
display: inline-block;
}
}
.backup-list_tooltip {
margin-left: 8px;
}
}
`;
class BackupListModalDialog extends React.Component {
constructor(props) {
@ -89,7 +123,7 @@ class BackupListModalDialog extends React.Component {
};
onRestorePortal = () => {
const { selectedFileId } = this.state;
const { isNotify, history, socketHelper, t } = this.props;
const { isNotify, history, socketHelper, t, setTenantStatus } = this.props;
if (!selectedFileId) {
toastr.error(t("RecoveryFileNotSelected"));
@ -106,6 +140,7 @@ class BackupListModalDialog extends React.Component {
];
startRestore(backupId, storageType, storageParams, isNotify)
.then(() => setTenantStatus(TenantStatus.PortalRestore))
.then(() => {
socketHelper.emit({
command: "restore-backup",
@ -159,15 +194,17 @@ class BackupListModalDialog extends React.Component {
);
return (
<ModalDialog
<StyledModalDialog
displayType="aside"
visible={isVisibleDialog}
onClose={onModalClose}
displayType="aside"
withoutBodyScroll
contentHeight="100%"
contentPaddingBottom="0px"
withFooterBorder
>
<ModalDialog.Header>{t("BackupList")}</ModalDialog.Header>
<ModalDialog.Header>
<Text fontSize="21px" fontWeight={700}>
{t("BackupList")}
</Text>
</ModalDialog.Header>
<ModalDialog.Body>
<StyledBackupList
isCopyingToLocal={isCopyingToLocal}
@ -210,53 +247,52 @@ class BackupListModalDialog extends React.Component {
)
) : (
<div className="loader" key="loader">
<Loaders.ListLoader />
</div>
)}
</div>
<div className="backup-list_footer">
{filesList.length > 0 && (
<div>
<div id="backup-list_help">
<Checkbox
truncate
className="backup-list_checkbox"
onChange={this.onChangeCheckbox}
isChecked={isChecked}
/>
<Text as="span" className="backup-list_agreement-text">
{t("UserAgreement")}
<HelpButton
className="backup-list_tooltip"
offsetLeft={100}
iconName={HelpReactSvgUrl}
getContent={helpContent}
tooltipMaxWidth={"286px"}
/>
</Text>
</div>
<div className="restore_dialog-button">
<Button
primary
size="normal"
label={t("Common:Restore")}
onClick={this.onRestorePortal}
isDisabled={isCopyingToLocal || !isChecked}
/>
<Button
size="normal"
label={t("Common:CloseButton")}
onClick={onModalClose}
/>
</div>
<Loaders.ListLoader count={7} />
</div>
)}
</div>
</div>
</StyledBackupList>
</ModalDialog.Body>
</ModalDialog>
<ModalDialog.Footer>
<div className="restore_footer">
<div id="backup-list_help">
<Checkbox
truncate
className="backup-list_checkbox"
onChange={this.onChangeCheckbox}
isChecked={isChecked}
/>
<Text as="span" className="backup-list_agreement-text">
{t("UserAgreement")}
<HelpButton
className="backup-list_tooltip"
offsetLeft={100}
iconName={HelpReactSvgUrl}
getContent={helpContent}
tooltipMaxWidth={"286px"}
/>
</Text>
</div>
<div className="restore_dialog-button">
<Button
primary
size="normal"
label={t("Common:Restore")}
onClick={this.onRestorePortal}
isDisabled={isCopyingToLocal || !isChecked}
/>
<Button
size="normal"
label={t("Common:CloseButton")}
onClick={onModalClose}
/>
</div>
</div>
</ModalDialog.Footer>
</StyledModalDialog>
);
}
}
@ -268,9 +304,10 @@ BackupListModalDialog.propTypes = {
export default inject(({ auth }) => {
const { settingsStore } = auth;
const { socketHelper, theme } = settingsStore;
const { socketHelper, theme, setTenantStatus } = settingsStore;
return {
setTenantStatus,
theme,
socketHelper,
};

View File

@ -17,6 +17,24 @@ const StyledPreparationPortal = styled.div`
line-height: 20px;
max-width: 480px;
}
.preparation-portal_body-wrapper {
margin-bottom: 24px;
width: 100%;
max-width: ${(props) => (props.errorMessage ? "560px" : "480px")};
box-sizing: border-box;
align-items: center;
.preparation-portal_error {
text-align: center;
color: ${(props) => props.theme.preparationPortalProgress.errorTextColor};
}
.preparation-portal_text {
text-align: center;
color: ${(props) =>
props.theme.preparationPortalProgress.descriptionTextColor};
}
}
`;
export { StyledPreparationPortal };

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import ErrorContainer from "@docspace/common/components/ErrorContainer";
import { withTranslation } from "react-i18next";
@ -14,233 +14,237 @@ const unSizeMultiplicationFactor = 3;
const baseFirstMultiplicationFactor = 700;
const baseSecondMultiplicationFactor = 400;
const baseThirdMultiplicationFactor = 180;
const firstBound = 10,
secondBound = 63,
thirdBound = 98;
class PreparationPortal extends React.Component {
constructor(props) {
super(props);
this.state = {
percent: 0,
errorMessage: "",
firstBound: 10,
secondBound: 63,
let timerId = null,
progressTimerId = null,
prevProgress;
const PreparationPortal = (props) => {
const {
multiplicationFactor,
t,
withoutHeader,
style,
clearLocalStorage,
} = props;
const [percent, setPercent] = useState(0);
const [errorMessage, setErrorMessage] = useState("");
const clearAllIntervals = () => {
clearInterval(timerId);
clearInterval(progressTimerId);
progressTimerId = null;
timerId = null;
};
const returnToPortal = () => {
setTimeout(() => {
window.location.replace("/");
}, 5000);
};
const reachingFirstBoundary = (percent) => {
let progress = percent;
const delay = baseFirstMultiplicationFactor * multiplicationFactor;
if (progressTimerId) return;
progressTimerId = setInterval(() => {
progress += 1;
if (progress !== firstBound) setPercent(progress);
else {
clearInterval(progressTimerId);
progressTimerId = null;
}
}, delay);
};
const reachingSecondBoundary = (percent) => {
let progress = percent;
const delay = baseSecondMultiplicationFactor * multiplicationFactor;
if (progressTimerId) return;
progressTimerId = setInterval(() => {
progress += 1;
if (progress !== secondBound) setPercent(progress);
else {
clearInterval(progressTimerId);
progressTimerId = null;
}
}, delay);
};
const reachingThirdBoundary = (percent) => {
let progress = percent;
const delay = baseThirdMultiplicationFactor * multiplicationFactor;
if (progressTimerId) return;
progressTimerId = setInterval(() => {
progress += 1;
if (progress < thirdBound) setPercent(progress);
else {
clearInterval(progressTimerId);
progressTimerId = null;
}
}, delay);
};
useEffect(() => {
if (percent >= firstBound) {
if (percent < secondBound) {
reachingSecondBoundary(percent);
return;
} else reachingThirdBoundary(percent);
}
}, [percent]);
const getIntervalProgress = async () => {
try {
const response = await getRestoreProgress();
if (!response) {
setErrorMessage(t("Common:ErrorInternalServer"));
clearAllIntervals();
return;
}
if (response.error) {
clearInterval(timerId);
clearInterval(progressTimerId);
progressTimerId = null;
timerId = null;
setErrorMessage(response.error);
return;
}
const currProgress = response.progress;
if (currProgress > 0 && prevProgress !== currProgress) {
setPercent(currProgress);
clearInterval(progressTimerId);
progressTimerId = null;
}
prevProgress = currProgress;
if (currProgress === 100) {
clearAllIntervals();
clearLocalStorage();
returnToPortal();
}
} catch (error) {
clearAllIntervals();
setErrorMessage(error);
}
};
const getRecoveryProgress = async () => {
const errorMessage = (error) => {
if (typeof error !== "object") return error;
return (
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
t("Common:ErrorInternalServer")
);
};
this.timerId = null;
this.progressTimerId = null;
}
componentDidMount() {
getRestoreProgress()
.then((response) => {
if (response) {
if (!response.error) {
if (response.progress === 100)
this.setState({
percent: 100,
});
if (response.progress !== 100) {
this.timerId = setInterval(() => this.getProgress(), 1000);
this.progressInitiationFirstBound();
}
} else {
this.setState({
errorMessage: response.error,
});
}
}
})
.catch((err) => {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
this.setState({
errorMessage: errorMessage,
});
});
}
componentWillUnmount() {
clearInterval(this.timerId);
clearInterval(this.progressTimerId);
}
try {
const response = await getRestoreProgress();
progressInitiationFirstBound = () => {
const { multiplicationFactor } = this.props;
const { percent, firstBound } = this.state;
if (!response) {
setErrorMessage(t("Common:ErrorInternalServer"));
return;
}
const { error, progress } = response;
let progress = percent;
if (error) {
setErrorMessage(response.error);
const common = baseFirstMultiplicationFactor * multiplicationFactor;
return;
}
if (!this.progressTimerId)
this.progressTimerId = setInterval(() => {
progress += 1;
if (progress !== firstBound && percent < progress)
percent < progress &&
this.setState({
percent: progress,
});
else {
clearInterval(this.progressTimerId);
this.progressTimerId = null;
}
}, common);
if (progress === 100) {
returnToPortal();
clearLocalStorage();
} else {
timerId = setInterval(() => getIntervalProgress(), 1000);
if (progress < firstBound) reachingFirstBoundary(progress);
}
setPercent(progress);
} catch (err) {
setErrorMessage(errorMessage(err));
}
};
progressInitiationSecondBound = () => {
const { multiplicationFactor } = this.props;
const { percent, secondBound } = this.state;
useEffect(async () => {
setTimeout(() => {
getRecoveryProgress();
}, 4000);
let progress = percent;
return () => {
clearAllIntervals();
};
}, []);
const common = baseSecondMultiplicationFactor * multiplicationFactor;
const headerText = errorMessage
? t("Common:Error")
: t("Common:PreparationPortalTitle");
if (!this.progressTimerId)
this.progressTimerId = setInterval(() => {
progress += 1;
if (progress !== secondBound)
percent < progress &&
this.setState({
percent: progress,
});
else {
clearInterval(this.progressTimerId);
this.progressTimerId = null;
}
}, common);
};
progressInitiationThirdBound = () => {
const { multiplicationFactor } = this.props;
const { percent } = this.state;
let progress = percent;
const common = baseThirdMultiplicationFactor * multiplicationFactor;
if (!this.progressTimerId)
this.progressTimerId = setInterval(() => {
progress += 1;
if (progress < 98)
percent < progress &&
this.setState({
percent: progress,
});
else {
clearInterval(this.progressTimerId);
this.progressTimerId = null;
}
}, common);
};
getProgress = () => {
const { secondBound } = this.state;
getRestoreProgress()
.then((response) => {
if (response) {
if (!response.error) {
const percentProgress = response.progress;
percentProgress !== this.state.percent &&
this.state.percent < percentProgress &&
this.setState(
{
percent: percentProgress,
},
() => {
clearInterval(this.progressTimerId);
this.progressTimerId = null;
if (percentProgress < secondBound) {
this.progressInitiationSecondBound();
} else {
this.progressInitiationThirdBound();
}
}
);
if (percentProgress === 100) {
clearInterval(this.timerId);
clearInterval(this.progressTimerId);
this.progressTimerId = null;
this.timerId = null;
}
} else {
clearInterval(this.timerId);
clearInterval(this.progressTimerId);
this.progressTimerId = null;
this.timerId = null;
this.setState({
errorMessage: response.error,
});
}
}
})
.catch((e) => {
clearInterval(this.timerId);
clearInterval(this.progressTimerId);
this.progressTimerId = null;
this.timerId = null;
this.setState({
percent: 100,
});
});
};
render() {
const { t, withoutHeader, style } = this.props;
const { percent, errorMessage } = this.state;
return (
<StyledPreparationPortal>
<ErrorContainer
headerText={withoutHeader ? "" : t("Common:PreparationPortalTitle")}
style={style}
>
<ColorTheme
themeId={ThemeType.Progress}
percent={percent}
errorMessage={errorMessage}
className="preparation-portal_body-wrapper"
>
{errorMessage ? (
<Text
className="preparation-portal_error"
color="#F21C0E"
>{`${errorMessage}`}</Text>
) : (
<>
<div className="preparation-portal_progress">
<div className="preparation-portal_progress-bar">
<div className="preparation-portal_progress-line"></div>
</div>
<Text className="preparation-portal_percent">{`${percent} %`}</Text>
return (
<StyledPreparationPortal errorMessage={errorMessage}>
<ErrorContainer
headerText={withoutHeader ? "" : headerText}
style={style}
>
<div className="preparation-portal_body-wrapper">
{errorMessage ? (
<Text className="preparation-portal_error">{`${errorMessage}`}</Text>
) : (
<ColorTheme
themeId={ThemeType.Progress}
percent={percent}
errorMessage={errorMessage}
className="preparation-portal_body-wrapper"
>
<div className="preparation-portal_progress">
<div className="preparation-portal_progress-bar">
<div className="preparation-portal_progress-line"></div>
</div>
<Text className="preparation-portal_text">
{t("PreparationPortalDescription")}
</Text>
</>
)}
</ColorTheme>
</ErrorContainer>
</StyledPreparationPortal>
);
}
}
<Text className="preparation-portal_percent">{`${percent} %`}</Text>
</div>
<Text className="preparation-portal_text">
{t("PreparationPortalDescription")}
</Text>
</ColorTheme>
)}
</div>
</ErrorContainer>
</StyledPreparationPortal>
);
};
const PreparationPortalWrapper = inject(({ backup }) => {
const { backupSize } = backup;
const { backupSize, clearLocalStorage } = backup;
const multiplicationFactor = backupSize
? backupSize / baseSize
: unSizeMultiplicationFactor;
return {
clearLocalStorage,
multiplicationFactor,
};
})(

View File

@ -73,7 +73,7 @@ export const WizardContainer = styled.div`
}
.password-field {
margin: 0 0 10px 0 !important;
margin: 0px !important;
}
`;
@ -82,7 +82,8 @@ export const StyledLink = styled.div`
display: flex;
gap: 8px;
align-items: center;
padding-bottom: 20px;
padding-bottom: 16px;
padding-top: 8px;
.generate-password-link {
color: ${(props) => props.theme.client.wizard.generatePasswordColor};
@ -98,14 +99,21 @@ export const StyledLink = styled.div`
export const StyledInfo = styled.div`
width: 100%;
display: grid;
grid-template-columns: 83px 1fr;
grid-template-columns: 59px 1fr;
align-items: center;
padding-bottom: 4px;
gap: 16px;
margin-bottom: 4px;
.machine-name {
padding-bottom: 4px;
padding-top: 4px;
padding-left: 16px;
padding-left: 8px;
line-height: 20px;
}
.combo-button {
padding-left: 8px;
}
`;
@ -114,7 +122,7 @@ export const StyledAcceptTerms = styled.div`
display: flex;
align-items: center;
gap: 0.3em;
padding-top: 12px;
padding-top: 20px;
padding-bottom: 24px;
.wizard-checkbox svg {

View File

@ -326,6 +326,7 @@ const Wizard = (props) => {
{t("Common:Language")}
</Text>
<ComboBox
withoutPadding
directionY="both"
options={cultureNames}
selectedOption={selectedLanguage}
@ -348,6 +349,7 @@ const Wizard = (props) => {
{t("Timezone")}
</Text>
<ComboBox
withoutPadding
directionY="both"
options={timezones}
selectedOption={selectedTimezone}

View File

@ -367,6 +367,10 @@ class SettingsSetupStore {
return api.settings.removeActiveSession(id);
};
getAllSettings = () => {
return api.settings.getSettings();
};
setLogoutVisible = (visible) => (this.logoutVisible = visible);
setLogoutAllVisible = (visible) => (this.logoutAllVisible = visible);

View File

@ -1,5 +1,5 @@
import styled, { css } from "styled-components";
import StyledBodyPreparationPortal from "../../StyledBodyPreparationPortal";
import StyledPreparationPortalProgress from "../../StyledPreparationPortalProgress";
import Base from "@docspace/components/themes/base";
const getDefaultStyles = ({ $currentColorScheme, theme }) =>
@ -10,6 +10,6 @@ const getDefaultStyles = ({ $currentColorScheme, theme }) =>
}
`;
StyledBodyPreparationPortal.defaultProps = { theme: Base };
StyledPreparationPortalProgress.defaultProps = { theme: Base };
export default styled(StyledBodyPreparationPortal)(getDefaultStyles);
export default styled(StyledPreparationPortalProgress)(getDefaultStyles);

View File

@ -136,10 +136,7 @@ const PrivateRoute = ({ component: Component, ...rest }) => {
);
}
if (tenantStatus !== TenantStatus.PortalRestore && isPortalUrl) {
return window.location.replace("/");
}
if (
isNotPaidPeriod &&
isLoaded &&

View File

@ -3,15 +3,26 @@ import React from "react";
import { Redirect, Route } from "react-router-dom";
import AppLoader from "../AppLoader";
import { inject, observer } from "mobx-react";
import { TenantStatus } from "../../constants";
export const PublicRoute = ({ component: Component, ...rest }) => {
const { wizardCompleted, isAuthenticated, isLoaded, personal } = rest;
const {
wizardCompleted,
isAuthenticated,
isLoaded,
personal,
tenantStatus,
} = rest;
const renderComponent = (props) => {
const isPreparationPortalUrl =
props.location.pathname === "/preparation-portal";
const isPortalRestoring = tenantStatus === TenantStatus.PortalRestore;
if (!isLoaded) {
return <AppLoader />;
}
if (isAuthenticated || personal) {
if (personal) {
return (
<Redirect
to={{
@ -22,6 +33,31 @@ export const PublicRoute = ({ component: Component, ...rest }) => {
);
}
if (isAuthenticated && !isPortalRestoring) {
return (
<Redirect
to={{
pathname: "/",
state: { from: props.location },
}}
/>
);
}
if (isAuthenticated && isPortalRestoring && !isPreparationPortalUrl) {
return (
<Redirect
to={{
pathname: combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/preparation-portal"
),
state: { from: props.location },
}}
/>
);
}
if (!wizardCompleted && props.location.pathname !== "/wizard") {
return (
<Redirect
@ -33,6 +69,28 @@ export const PublicRoute = ({ component: Component, ...rest }) => {
);
}
if (
!isAuthenticated &&
isPortalRestoring &&
wizardCompleted &&
!isPreparationPortalUrl
) {
return (
<Redirect
to={{
pathname: combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/preparation-portal"
),
state: { from: props.location },
}}
/>
);
}
if (wizardCompleted && !isAuthenticated && !isPortalRestoring)
return window.location.replace("/login");
return <Component {...props} {...rest} />;
};
return <Route {...rest} render={renderComponent} />;
@ -40,9 +98,10 @@ export const PublicRoute = ({ component: Component, ...rest }) => {
export default inject(({ auth }) => {
const { settingsStore, isAuthenticated, isLoaded } = auth;
const { wizardCompleted, personal } = settingsStore;
const { wizardCompleted, personal, tenantStatus } = settingsStore;
return {
tenantStatus,
wizardCompleted,
isAuthenticated,
isLoaded,

View File

@ -1,13 +1,7 @@
import styled from "styled-components";
import { Base } from "@docspace/components/themes";
const StyledBodyPreparationPortal = styled.div`
margin-bottom: 24px;
width: 100%;
max-width: ${(props) => (props.errorMessage ? "560px" : "480px")};
box-sizing: border-box;
align-items: center;
const StyledPreparationPortalProgress = styled.div`
.preparation-portal_progress {
display: flex;
margin-bottom: 16px;
@ -40,13 +34,8 @@ const StyledBodyPreparationPortal = styled.div`
left: calc(50% - 9px);
}
}
.preparation-portal_text {
text-align: center;
color: ${(props) => props.theme.text.disableColor};
}
`;
StyledBodyPreparationPortal.defaultProps = { theme: Base };
StyledPreparationPortalProgress.defaultProps = { theme: Base };
export default StyledBodyPreparationPortal;
export default StyledPreparationPortalProgress;

View File

@ -64,18 +64,23 @@ class AuthStore {
}
if (this.isAuthenticated && !skipRequest) {
requests.push(
this.currentQuotaStore.init(),
this.currentTariffStatusStore.init()
);
!this.settingsStore.passwordSettings &&
if (this.settingsStore.tenantStatus !== TenantStatus.PortalRestore) {
requests.push(
this.settingsStore.getPortalPasswordSettings(),
this.settingsStore.getAdditionalResources(),
this.settingsStore.getCompanyInfoSettings(),
this.settingsStore.getWhiteLabelLogoUrls()
this.currentQuotaStore.init(),
this.currentTariffStatusStore.init()
);
}
if (!this.settingsStore.passwordSettings) {
if (this.settingsStore.tenantStatus !== TenantStatus.PortalRestore) {
requests.push(
this.settingsStore.getPortalPasswordSettings(),
this.settingsStore.getAdditionalResources(),
this.settingsStore.getCompanyInfoSettings()
);
}
requests.push(this.settingsStore.getWhiteLabelLogoUrls());
}
}
return Promise.all(requests);
@ -95,10 +100,8 @@ class AuthStore {
get isLoaded() {
let success = false;
if (this.isAuthenticated) {
success =
(this.userStore.isLoaded && this.settingsStore.isLoaded) ||
this.settingsStore.tenantStatus === TenantStatus.PortalRestore;
success = this.userStore.isLoaded && this.settingsStore.isLoaded;
success && this.setLanguage();
} else {
success = this.settingsStore.isLoaded;
@ -231,8 +234,8 @@ class AuthStore {
get isAuthenticated() {
return (
(this.settingsStore.isLoaded && !!this.settingsStore.socketUrl) || //this.userStore.isAuthenticated ||
this.settingsStore.tenantStatus === TenantStatus.PortalRestore
this.settingsStore.isLoaded && !!this.settingsStore.socketUrl
//|| //this.userStore.isAuthenticated
);
}

View File

@ -257,12 +257,10 @@ class SettingsStore {
requests.push(
this.getPortalSettings(),
this.getAppearanceTheme(),
this.getWhiteLabelLogoUrls()
this.getWhiteLabelLogoUrls(),
this.getBuildVersionInfo()
);
this.tenantStatus !== TenantStatus.PortalRestore &&
requests.push(this.getBuildVersionInfo());
await Promise.all(requests);
this.setIsLoading(false);

View File

@ -113,6 +113,7 @@ class ComboBox extends React.Component {
withBackground,
advancedOptionsCount,
isMobileView,
withoutPadding,
} = this.props;
const { tabIndex, ...props } = this.props;
@ -168,6 +169,7 @@ class ComboBox extends React.Component {
toggleAction={toggleAction}
isOpen={isOpen}
disableMobileView={disableMobileView}
withoutPadding={withoutPadding}
{...props}
>
<ComboButton
@ -315,6 +317,7 @@ ComboBox.propTypes = {
/**Count of advanced options */
advancedOptionsCount: PropTypes.number,
tabIndex: PropTypes.number,
withoutPadding: PropTypes.bool,
};
ComboBox.defaultProps = {
@ -335,6 +338,7 @@ ComboBox.defaultProps = {
isExternalLink: false,
modernView: false,
tabIndex: -1,
withoutPadding: false,
};
export default ComboBox;

View File

@ -13,7 +13,7 @@ const StyledComboBox = styled.div`
position: relative;
outline: 0;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding: 4px 0;
padding: ${(props) => (props.withoutPadding ? "0" : "4px 0")};
${(props) =>
props.isOpen &&

View File

@ -73,6 +73,7 @@ const Container = styled.div`
max-width: ${(props) => (props.maxwidth ? props.maxwidth : "293px")};
color: ${(props) =>
props.color ? props.color : props.theme.fieldContainer.errorLabel.color};
padding-top: 4px;
}
${(props) =>
props.vertical ? getVerticalCss() : getHorizontalCss(props.maxLabelWidth)}

View File

@ -42,7 +42,11 @@ const ModalDialog = ({
isScrollLocked,
containerVisible,
isDoubleFooterLine,
isCloseable,
}) => {
const onCloseEvent = () => {
isCloseable && onClose();
};
const [currentDisplayType, setCurrentDisplayType] = useState(
getCurrentDisplayType(displayType, displayTypeDetailed)
);
@ -59,7 +63,7 @@ const ModalDialog = ({
const onSwipe = (e) => setModalSwipeOffset(handleTouchMove(e, onClose));
const onSwipeEnd = () => setModalSwipeOffset(0);
const onKeyPress = (e) => {
if ((e.key === "Esc" || e.key === "Escape") && visible) onClose();
if ((e.key === "Esc" || e.key === "Escape") && visible) onCloseEvent();
};
window.addEventListener("resize", onResize);
@ -102,7 +106,7 @@ const ModalDialog = ({
autoMaxHeight={autoMaxHeight}
autoMaxWidth={autoMaxWidth}
withFooterBorder={withFooterBorder}
onClose={onClose}
onClose={onCloseEvent}
isLoading={isLoading}
header={header}
body={body}
@ -111,6 +115,7 @@ const ModalDialog = ({
visible={visible}
modalSwipeOffset={modalSwipeOffset}
containerVisible={containerVisible}
isCloseable={isCloseable}
/>
}
/>
@ -137,6 +142,9 @@ ModalDialog.propTypes = {
/** Show loader in body */
isLoading: PropTypes.bool,
/**The displayed dialog can be closed or not */
isCloseable: PropTypes.bool,
/** **`MODAL-ONLY`**
Sets `width: 520px` and `max-hight: 400px`*/
@ -180,7 +188,7 @@ ModalDialog.defaultProps = {
zIndex: 310,
isLarge: false,
isLoading: false,
withoutCloseButton: false,
isCloseable: true,
withBodyScroll: false,
withFooterBorder: false,
containerVisible: false,

View File

@ -39,6 +39,7 @@ const Modal = ({
modalSwipeOffset,
containerVisible,
isDoubleFooterLine,
isCloseable,
}) => {
const headerComponent = header ? header.props.children : null;
const bodyComponent = body ? body.props.children : null;
@ -82,11 +83,13 @@ const Modal = ({
autoMaxWidth={autoMaxWidth}
modalSwipeOffset={modalSwipeOffset}
>
<CloseButton
currentDisplayType={currentDisplayType}
onClick={onClose}
id={id}
/>
{isCloseable && (
<CloseButton
currentDisplayType={currentDisplayType}
onClick={onClose}
id={id}
/>
)}
{isLoading ? (
currentDisplayType === "modal" ? (
<Loaders.DialogLoader

View File

@ -824,7 +824,7 @@ const Base = {
middle: "8px 12px",
big: "8px 16px",
huge: "8px 20px",
large: "11px 15px",
large: "11px 12px",
},
},
@ -3100,6 +3100,8 @@ const Base = {
backgroundColor: "#F3F4F4",
colorPercentSmall: "#333333",
colorPercentBig: "#FFFFFF",
errorTextColor: "#F21C0E",
descriptionTextColor: "#A3A9AE",
},
codeInput: {

View File

@ -816,7 +816,7 @@ const Dark = {
middle: "8px 12px",
big: "8px 16px",
huge: "8px 20px",
large: "11px 15px",
large: "11px 12px",
},
},
@ -3099,6 +3099,8 @@ const Dark = {
backgroundColor: "#282828",
colorPercentSmall: "#FFFFFF",
colorPercentBig: "#333333",
errorTextColor: "#E06451",
descriptionTextColor: "#858585",
},
codeInput: {

View File

@ -629,13 +629,18 @@ function Editor({
}
};
const errorMessage = () => {
if (typeof error !== "string") return error.errorMessage;
if (error === "restore-backup") return t("Common:PreparationPortalTitle");
return error;
};
const additionalComponents =
error && !error?.unAuthorized ? (
<ErrorContainerBody
headerText={t("Common:Error")}
customizedBodyText={
typeof error === "string" ? error : error.errorMessage
}
customizedBodyText={errorMessage()}
/>
) : (
<>

View File

@ -44,7 +44,7 @@ const withDialogs = (WrappedComponent) => {
const { socketHelper } = window.authStore.auth.settingsStore;
socketHelper.emit({
command: "subscribe",
data: { roomParts: "backup-restore" }
data: { roomParts: "backup-restore" },
});
socketHelper.on("restore-backup", () => {
const message = t("Common:PreparationPortalTitle");
@ -52,7 +52,7 @@ const withDialogs = (WrappedComponent) => {
typeof window !== "undefined" &&
window.DocEditor?.instances[EDITOR_ID];
docEditor?.showMessage(message);
docEditor?.denyEditingRights(message);
});
};

View File

@ -13,6 +13,7 @@ import {
getSettingsFiles,
// getShareFiles,
} from "@docspace/common/api/files";
import { TenantStatus } from "@docspace/common/constants";
import { getLogoFromPath } from "@docspace/common/utils";
@ -56,20 +57,25 @@ export const initDocEditor = async (req) => {
const view = url.indexOf("action=view") !== -1;
const fileVersion = version || null;
[
user,
settings,
filesSettings,
versionInfo,
appearanceTheme,
logoUrls,
] = await Promise.all([
const baseSettings = [
getUser(),
getSettings(),
getSettingsFiles(),
getBuildVersion(),
getAppearanceTheme(),
getLogoUrls(),
];
[user, settings, appearanceTheme, logoUrls] = await Promise.all(
baseSettings
);
if (settings.tenantStatus === TenantStatus.PortalRestore) {
error = "restore-backup";
return { error, logoUrls };
}
[filesSettings, versionInfo] = await Promise.all([
getSettingsFiles(),
getBuildVersion(),
]);
const successAuth = !!user;

View File

@ -1,4 +1,4 @@
import React, { useState, useCallback } from "react";
import React, { useState, useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
import { ButtonsWrapper, LoginFormWrapper } from "./StyledLogin";
@ -26,7 +26,7 @@ import { getBgPattern } from "@docspace/common/utils";
import useIsomorphicLayoutEffect from "../hooks/useIsomorphicLayoutEffect";
import { getLogoFromPath } from "@docspace/common/utils";
import { useThemeDetector } from "@docspace/common/utils/useThemeDetector";
import { TenantStatus } from "@docspace/common/constants";
interface ILoginProps extends IInitialState {
isDesktopEditor?: boolean;
}
@ -42,13 +42,20 @@ const Login: React.FC<ILoginProps> = ({
setTheme,
logoUrls,
}) => {
const isRestoringPortal =
portalSettings.tenantStatus === TenantStatus.PortalRestore;
useEffect(() => {
isRestoringPortal && window.location.replace("/preparation-portal");
}, []);
const [isLoading, setIsLoading] = useState(false);
const [moreAuthVisible, setMoreAuthVisible] = useState(false);
const [recoverDialogVisible, setRecoverDialogVisible] = useState(false);
const { enabledJoin, greetingSettings, enableAdmMess } = portalSettings;
const { ssoLabel, ssoUrl } = capabilities;
const ssoLabel = capabilities?.ssoLabel;
const ssoUrl = capabilities?.ssoUrl;
const { t } = useTranslation(["Login", "Common"]);
const mounted = useMounted();
const systemTheme = typeof window !== "undefined" && useThemeDetector();
@ -191,6 +198,7 @@ const Login: React.FC<ILoginProps> = ({
: getLogoFromPath(logo.path.light).replace("client/", "login/");
if (!mounted) return <></>;
if (isRestoringPortal) return <></>;
return (
<LoginFormWrapper

View File

@ -6,9 +6,10 @@ import {
getAuthProviders,
getCapabilities,
getAppearanceTheme,
getLogoUrls
getLogoUrls,
} from "@docspace/common/api/settings";
import { checkIsAuthenticated } from "@docspace/common/api/user";
import { TenantStatus } from "@docspace/common/constants";
export const getAssets = (): assetsType => {
const manifest = fs.readFileSync(
@ -61,23 +62,25 @@ export const getInitialState = async (
isAuth: any,
logoUrls: any;
[
portalSettings,
buildInfo,
providers,
capabilities,
availableThemes,
isAuth,
logoUrls
] = await Promise.all([
const baseSettings = [
getSettings(),
getBuildVersion(),
getAppearanceTheme(),
getLogoUrls(),
];
const settings = [
getAuthProviders(),
getCapabilities(),
getAppearanceTheme(),
checkIsAuthenticated(),
getLogoUrls()
]);
];
[portalSettings, buildInfo, availableThemes, logoUrls] = await Promise.all(
baseSettings
);
if (portalSettings.tenantStatus !== TenantStatus.PortalRestore)
[providers, capabilities, isAuth] = await Promise.all(settings);
const currentColorScheme = availableThemes.themes.find((theme) => {
return availableThemes.selected === theme.id;
@ -91,7 +94,7 @@ export const getInitialState = async (
match: query,
currentColorScheme,
isAuth,
logoUrls
logoUrls,
};
return initialState;

View File

@ -62,10 +62,9 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
private readonly bool _isEmptyTrash;
private readonly IDictionary<string, StringValues> _headers;
private readonly ThumbnailSettings _thumbnailSettings;
private readonly SocketManager _socketManager;
public FileDeleteOperation(
IServiceProvider serviceProvider, FileDeleteOperationData<T> fileOperationData, ThumbnailSettings thumbnailSettings, SocketManager socketManager)
IServiceProvider serviceProvider, FileDeleteOperationData<T> fileOperationData, ThumbnailSettings thumbnailSettings)
: base(serviceProvider, fileOperationData)
{
_ignoreException = fileOperationData.IgnoreException;
@ -73,7 +72,6 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
_headers = fileOperationData.Headers;
_isEmptyTrash = fileOperationData.IsEmptyTrash;
_thumbnailSettings = thumbnailSettings;
_socketManager = socketManager;
this[OpType] = (int)FileOperationType.Delete;
}
@ -81,6 +79,8 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
{
var folderDao = scope.ServiceProvider.GetService<IFolderDao<int>>();
var messageService = scope.ServiceProvider.GetService<MessageService>();
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
tenantManager.SetCurrentTenant(CurrentTenant);
_trashId = await folderDao.GetFolderIDTrashAsync(true);
Folder<T> root = null;
@ -112,6 +112,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
private async Task DeleteFoldersAsync(IEnumerable<T> folderIds, IServiceScope scope, bool isNeedSendActions = false, bool checkPermissions = true)
{
var scopeClass = scope.ServiceProvider.GetService<FileDeleteOperationScope>();
var socketManager = scope.ServiceProvider.GetService<SocketManager>();
var (fileMarker, filesMessageService, roomLogoManager) = scopeClass;
foreach (var folderId in folderIds)
{
@ -154,7 +155,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
if (providerInfo.FolderId != null)
{
var room = await roomLogoManager.DeleteAsync(providerInfo.FolderId, checkPermissions);
await _socketManager.UpdateFolderAsync(room);
await socketManager.UpdateFolderAsync(room);
}
}
@ -183,7 +184,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
if (isRoom)
{
var room = await roomLogoManager.DeleteAsync(folder.Id, checkPermissions);
await _socketManager.UpdateFolderAsync(room);
await socketManager.UpdateFolderAsync(room);
}
await FolderDao.DeleteFolderAsync(folder.Id);
@ -193,7 +194,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
await ProviderDao.RemoveProviderInfoAsync(folder.ProviderId);
}
await _socketManager.DeleteFolder(folder);
await socketManager.DeleteFolder(folder);
filesMessageService.Send(folder, _headers, isRoom ? MessageAction.RoomDeleted : MessageAction.FolderDeleted, folder.Title);
@ -215,7 +216,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
if (isRoom)
{
var room = await roomLogoManager.DeleteAsync(folder.Id, checkPermissions);
await _socketManager.UpdateFolderAsync(room);
await socketManager.UpdateFolderAsync(room);
}
await FolderDao.DeleteFolderAsync(folder.Id);
@ -225,7 +226,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
await ProviderDao.RemoveProviderInfoAsync(folder.ProviderId);
}
await _socketManager.DeleteFolder(folder);
await socketManager.DeleteFolder(folder);
if (isNeedSendActions)
{

View File

@ -81,10 +81,9 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
private readonly FileConflictResolveType _resolveType;
private readonly IDictionary<string, StringValues> _headers;
private readonly ThumbnailSettings _thumbnailSettings;
private readonly SocketManager _socketManager;
private readonly Dictionary<T, Folder<T>> _parentRooms = new Dictionary<T, Folder<T>>();
public FileMoveCopyOperation(IServiceProvider serviceProvider, FileMoveCopyOperationData<T> data, ThumbnailSettings thumbnailSettings, SocketManager socketManager)
public FileMoveCopyOperation(IServiceProvider serviceProvider, FileMoveCopyOperationData<T> data, ThumbnailSettings thumbnailSettings)
: base(serviceProvider, data)
{
_daoFolderId = data.DaoFolderId;
@ -94,7 +93,6 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
_headers = data.Headers;
_thumbnailSettings = thumbnailSettings;
_socketManager = socketManager;
this[OpType] = (int)(_copy ? FileOperationType.Copy : FileOperationType.Move);
}
@ -226,6 +224,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
var (filesMessageService, fileMarker, _, _, _) = scopeClass;
var folderDao = scope.ServiceProvider.GetService<IFolderDao<TTo>>();
var countRoomChecker = scope.ServiceProvider.GetRequiredService<CountRoomChecker>();
var socketManager = scope.ServiceProvider.GetService<SocketManager>();
var toFolderId = toFolder.Id;
var isToFolder = Equals(toFolderId, _daoFolderId);
@ -332,7 +331,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
await FolderDao.DeleteFolderAsync(folder.Id);
await _socketManager.DeleteFolder(folder);
await socketManager.DeleteFolder(folder);
if (ProcessedFolder(folderId))
{

View File

@ -31,7 +31,6 @@ public class FileOperationsManager
{
public const string CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME = "files_operation";
private readonly ThumbnailSettings _thumbnailSettings;
private readonly SocketManager _socketManager;
private readonly DistributedTaskQueue _tasks;
private readonly TempStream _tempStream;
private readonly IServiceProvider _serviceProvider;
@ -40,14 +39,12 @@ public class FileOperationsManager
TempStream tempStream,
IDistributedTaskQueueFactory queueFactory,
IServiceProvider serviceProvider,
ThumbnailSettings thumbnailSettings,
SocketManager socketManager)
ThumbnailSettings thumbnailSettings)
{
_tasks = queueFactory.CreateQueue(CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME);
_tempStream = tempStream;
_serviceProvider = serviceProvider;
_thumbnailSettings = thumbnailSettings;
_socketManager = socketManager;
}
public List<FileOperationResult> GetOperationResults(Guid userId)
@ -143,8 +140,8 @@ public class FileOperationsManager
var (folderIntIds, folderStringIds) = GetIds(folders);
var (fileIntIds, fileStringIds) = GetIds(files);
var op1 = new FileMoveCopyOperation<int>(_serviceProvider, new FileMoveCopyOperationData<int>(folderIntIds, fileIntIds, tenant, destFolderId, copy, resolveType, holdResult, headers), _thumbnailSettings, _socketManager);
var op2 = new FileMoveCopyOperation<string>(_serviceProvider, new FileMoveCopyOperationData<string>(folderStringIds, fileStringIds, tenant, destFolderId, copy, resolveType, holdResult, headers), _thumbnailSettings, _socketManager);
var op1 = new FileMoveCopyOperation<int>(_serviceProvider, new FileMoveCopyOperationData<int>(folderIntIds, fileIntIds, tenant, destFolderId, copy, resolveType, holdResult, headers), _thumbnailSettings);
var op2 = new FileMoveCopyOperation<string>(_serviceProvider, new FileMoveCopyOperationData<string>(folderStringIds, fileStringIds, tenant, destFolderId, copy, resolveType, holdResult, headers), _thumbnailSettings);
var op = new FileMoveCopyOperation(_serviceProvider, op2, op1);
return QueueTask(userId, op);
@ -152,7 +149,7 @@ public class FileOperationsManager
public List<FileOperationResult> Delete<T>(Guid userId, Tenant tenant, IEnumerable<T> folders, IEnumerable<T> files, bool ignoreException, bool holdResult, bool immediately, IDictionary<string, StringValues> headers, bool isEmptyTrash = false)
{
var op = new FileDeleteOperation<T>(_serviceProvider, new FileDeleteOperationData<T>(folders, files, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings, _socketManager);
var op = new FileDeleteOperation<T>(_serviceProvider, new FileDeleteOperationData<T>(folders, files, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings);
return QueueTask(userId, op);
}
@ -161,8 +158,8 @@ public class FileOperationsManager
var (folderIntIds, folderStringIds) = GetIds(folders);
var (fileIntIds, fileStringIds) = GetIds(files);
var op1 = new FileDeleteOperation<int>(_serviceProvider, new FileDeleteOperationData<int>(folderIntIds, fileIntIds, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings, _socketManager);
var op2 = new FileDeleteOperation<string>(_serviceProvider, new FileDeleteOperationData<string>(folderStringIds, fileStringIds, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings, _socketManager);
var op1 = new FileDeleteOperation<int>(_serviceProvider, new FileDeleteOperationData<int>(folderIntIds, fileIntIds, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings);
var op2 = new FileDeleteOperation<string>(_serviceProvider, new FileDeleteOperationData<string>(folderStringIds, fileStringIds, tenant, holdResult, ignoreException, immediately, headers, isEmptyTrash), _thumbnailSettings);
var op = new FileDeleteOperation(_serviceProvider, op2, op1);
return QueueTask(userId, op);