Merge branch 'develop' into feature/active-sessions

This commit is contained in:
Elyor Djalilov 2024-05-06 17:32:29 +05:00
commit e9032368ec
312 changed files with 3297 additions and 5999 deletions

View File

@ -8,7 +8,7 @@
"name": "🚀 @docspace/client",
"path": "packages/client",
},
{
{
"name": "🔑 @docspace/login",
"path": "packages/login",
},

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "الغرفة غير مثبتة",
"SearchByContent": "ابحث من خلال محتويات الملف",
"SendByEmail": "ارسل بالبريد الإلكترونى",
"Share": "شارك",
"ShareFolder": "مشاركة المجلد",
"ShareFolderDescription": "سيتم إنشاء غرفة جديدة وسيتم نسخ جميع محتويات المجلد المحدد هناك. وبعد ذلك، يمكنك دعوة مستخدمين آخرين للتعاون في الملفات الموجودة داخل الغرفة.",
"ShareRoom": "مشاركة الغرفة",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Otaq sabitləmədən çıxarıldı",
"SearchByContent": "Fayl məzmununa görə axtarın",
"SendByEmail": "Elektron poçt vasitəsi ilə göndər",
"Share": "Paylaş",
"ShareFolder": "Qovluğu paylaşın",
"ShareFolderDescription": "Yeni otaq yaradılacaq və seçilmiş qovluğun bütün məzmunu oraya nüsxələnəcək. Daha sonra digər istifadəçiləri otaqdakı fayllar üzərində əməkdaşlığa dəvət edə bilərsiniz.",
"ShareRoom": "Otağı paylaşın",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Стаята е освободена",
"SearchByContent": "Търсене по съдържание на файла",
"SendByEmail": "Изпратен по имейл",
"Share": "Сподели",
"ShareFolder": "Папка за споделяне",
"ShareFolderDescription": "Ще бъде създадена нова стая и цялото съдържание на избраната папка ще бъде копирано там. След това можете да поканите други потребители да си сътрудничат по файловете в стаята.",
"ShareRoom": "Сподели стая",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Místnost odepnuta",
"SearchByContent": "Vyhledávání podle obsahu souboru",
"SendByEmail": "Odeslat emailem",
"Share": "Sdílet",
"ShareFolder": "Sdílet složku",
"ShareFolderDescription": "Vytvoří se nová místnost a veškerý obsah vybrané složky se do ní zkopíruje. Poté můžete ke spolupráci na souborech v místnosti přizvat další uživatele.",
"ShareRoom": "Sdílet místnost",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Raum nicht mehr angeheftet",
"SearchByContent": "Suche nach Dateiinhalten",
"SendByEmail": "Per E-Mail senden",
"Share": "Freigeben",
"ShareFolder": "Ordner freigeben",
"ShareFolderDescription": "Es wird ein neuer Raum erstellt und alle Inhalte des ausgewählten Ordners werden dorthin kopiert. Anschließend können Sie andere Benutzer zur Zusammenarbeit an den Dateien in einem Raum einladen.",
"ShareRoom": "Raum freigeben",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Το δωμάτιο ξεκαρφιτσώθηκε",
"SearchByContent": "Αναζήτηση με βάση τα περιεχόμενα του αρχείου",
"SendByEmail": "Αποστολή μέσω email",
"Share": "Κοινή χρήση",
"ShareFolder": "Φάκελος κοινής χρήσης",
"ShareFolderDescription": "Θα δημιουργηθεί ένα νέο δωμάτιο και όλα τα περιεχόμενα του επιλεγμένου φακέλου θα αντιγραφούν εκεί. Στη συνέχεια, μπορείτε να προσκαλέσετε άλλους χρήστες να συνεργαστούν στα αρχεία εντός ενός δωματίου.",
"ShareRoom": "Κοινή χρήση δωματίου",

View File

@ -1,4 +1,5 @@
{
"ActivationRequired": "activation required",
"ChooseRoomType": "Choose room type",
"CollaborationRoomDescription": "Collaborate on one or multiple documents with your team",
"CollaborationRoomTitle": "Collaboration room",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Room unpinned",
"SearchByContent": "Search by file contents",
"SendByEmail": "Send by email",
"Share": "Share",
"ShareFolder": "Share folder",
"ShareFolderDescription": "A new room will be created and all the contents of the selected folder will be copied there. Afterward, you can invite other users to collaborate on the files within a room.",
"ShareRoom": "Share room",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Se ha desanclado la sala",
"SearchByContent": "Buscar por contenido del archivo",
"SendByEmail": "Enviar por correo electrónico",
"Share": "Compartir",
"ShareFolder": "Compartir carpeta",
"ShareFolderDescription": "Se creará una nueva sala y allí se copiará todo el contenido de la carpeta seleccionada. Luego, puede invitar a otros usuarios a colaborar en los archivos dentro de una sala.",
"ShareRoom": "Compartir sala",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Huone irrotettu",
"SearchByContent": "Etsi kansion sisällön mukaan",
"SendByEmail": "Lähetä sähköpostitse",
"Share": "Jaa",
"ShareFolder": "Jaa kansio",
"ShareFolderDescription": "Uusi huone luodaan ja koko valitun kansion sisältö kopioidaan tähän. Sen jälkeen voit kutsua käyttäjiä huoneeseen tekemään yhteistyötä tiedostoilla.",
"ShareRoom": "Jaa huone",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Salle désépinglée",
"SearchByContent": "Recherche par contenu de fichier",
"SendByEmail": "Envoyer par émail",
"Share": "Partager",
"ShareFolder": "Partager le dossier",
"ShareFolderDescription": "Une nouvelle salle sera créée et tout le contenu du dossier sélectionné y sera copié. Vous pouvez ensuite inviter d'autres utilisateurs à collaborer sur les fichiers d'une salle.",
"ShareRoom": "Partager la salle",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Սենյակն ապաամրացվեց",
"SearchByContent": "Որոնել ըստ ֆայլի բովանդակության",
"SendByEmail": "Ուղարկել էլփոստով",
"Share": "Համօգտագործել",
"ShareFolder": "Համօգտագործել թղթապանակը",
"ShareFolderDescription": "Կստեղծվի նոր սենյակ, և ընտրված թղթապանակի ամբողջ բովանդակությունը կպատճենվի այնտեղ: Այնուհետև կարող եք հրավիրել այլ օգտատերերի՝ համագործակցելու սենյակում գտնվող ֆայլերի շուրջ:",
"ShareRoom": "Կիսվեք սենյակով",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "La stanza è stata sbloccata",
"SearchByContent": "Cerca per contenuto del file",
"SendByEmail": "Invia per email",
"Share": "Condividi",
"ShareFolder": "Condividi cartella",
"ShareFolderDescription": "Verrà creata una nuova stanza e tutto il contenuto della cartella selezionata verrà copiato lì. Successivamente, puoi invitare altri utenti a collaborare sui file all'interno di una stanza.",
"ShareRoom": "Condividi stanza",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "ルームの固定を削除されました",
"SearchByContent": "ファイルの内容で検索",
"SendByEmail": "メールで送信",
"Share": "シェア",
"ShareFolder": "共有フォルダ",
"ShareFolderDescription": "新しいルームが作成され、選択したフォルダのすべての内容がそこにコピーされます。その後、あなたはルーム内のファイル上で共同作業を行うために他のユーザーを招待することができます。",
"ShareRoom": "共有ルーム",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "방의 고정이 해제되었습니다",
"SearchByContent": "파일 콘텐츠로 검색",
"SendByEmail": "이메일로 전송",
"Share": "공유",
"ShareFolder": "폴더 공유",
"ShareFolderDescription": "새 방이 생성되고 선택한 폴더의 모든 내용이 해당 방에 복사됩니다. 그 다음 다른 사용자를 초대하여 방에 있는 파일을 공동작업할 수 있습니다.",
"ShareRoom": "방 공유",

View File

@ -99,7 +99,6 @@
"RoomUnpinned": "ຖອດປັກໝຸດຫ້ອງແລ້ວ",
"SearchByContent": "ຄົ້ນ​ຫາ​ໂດຍ​ເນື້ອ​ໃນ​ໄຟລ​໌​",
"SendByEmail": "ສົ່ງຜ່ານອີເມວ",
"Share": "ແບ່ງປັນ",
"ShowVersionHistory": "ສະແດງປະຫວັດສະບັບ",
"Spreadsheet": "ຄຳນວນ",
"TooltipElementCopyMessage": "ຄັດລອກ {{element}}",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Telpa ir atbloķēta",
"SearchByContent": "Meklēt pēc faila satura",
"SendByEmail": "Sūtīt pa e-pastu",
"Share": "Kopīgot",
"ShareFolder": "Kopīgot mapi",
"ShareFolderDescription": "Tiks izveidota jauna telpa, un tur tiks kopēts viss atlasītās mapes saturs. Pēc tam varat uzaicināt citus lietotājus sadarboties ar telpas failiem.",
"ShareRoom": "Kopīgot telpu",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Kamer niet gepint",
"SearchByContent": "Zoeken op inhoud van bestanden",
"SendByEmail": "Verstuur per e-mail",
"Share": "Delen",
"ShareFolder": "Deel map",
"ShareFolderDescription": "Er wordt een nieuwe kamer aangemaakt en al het materiaal van de geselecteerde map wordt daarheen gekopieerd. Daarna kunt u andere gebruikers uitnodigen om samen te werken aan de bestanden binnen een kamer.",
"ShareRoom": "Kamer delen",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Pokój został odpięty",
"SearchByContent": "Wyszukaj wg zawartości pliku",
"SendByEmail": "Wyślij e-mailem",
"Share": "Udostępnij",
"ShareFolder": "Udostępnij katalog",
"ShareFolderDescription": "Zostanie utworzony nowy pokój, a cała zawartość wybranego katalogu zostanie do niego skopiowana. Następnie możesz zaprosić innych użytkowników do współpracy nad plikami w pokoju.",
"ShareRoom": "Udostępnij pokój",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Sala desafixada",
"SearchByContent": "Pesquise pelo conteúdo do arquivo",
"SendByEmail": "Enviar por e-mail",
"Share": "Compartilhar",
"ShareFolder": "Compartilhar pasta",
"ShareFolderDescription": "Uma nova sala será criada e todo o conteúdo da pasta selecionada será copiado para lá. Depois, você pode convidar outros usuários para colaborar nos arquivos de uma sala.",
"ShareRoom": "Sala compartilhado",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Sala não afixada",
"SearchByContent": "Procurar pelo conteúdo do ficheiro",
"SendByEmail": "Enviar por e-mail",
"Share": "Partilhar",
"ShareFolder": "Compartilhar pasta",
"ShareFolderDescription": "Uma nova sala será criada e todo o conteúdo da pasta selecionada será copiado para lá. Depois, você pode convidar outros usuários para colaborar nos arquivos de uma sala.",
"ShareRoom": "Sala compartilhada",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Fixarea sălii anulată",
"SearchByContent": "Căutare după conținutul fișierelor",
"SendByEmail": "Trimite prin email",
"Share": "Partajează",
"ShareFolder": "Partajare folder",
"ShareFolderDescription": "O sală nouă va fi creată și tot conținutul folderului selectat va fi copiat acolo. Ulterior, puteți invita alți utilizatori să colaboreze la fișiere în cadrul unei săli.",
"ShareRoom": "Partajare sală",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Комната откреплена",
"SearchByContent": "Поиск по содержимому файла",
"SendByEmail": "Отправить по почте",
"Share": "Доступ",
"ShareFolder": "Предоставить доступ к папке",
"ShareFolderDescription": "Будет создана новая комната, и все содержимое выбранной папки будет скопировано в нее. После этого вы сможете пригласить других пользователей для совместной работы над файлами в комнате.",
"ShareRoom": "Предоставить доступ к комнате",

View File

@ -90,7 +90,6 @@
"RoomUnpinned": "කාමරය ගැළවිණි",
"SearchByContent": "ගොනු අන්තර්ගත අනුව සොයන්න",
"SendByEmail": "වි-තැපෑලෙන් යවන්න",
"Share": "බෙදාගන්න",
"ShowVersionHistory": "අනුවාද ඉතිහාසය පෙන්වන්න",
"Spreadsheet": "පැතුරුම්පත",
"TooltipElementCopyMessage": "{{element}} පිටපත් කරන්න",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Miestnosť je odopnutá",
"SearchByContent": "Vyhľadávanie podľa obsahu súboru",
"SendByEmail": "Poslať e-mailom",
"Share": "Zdieľať",
"ShareFolder": "Zdieľať priečinok",
"ShareFolderDescription": "Bude vytvorená nová miestnosť a skopíruje sa do nej celý obsah vybraného priečinka. Následne môžete pozvať ďalších používateľov na spoluprácu na súboroch v danej miestnosti.",
"ShareRoom": "Zdieľať miestnosť",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Soba odpeta",
"SearchByContent": "Išči po vsebini datotek",
"SendByEmail": "Pošlji po email",
"Share": "Deli",
"ShareFolder": "Deljena mapa",
"ShareFolderDescription": "Ustvarila se bo nova soba in vanjo bo kopirana vsa vsebina izbrane mape. Nato lahko druge uporabnike povabite k sodelovanju pri datotekah v sobi.",
"ShareRoom": "Deli sobo",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Sobe otkačene",
"SearchByContent": "Pretražuj po sadržaju datoteke",
"SendByEmail": "Pošalji putem email-a",
"Share": "Deli",
"ShareFolder": "Deli folder",
"ShareFolderDescription": "Nova soba će biti kreirana i sav sadržaj izabranog foldera će biti kopiran tamo. Nakon toga, možete pozvati druge korisnike da sarađuju na datotekama u sobi.",
"ShareRoom": "Deli sobu",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Odanın sabitlemesi kaldırıldı",
"SearchByContent": "Dosya içeriğine göre ara",
"SendByEmail": "E-posta ile gönder",
"Share": "Paylaş",
"ShareFolder": "Klasörü paylaş",
"ShareFolderDescription": "Yeni bir oda oluşturulacak ve seçilen klasörün tüm içeriği buraya kopyalanacaktır. Daha sonra, diğer kullanıcıları bir oda içindeki dosyalar üzerinde iş birliği yapmaya davet edebilirsiniz.",
"ShareRoom": "Odayı paylaş",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Кімнату відкріплено",
"SearchByContent": "Пошук за вмістом файлу",
"SendByEmail": "Надіслати ел. поштою",
"Share": "Надати спільний доступ",
"ShareFolder": "Спільний доступ до папки",
"ShareFolderDescription": "Буде створено нову кімнату, і весь вміст вибраної папки буде скопійовано туди. Після цього ви зможете запрошувати інших користувачів до спільної роботи над файлами в кімнаті.",
"ShareRoom": "Поділитися кімнатою",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "Phòng đã hủy ghim",
"SearchByContent": "Tìm kiếm theo nội dung tập tin",
"SendByEmail": "Gửi qua email",
"Share": "Chia sẻ",
"ShareFolder": "Chia sẻ thư mục",
"ShareFolderDescription": "Một phòng mới sẽ được tạo và tất cả nội dung của thư mục đã chọn sẽ được sao chép vào đó. Sau đó, bạn có thể mời những người dùng khác cộng tác trên các tập tin trong phòng.",
"ShareRoom": "Chia sẻ phòng",

View File

@ -158,7 +158,6 @@
"RoomUnpinned": "房间已取消置顶",
"SearchByContent": "按文件内容搜索",
"SendByEmail": "通过邮件发送",
"Share": "分享",
"ShareFolder": "共享文件夹",
"ShareFolderDescription": "系统会创建一个新房间,并将所选文件夹的所有内容复制到其中。之后,您可以邀请其他用户在房间内协作处理文件。",
"ShareRoom": "分享房间",

View File

@ -36,6 +36,8 @@ import ErrorBoundary from "./components/ErrorBoundaryWrapper";
import store from "client/store";
import i18n from "./i18n";
import "@docspace/shared/polyfills/broadcastchannel";
import "@docspace/shared/styles/custom.scss";
import router from "./router";

View File

@ -33,11 +33,15 @@ import {
ShareAccessRights,
} from "@docspace/shared/enums";
//import { combineUrl } from "@docspace/shared/utils/combineUrl";
import { getCorrectDate, getCookie } from "@docspace/shared/utils";
import {
getCorrectDate,
getCookie,
getTitleWithoutExtension,
} from "@docspace/shared/utils";
import { LANGUAGE } from "@docspace/shared/constants";
import config from "PACKAGE_FILE";
//import EditingWrapperComponent from "../components/EditingWrapperComponent";
import { getTitleWithoutExtension } from "SRC_DIR/helpers/filesUtils";
//import { getDefaultFileName } from "@docspace/client/src/helpers/filesUtils";
//import ItemIcon from "../components/ItemIcon";

View File

@ -34,10 +34,10 @@ import { combineUrl } from "@docspace/shared/utils/combineUrl";
import { setEncryptionAccess } from "SRC_DIR/helpers/desktop";
import config from "PACKAGE_FILE";
import { getTitleWithoutExtension } from "SRC_DIR/helpers/filesUtils";
import { getDefaultFileName } from "@docspace/client/src/helpers/filesUtils";
import Dialog from "./sub-components/Dialog";
import { getTitleWithoutExtension } from "@docspace/shared/utils";
const CreateEvent = ({
id,

View File

@ -29,7 +29,7 @@ import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import { toastr } from "@docspace/shared/components/toast";
import Dialog from "./sub-components/Dialog";
import { getTitleWithoutExtension } from "SRC_DIR/helpers/filesUtils";
import { getTitleWithoutExtension } from "@docspace/shared/utils";
const RenameEvent = ({
type,

View File

@ -45,6 +45,7 @@ import { inject, observer } from "mobx-react";
import PreparationPortalDialog from "../dialogs/PreparationPortalDialog";
import { Base } from "@docspace/shared/themes";
import { DeviceType } from "@docspace/shared/enums";
import { isPublicPreview } from "@docspace/shared/utils/common";
const StyledContainer = styled.header`
height: ${(props) => props.theme.header.height};
@ -158,7 +159,7 @@ const NavMenu = (props) => {
} = props;
const isAsideAvailable = !!asideContent;
const hideHeader = !showHeader && isFrame;
const hideHeader = (!showHeader && isFrame) || isPublicPreview();
if (currentDeviceType !== DeviceType.mobile || !isMobile() || hideHeader)
return <></>;

View File

@ -453,7 +453,6 @@ export default inject(
openConnectWindow,
fetchThirdPartyProviders,
setConnectDialogVisible,
setSelectedThirdPartyAccount,
isConnectDialogReconnect,
saveAfterReconnectOAuth,
setSaveAfterReconnectOAuth,

View File

@ -36,6 +36,8 @@ import { Base } from "@docspace/shared/themes";
import { toastr } from "@docspace/shared/components/toast";
import { ComboBox } from "@docspace/shared/components/combobox";
import ExternalLinkReactSvgUrl from "PUBLIC_DIR/images/external.link.react.svg?url";
const StyledStorageLocation = styled.div`
display: flex;
flex-direction: column;
@ -58,10 +60,49 @@ const StyledStorageLocation = styled.div`
flex-direction: row;
gap: 8px;
}
.storage-unavailable {
display: flex;
justify-content: space-between;
flex-direction: row-reverse;
.drop-down-item_icon {
svg {
path[fill] {
fill: ${(props) => props.theme.dropDownItem.disableColor};
}
path[stroke] {
stroke: ${(props) => props.theme.dropDownItem.disableColor};
}
circle[fill] {
fill: ${(props) => props.theme.dropDownItem.disableColor};
}
rect[fill] {
fill: ${(props) => props.theme.dropDownItem.disableColor};
}
}
}
color: ${(props) => props.theme.dropDownItem.disableColor};
}
`;
StyledStorageLocation.defaultProps = { theme: Base };
const services = {
GoogleDrive: "google",
Box: "box",
Dropbox: "dropbox",
OneDrive: "skydrive",
Nextcloud: "nextcloud",
kDrive: "kdrive",
ownCloud: "owncloud",
WebDav: "webdav",
};
const ThirdPartyComboBox = ({
t,
@ -84,14 +125,28 @@ const ThirdPartyComboBox = ({
isDisabled,
}) => {
const deafultSelectedItem = {
key: "length",
label:
storageLocation?.provider?.title ||
t("ThirdPartyStorageComboBoxPlaceholder"),
};
const [selectedItem, setSelectedItem] = useState(deafultSelectedItem);
const thirdparties = connectItems.map((item) => ({
...item,
title: item.category
? item.category
: ProviderKeyTranslation(item.providerKey, t),
title: ProviderKeyTranslation(item.providerKey, t),
}));
const setStorageLocaiton = (thirparty) => {
const setStorageLocaiton = (thirparty, isConnected) => {
if (!isConnected) {
window.open(
`/portal-settings/integration/third-party-services?service=${services[thirparty.id]}`,
"_blank",
);
return;
}
onChangeProvider(thirparty);
};
@ -152,19 +207,26 @@ const ThirdPartyComboBox = ({
setSaveThirdpartyResponse(null);
}, [saveThirdpartyResponse]);
const options = thirdparties.map((item) => ({
label: item.title,
title: item.title,
key: item?.category ?? item.id,
}));
const options = thirdparties
.sort((storage) => (storage.isConnected ? -1 : 1))
.map((item) => ({
label:
item.title + (item.isConnected ? "" : ` (${t("ActivationRequired")})`),
title: item.title,
key: item.id,
icon: item.isConnected ? undefined : ExternalLinkReactSvgUrl,
className: item.isConnected ? "" : "storage-unavailable",
}));
const onSelect = (elem) => {
const thirdparty = thirdparties.find((t) => {
if (t.category) return elem.key === t.category;
else return elem.key === t.id;
return elem.key === t.id;
});
thirdparty && setStorageLocaiton(thirdparty);
thirdparty && setStorageLocaiton(thirdparty, thirdparty.isConnected);
thirdparty.isConnected
? setSelectedItem(elem)
: setSelectedItem({ ...deafultSelectedItem });
};
return (
@ -172,12 +234,7 @@ const ThirdPartyComboBox = ({
<div className="set_room_params-thirdparty">
<ComboBox
className="thirdparty-combobox"
selectedOption={{
key: "length",
label:
storageLocation?.provider?.title ||
t("ThirdPartyStorageComboBoxPlaceholder"),
}}
selectedOption={selectedItem}
options={options}
scaled
withBackdrop={isMobile}

View File

@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React, { useState } from "react";
import React, { useState, useRef, useEffect } from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import { Text } from "@docspace/shared/components/text";
@ -73,7 +73,14 @@ const ThirdPartyStorage = ({
isRoomAdmin,
createNewFolderIsChecked,
onCreateFolderChange,
fetchConnectingStorages,
}) => {
const channel = useRef(new BroadcastChannel("thirdpartyActivation"));
channel.current.onmessage = (shouldRender) => {
shouldRender && fetchConnectingStorages()
};
const onChangeIsThirdparty = () => {
if (isDisabled) return;
@ -125,6 +132,10 @@ const ThirdPartyStorage = ({
storageFolderId,
});
useEffect(() => {
fetchConnectingStorages();
}, []);
return (
<StyledThirdPartyStorage>
<ToggleParam
@ -196,38 +207,7 @@ export default inject(
const thirdPartyStore = filesSettingsStore.thirdPartyStore;
const connectItems = [
thirdPartyStore.googleConnectItem,
thirdPartyStore.boxConnectItem,
thirdPartyStore.dropboxConnectItem,
thirdPartyStore.oneDriveConnectItem,
thirdPartyStore.nextCloudConnectItem && [
...thirdPartyStore.nextCloudConnectItem,
"Nextcloud",
],
thirdPartyStore.kDriveConnectItem,
thirdPartyStore.yandexConnectItem,
thirdPartyStore.ownCloudConnectItem && [
...thirdPartyStore.ownCloudConnectItem,
"ownCloud",
],
thirdPartyStore.webDavConnectItem,
thirdPartyStore.sharePointConnectItem,
]
.map(
(item) =>
item && {
id: item[0],
className: `storage_${item[0].toLowerCase()}`,
providerKey: item[0],
isOauth: item.length > 1 && item[0] !== "WebDav",
oauthHref: item.length > 1 && item[0] !== "WebDav" ? item[1] : "",
...(item[0] === "WebDav" && {
category: item[item.length - 1],
}),
},
)
.filter((item) => !!item);
const connectItems = thirdPartyStore.connectingStorages;
const { isRoomAdmin } = authStore;
@ -248,6 +228,7 @@ export default inject(
getOAuthToken,
currentColorScheme,
isRoomAdmin,
fetchConnectingStorages: thirdPartyStore.fetchConnectingStorages,
};
},
)(observer(ThirdPartyStorage));

View File

@ -226,10 +226,3 @@ export const connectedCloudsTypeIcon = (key) => {
default:
}
};
export const getTitleWithoutExtension = (item, fromTemplate) => {
const titleWithoutExst = item.title.split(".").slice(0, -1).join(".");
return titleWithoutExst && item.fileExst && !fromTemplate
? titleWithoutExst
: item.title;
};

View File

@ -130,7 +130,7 @@ const InfoPanelHeaderContent = (props) => {
if (selection?.canShare) {
personalSubmenu.unshift({
id: "info_share",
name: t("Files:Share"),
name: t("Common:Share"),
onClick: setShare,
content: null,
});

View File

@ -50,7 +50,6 @@ class FilesTableHeader extends React.Component {
columnInfoPanelStorageName,
isPublicRoom,
isFrame,
frameTableColumns,
isRecentTab,
isDefaultRoomsQuotaSet,
showStorageInfo,
@ -362,18 +361,7 @@ class FilesTableHeader extends React.Component {
const storageColumns = localStorage.getItem(this.props.tableStorageName);
const splitColumns = storageColumns && storageColumns.split(",");
const resetColumnsSize =
(splitColumns && splitColumns.length !== columns.length) ||
!splitColumns ||
isFrame;
if (isFrame && frameTableColumns) {
const frameTableArray = frameTableColumns.split(",");
columns = columns.map((col) => {
col.enable = frameTableArray.includes(col.key) ? true : false;
return col;
});
}
(splitColumns && splitColumns.length !== columns.length) || !splitColumns;
const tableColumns = columns.map((c) => c.enable && c.key);
@ -705,7 +693,6 @@ export default inject(
publicRoomKey,
isFrame,
frameTableColumns: frameConfig?.viewTableColumns,
isRecentTab,
showSettings: frameConfig?.showSettings,
isDefaultRoomsQuotaSet,

View File

@ -925,7 +925,7 @@ const SectionHeaderContent = (props) => {
{
id: "header_option_sharing-settings",
key: "sharing-settings",
label: t("Files:Share"),
label: t("Common:Share"),
onClick: onClickShare,
disabled: !selectedFolder.security?.CreateRoomFrom,
icon: ShareReactSvgUrl,

View File

@ -518,7 +518,10 @@ const Manager = (props) => {
const onChangeSearch = (e) => {
setConfig((config) => {
return { ...config, search: e.target.value };
return {
...config,
filter: { ...config.filter, filterValue: e.target.value },
};
});
};
@ -1266,7 +1269,7 @@ const Manager = (props) => {
scale={true}
onChange={onChangeSearch}
placeholder={t("Common:Search")}
value={config.search}
value={config.filter.filterValue}
tabIndex={5}
/>
<Checkbox

View File

@ -116,9 +116,17 @@ class ThirdPartyServices extends React.Component {
}
componentDidMount() {
const { getConsumers } = this.props;
const { getConsumers, fetchAndSetConsumers } = this.props;
showLoader();
getConsumers().finally(() => hideLoader());
const urlParts = window.location.href.split("?");
if (urlParts.length > 1) {
const queryValue = urlParts[1].split("=")[1];
fetchAndSetConsumers(queryValue)
.then((isConsumerExist) => isConsumerExist && this.onModalOpen())
.finally(() => hideLoader());
} else {
getConsumers().finally(() => hideLoader());
}
}
onChangeLoading = (status) => {
@ -340,6 +348,7 @@ export default inject(
integration,
updateConsumerProps,
setSelectedConsumer,
fetchAndSetConsumers,
} = setup;
const { consumers } = integration;
const { isThirdPartyAvailable } = currentQuotaStore;
@ -351,6 +360,7 @@ export default inject(
getConsumers,
updateConsumerProps,
setSelectedConsumer,
fetchAndSetConsumers,
setDocumentTitle,
currentColorScheme,
isThirdPartyAvailable,

View File

@ -111,6 +111,9 @@ class ConsumerModalDialog extends React.Component {
onChangeLoading(false);
hideLoader();
toastr.success(t("ThirdPartyPropsActivated"));
const channel = new BroadcastChannel("thirdpartyActivation");
channel.postMessage(true);
})
.catch((error) => {
onChangeLoading(false);

View File

@ -23,12 +23,15 @@
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
// in this file you can append custom step methods to 'I' object
module.exports = function () {
return actor({
// Define custom steps here, use 'this' to access default methods of I.
// It is recommended to place a general 'login' function here.
});
};
export const DEFAULT_EXTS_IMAGE = [
".svg",
".bmp",
".gif",
".jpeg",
".jpg",
".png",
".ico",
".tif",
".tiff",
".webp",
];

View File

@ -0,0 +1,70 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { AxiosError } from "axios";
import { isMobile } from "react-device-detect";
import { useState, useEffect, useCallback } from "react";
import { getDeviceTypeByWidth } from "@docspace/shared/utils";
import { DeviceType } from "@docspace/shared/enums";
export const useDeviceType = () => {
const [currentDeviceType, setCurrentDeviceType] = useState<DeviceType>(() =>
getDeviceTypeByWidth(window.innerWidth),
);
const onResize = useCallback(() => {
setCurrentDeviceType(getDeviceTypeByWidth(window.innerWidth));
}, []);
useEffect(() => {
window.addEventListener("resize", onResize);
if (isMobile) {
if (window.screen.orientation) {
window.screen.orientation.addEventListener("change", onResize);
} else {
window.addEventListener("orientationchange", onResize);
}
}
return () => {
window.removeEventListener("resize", onResize);
window.removeEventListener("orientationchange", onResize);
window.screen?.orientation?.removeEventListener("change", onResize);
};
}, [onResize]);
return currentDeviceType;
};
export const isAxiosError = (error: unknown): error is AxiosError => {
return (
error !== null &&
typeof error === "object" &&
"isAxiosError" in error &&
typeof error.isAxiosError === "boolean" &&
error.isAxiosError
);
};

View File

@ -0,0 +1,140 @@
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { observer, inject } from "mobx-react";
import { useParams, useSearchParams } from "react-router-dom";
import api from "@docspace/shared/api";
import { UrlActionType } from "@docspace/shared/enums";
import { toastr } from "@docspace/shared/components/toast";
import MediaViewer from "@docspace/shared/components/media-viewer/MediaViewer";
import { ViewerLoader } from "@docspace/shared/components/media-viewer/sub-components/ViewerLoader";
import Error403 from "@docspace/shared/components/errors/Error403";
import type { TFile } from "@docspace/shared/api/files/types";
import type {
NumberOrString,
PlaylistType,
} from "@docspace/shared/components/media-viewer/MediaViewer.types";
import type { PublicPreviewProps } from "./PublicPreview.types";
import { DEFAULT_EXTS_IMAGE } from "./PublicPreview.constants";
import { isAxiosError, useDeviceType } from "./PublicPreview.helpers";
const PublicPreview = ({
getIcon,
openUrl,
getFilesSettings,
extsImagePreviewed,
}: PublicPreviewProps) => {
const { t } = useTranslation();
const { id } = useParams();
const currentDeviceType = useDeviceType();
const [files, setFiles] = useState<TFile[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [searchParams] = useSearchParams();
const [errorStatus, setErrorStatus] = useState<number>();
const init = useCallback(async () => {
const key = searchParams.get("share");
if (!id || !key) return;
try {
setIsLoading(true);
const [fileInfo] = await Promise.all([
api.files.getFileInfo(id, key),
getFilesSettings?.(),
]);
if (!fileInfo) return;
setFiles([fileInfo]);
} catch (error) {
if (isAxiosError(error)) {
const status = error.response?.status;
if (status !== undefined && status === 403) {
return setErrorStatus(status);
}
toastr.error(error);
}
// eslint-disable-next-line no-console
console.error(error);
} finally {
setIsLoading(false);
}
}, [getFilesSettings, searchParams, id]);
useEffect(() => {
init();
}, [init]);
const getIconUrl = useCallback(
(size: number, ext: string) => {
return getIcon?.(size, ext) ?? "";
},
[getIcon],
);
const playlist: PlaylistType[] = files.map((file, index) => ({
id: index,
fileId: file.id,
src: file.viewUrl,
title: file.title,
fileExst: file.fileExst,
fileStatus: file.fileStatus,
canShare: file.canShare,
version: file.version,
thumbnailUrl: file.thumbnailUrl ?? "",
}));
const onDownloadMediaFile = useCallback(
(fileId: NumberOrString) => {
if (playlist.length > 0) {
const viewUrlFile = playlist.find(
(file) => file.fileId === fileId,
)?.src;
if (!viewUrlFile) return;
return openUrl?.(viewUrlFile, UrlActionType.Download);
}
},
[playlist, openUrl],
);
if (errorStatus === 403) return <Error403 />;
if (isLoading) return <ViewerLoader isLoading />;
return (
<div>
{playlist.length > 0 && (
<MediaViewer
t={t}
visible
files={files}
isPublicFile
playlistPos={0}
playlist={playlist}
getIcon={getIconUrl}
currentFileId={playlist[0].fileId}
currentDeviceType={currentDeviceType}
extsImagePreviewed={extsImagePreviewed ?? DEFAULT_EXTS_IMAGE}
onDownload={onDownloadMediaFile}
/>
)}
</div>
);
};
export default inject<TStore>(({ filesSettingsStore, settingsStore }) => {
const { getFilesSettings, getIcon, extsImagePreviewed } = filesSettingsStore;
const { openUrl } = settingsStore;
return { getFilesSettings, getIcon, openUrl, extsImagePreviewed };
})(observer(PublicPreview));

View File

@ -0,0 +1,6 @@
export interface PublicPreviewProps {
getFilesSettings?: TStore["filesSettingsStore"]["getFilesSettings"];
getIcon?: TStore["filesSettingsStore"]["getIcon"];
extsImagePreviewed?: TStore["filesSettingsStore"]["extsImagePreviewed"];
openUrl?: TStore["settingsStore"]["openUrl"];
}

View File

@ -37,6 +37,9 @@ import ErrorBoundary from "../components/ErrorBoundaryWrapper";
import FilesView from "SRC_DIR/pages/Home/View/Files";
import AccountsView from "SRC_DIR/pages/Home/View/Accounts";
import SettingsView from "SRC_DIR/pages/Home/View/Settings";
const PublicPreview = loadable(() =>
componentLoader(() => import("../pages/PublicPreview/PublicPreview")),
);
import { generalRoutes } from "./general";
@ -380,6 +383,16 @@ const ClientRoutes = [
</PrivateRoute>
),
},
{
path: "/share/preview/:id",
element: (
<PublicRoute>
<ErrorBoundary>
<PublicPreview />
</ErrorBoundary>
</PublicRoute>
),
},
{
path: "/rooms/share",
element: (

View File

@ -1344,7 +1344,7 @@ class ContextOptionsStore {
{
id: "option_sharing-settings",
key: "sharing-settings",
label: t("Files:Share"),
label: t("Common:Share"),
icon: ShareReactSvgUrl,
onClick: () => this.onClickShare(item),
disabled: !isShareable,

View File

@ -29,7 +29,7 @@ import {
setFavoritesSetting,
setRecentSetting,
} from "@docspace/shared/api/files";
import { RoomsType } from "@docspace/shared/enums";
import { FolderType, RoomsType } from "@docspace/shared/enums";
import axios from "axios";
import { makeAutoObservable } from "mobx";
import { presentInArray } from "@docspace/shared/utils";
@ -40,7 +40,10 @@ import {
iconSize96,
} from "@docspace/shared/utils/image-helpers";
import { HTML_EXST } from "@docspace/shared/constants";
import { getIconPathByFolderType } from "@docspace/shared/utils/common";
import {
getIconPathByFolderType,
isPublicPreview,
} from "@docspace/shared/utils/common";
class FilesSettingsStore {
thirdPartyStore;
treeFoldersStore;
@ -150,7 +153,11 @@ class FilesSettingsStore {
this.setFilesSettings(settings);
this.setIsLoaded(true);
if (!settings.enableThirdParty || this.publicRoomStore.isPublicRoom)
if (
!settings.enableThirdParty ||
this.publicRoomStore.isPublicRoom ||
isPublicPreview()
)
return;
return axios
@ -294,6 +301,17 @@ class FilesSettingsStore {
isSpreadsheet = (extension) =>
presentInArray(this.extsSpreadsheet, extension);
/**
*
* @param {number} [size = 24]
* @param {string } [fileExst = null]
* @param {string} [pproviderKey
* @param {*} contentLength
* @param {RoomsType | null} roomType
* @param {boolean | null} isArchive
* @param {FolderType} folderType
* @returns {string | undefined}
*/
getIcon = (
size = 24,
fileExst = null,

View File

@ -249,7 +249,7 @@ class ProfileActionsStore {
} = this.settingsStore;
const isAdmin = this.authStore.isAdmin;
const isCommunity = this.authStore.isCommunity;
const { isOwner } = this.userStore.user;
// const { isOwner } = this.userStore.user;
// const settingsModule = modules.find((module) => module.id === "settings");
// const peopleAvailable = modules.some((m) => m.appName === "people");

View File

@ -498,6 +498,15 @@ class SettingsSetupStore {
this.setConsumers(res);
};
fetchAndSetConsumers = async (consumerName) => {
const res = await api.settings.getConsumersList();
const consumer = res.find((c) => c.name === consumerName);
this.integration.selectedConsumer = consumer || {};
this.setConsumers(res);
return !!consumer
};
updateConsumerProps = async (newProps) => {
await api.settings.updateConsumerProps(newProps);

View File

@ -34,19 +34,16 @@ const TABLE_ACCOUNTS_INSIDE_GROUP_COLUMNS = `insideGroupTableColumns_ver-${Table
const TABLE_ROOMS_COLUMNS = `roomsTableColumns_ver-${TableVersions.Rooms}`;
const TABLE_TRASH_COLUMNS = `trashTableColumns_ver-${TableVersions.Trash}`;
const TABLE_RECENT_COLUMNS = `recentTableColumns_ver-${TableVersions.Recent}`;
const TABLE_SDK_COLUMNS = `filesSDKTableColumns_ver-${TableVersions.Files}`;
const COLUMNS_SIZE = `filesColumnsSize_ver-${TableVersions.Files}`;
const COLUMNS_ROOMS_SIZE = `roomsColumnsSize_ver-${TableVersions.Rooms}`;
const COLUMNS_TRASH_SIZE = `trashColumnsSize_ver-${TableVersions.Trash}`;
const COLUMNS_RECENT_SIZE = `recentColumnsSize_ver-${TableVersions.Recent}`;
const COLUMNS_SDK_SIZE = `filesSDKColumnsSize_ver-${TableVersions.Files}`;
const COLUMNS_SIZE_INFO_PANEL = `filesColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
const COLUMNS_ROOMS_SIZE_INFO_PANEL = `roomsColumnsSizeInfoPanel_ver-${TableVersions.Rooms}`;
const COLUMNS_TRASH_SIZE_INFO_PANEL = `trashColumnsSizeInfoPanel_ver-${TableVersions.Trash}`;
const COLUMNS_RECENT_SIZE_INFO_PANEL = `recentColumnsSizeInfoPanel_ver-${TableVersions.Recent}`;
const COLUMNS_SDK_SIZE_INFO_PANEL = `filesSDKColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
class TableStore {
authStore;
@ -176,9 +173,13 @@ class TableStore {
setAccountsInsideGroupColumnGroup = (enable) =>
(this.groupAccountsInsideGroupColumnIsEnabled = enable);
setColumnsEnable = () => {
setColumnsEnable = (frameTableColumns) => {
const storageColumns = localStorage.getItem(this.tableStorageName);
const splitColumns = storageColumns && storageColumns.split(",");
const splitColumns = storageColumns
? storageColumns.split(",")
: frameTableColumns
? frameTableColumns.split(",")
: null;
if (splitColumns) {
const {
@ -360,8 +361,10 @@ class TableStore {
};
getColumns = (defaultColumns) => {
const { isFrame, frameConfig } = this.settingsStore;
const storageColumns = localStorage.getItem(this.tableStorageName);
const splitColumns = storageColumns && storageColumns.split(",");
const frameTableColumns = frameConfig?.viewTableColumns;
const columns = [];
@ -375,11 +378,20 @@ class TableStore {
columns.push(col);
}
return columns;
} else if (isFrame && frameTableColumns) {
this.setColumnsEnable(frameTableColumns);
const frameTableArray = frameTableColumns.split(",");
return defaultColumns.map((col) => {
col.enable = frameTableArray.includes(col.key) ? true : false;
return col;
});
} else {
return defaultColumns;
}
};
// Column names
get tableStorageName() {
const {
isRoomsFolder,
@ -395,9 +407,7 @@ class TableStore {
const userId = this.userStore.user?.id;
const isFrame = this.settingsStore.isFrame;
if (isFrame) return `${TABLE_SDK_COLUMNS}=${userId}`;
return isRooms
const tableStorageName = isRooms
? `${TABLE_ROOMS_COLUMNS}=${userId}`
: getIsAccountsPeople()
? `${TABLE_ACCOUNTS_PEOPLE_COLUMNS}=${userId}`
@ -410,8 +420,11 @@ class TableStore {
: isRecentTab
? `${TABLE_RECENT_COLUMNS}=${userId}`
: `${TABLE_COLUMNS}=${userId}`;
return isFrame ? `SDK_${tableStorageName}` : tableStorageName;
}
// Table column sizes
get columnStorageName() {
const { isRoomsFolder, isArchiveFolder, isTrashFolder, isRecentTab } =
this.treeFoldersStore;
@ -419,17 +432,18 @@ class TableStore {
const userId = this.userStore.user?.id;
const isFrame = this.settingsStore.isFrame;
if (isFrame) return `${COLUMNS_SDK_SIZE}=${userId}`;
return isRooms
const columnStorageName = isRooms
? `${COLUMNS_ROOMS_SIZE}=${userId}`
: isTrashFolder
? `${COLUMNS_TRASH_SIZE}=${userId}`
: isRecentTab
? `${COLUMNS_RECENT_SIZE}=${userId}`
: `${COLUMNS_SIZE}=${userId}`;
return isFrame ? `SDK_${columnStorageName}` : columnStorageName;
}
// Column names for info-panel
get columnInfoPanelStorageName() {
const { isRoomsFolder, isArchiveFolder, isTrashFolder, isRecentTab } =
this.treeFoldersStore;
@ -437,15 +451,17 @@ class TableStore {
const userId = this.userStore.user?.id;
const isFrame = this.settingsStore.isFrame;
if (isFrame) return `${COLUMNS_SDK_SIZE_INFO_PANEL}=${userId}`;
return isRooms
const columnInfoPanelStorageName = isRooms
? `${COLUMNS_ROOMS_SIZE_INFO_PANEL}=${userId}`
: isTrashFolder
? `${COLUMNS_TRASH_SIZE_INFO_PANEL}=${userId}`
: isRecentTab
? `${COLUMNS_RECENT_SIZE_INFO_PANEL}=${userId}`
: `${COLUMNS_SIZE_INFO_PANEL}=${userId}`;
return isFrame
? `SDK_${columnInfoPanelStorageName}`
: columnInfoPanelStorageName;
}
get filesColumnStorageName() {

View File

@ -51,6 +51,7 @@ import i18n from "../i18n";
class ThirdPartyStore {
capabilities = null;
providers = [];
connectingStorages = [];
constructor() {
makeAutoObservable(this);
@ -71,6 +72,20 @@ class ThirdPartyStore {
this.setThirdPartyProviders(list);
};
fetchConnectingStorages = async () => {
const res = await api.files.getConnectingStorages();
this.connectingStorages = res.map((storage) => ({
id: storage.name,
className: `storage_${storage.key}`,
providerKey: storage.key !== "WebDav" ? storage.key : storage.name,
isConnected: storage.connected,
isOauth: storage.oauth,
oauthHref: storage.redirectUrl,
category: storage.name,
}));
};
saveThirdParty = (
url,
login,

View File

@ -28,6 +28,7 @@ import { makeAutoObservable, runInAction } from "mobx";
import api from "@docspace/shared/api";
import { size } from "@docspace/shared/utils";
import { FileStatus } from "@docspace/shared/enums";
import { toastr } from "@docspace/shared/components/toast";
class VersionHistoryStore {
isVisible = false;
@ -154,7 +155,7 @@ class VersionHistoryStore {
updatedVersions.unshift(newVersion);
this.setVerHistoryFileVersions(updatedVersions);
})
.catch((e) => console.error(e))
.catch((e) => toastr.error(e))
.finally(() => {
clearTimeout(this.timerId);
this.timerId = null;

View File

@ -1,4 +1,4 @@
{
"trailingComma":"all",
"endOfLine": "crlf"
"trailingComma": "all",
"endOfLine": "auto"
}

View File

@ -1,12 +0,0 @@
{
"presets": [
"@babel/preset-react",
"@babel/preset-env",
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties",
["styled-components", { "ssr": true }]
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@ -1 +0,0 @@
dist/* linguist-vendored=false

View File

@ -1,123 +1,39 @@
# Logs
logs
*.log
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# local env files
.env*.local
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# vercel
.vercel
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# e2e test
tests/reports
tests/output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
# typescript
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
next-env.d.ts
autoGeneratedTranslations.ts
autoGeneratedTranslations.js

View File

@ -0,0 +1,4 @@
{
"trailingComma": "all",
"endOfLine": "auto"
}

View File

@ -1,114 +0,0 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
const { setHeadlessWhen, setWindowSize } = require("@codeceptjs/configure");
// turn on headless mode when running with HEADLESS=true environment variable
// export HEADLESS=true && npx codeceptjs run
setHeadlessWhen(process.env.HEADLESS);
const sizes = {
mobile: { width: 375, height: 667 },
smallTablet: { width: 600, height: 667 },
tablet: { width: 1023, height: 667 },
desktop: { width: 1920, height: 1080 },
};
const deviceType = process.env.DEVICE_TYPE || "desktop";
const device = sizes[deviceType];
setWindowSize(device.width, device.height);
const browser = process.env.profile || "chromium";
const isModel = !!process.env.MODEL;
const screenshotOutput = isModel
? `./tests/screenshots/${browser}/${deviceType}`
: `./tests/output/${browser}/${deviceType}`;
exports.config = {
tests: "./tests/*_tests.js",
output: screenshotOutput,
helpers: {
Playwright: {
url: "http://localhost:8092",
// show browser window
show: false,
browser: browser,
// restart browser between tests
restart: true,
waitForNavigation: "networkidle0",
// don't save screenshot on failure
disableScreenshots: false,
},
ResembleHelper: {
require: "codeceptjs-resemblehelper",
screenshotFolder: "./tests/output/",
baseFolder: `./tests/screenshots/${browser}/${deviceType}`,
diffFolder: "./tests/output/diff/",
},
PlaywrightHelper: {
require: "./tests/helpers/playwright.helper.js",
},
},
include: {
I: "./steps_file.js",
},
bootstrap: null,
mocha: {
reporterOptions: {
mochawesome: {
stdout: "-",
options: {
reportDir: `./tests/reports/${browser}/${deviceType}`,
reportFilename: "report",
},
},
"mocha-junit-reporter": {
stdout: "-",
options: {
mochaFile: `./tests/reports/${browser}/${deviceType}/report.xml`,
attachments: false, //add screenshot for a failed test
},
},
},
},
name: "ASC.Web.Login",
plugins: {
pauseOnFail: {},
retryFailedStep: {
enabled: true,
},
tryTo: {
enabled: true,
},
screenshotOnFail: {
enabled: true,
},
},
};

View File

@ -24,174 +24,12 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { Request } from "express";
type WindowI18nType = {
inLoad: object[];
loaded: {
[key: string]: {
data: {
[key: string]: string | undefined;
};
namespaces?: string;
};
};
};
declare global {
interface Window {
authCallback?: (profile: string) => {};
__ASC_INITIAL_LOGIN_STATE__: IInitialState;
initialI18nStoreASC: IInitialI18nStoreASC;
initialLanguage: string;
i18n: WindowI18nType;
[key: string]: object;
}
type MatchType = {
confirmedEmail?: string;
message?: string;
messageKey?: string;
authError?: string;
};
type PasswordHashType = {
iterations: number;
salt: string;
size: number;
};
type CaptchaPublicKeyType = string | undefined;
interface IEmailValid {
value: string;
isValid: boolean;
errors: string[]; // TODO: check type
}
interface IPortalSettings {
culture: string;
debugInfo: boolean;
docSpace: boolean;
enableAdmMess: boolean;
enabledJoin: boolean;
greetingSettings: string;
ownerId: string;
passwordHash: PasswordHashType;
tenantAlias: string;
tenantStatus: number;
thirdpartyEnable: boolean;
trustedDomainsType: number;
utcHoursOffset: number;
utcOffset: string;
version: string;
standalone: boolean;
trustedDomains: string[];
recaptchaPublicKey: CaptchaPublicKeyType;
}
interface IBuildInfo {
communityServer: string;
documentServer: string;
mailServer: string;
}
interface IProvider {
linked: boolean;
provider: string;
url: string;
}
type ProvidersType = IProvider[] | undefined;
interface ICapabilities {
ldapEnabled: boolean;
providers: string[];
ssoLabel: string;
ssoUrl: string;
}
type TThemeObj = {
accent: string;
buttons: string;
};
interface ITheme {
id: number;
main: TThemeObj;
text: TThemeObj;
name: string;
}
interface IThemes {
limit: number;
selected: number;
themes: ITheme[];
}
interface IError {
status: number;
standalone: boolean;
message: string | undefined;
}
interface ISSOSettings {
hideAuthPage: boolean;
}
interface IInitialState {
portalSettings?: IPortalSettings;
buildInfo?: IBuildInfo;
providers?: ProvidersType;
capabilities?: ICapabilities;
match?: MatchType;
currentColorScheme?: ITheme;
ssoSettings?: ISSOSettings;
logoUrls: ILogoUrl[];
error?: IError;
}
interface DevRequest {
assets: assetsType;
}
var IS_DEVELOPMENT: boolean;
var PORT: number;
var IS_ROOMS_MODE: boolean;
var BROWSER_DETECTOR_URL: string;
var CONFIG_URL: string;
type assetsType = { [key: string]: string } | undefined;
interface IInitialI18nStoreASC extends Object {
en: {
[Common: string]: { [key: any]: string };
[Login: string]: { [key: any]: string };
};
[key: string]: {
[Common: string]: { [key: any]: string };
[Login: string]: { [key: any]: string };
};
}
type HTMLElementEvent<T extends HTMLElement> = Event & {
target: T;
};
type TFuncType = (key: string) => string;
interface IParsedConfig extends Object {
PORT: number;
}
interface ILoginRequest extends Request {
i18n?: I18next;
t?: TFuncType;
}
type timeoutType = ReturnType<typeof setTimeout>;
interface IAcceptLanguage {
code?: string;
quality?: number;
}
interface IUserTheme {
[key: string]: string;
isBase: boolean;
}
declare module "*.ico?url" {
const content: string;
export default content;
}
declare module "*.svg?url" {
const content: string;
export default content;
}

View File

@ -1,9 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"components": ["@docspace/shared/components"]
}
},
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,115 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
/** @type {import('next').NextConfig} */
const path = require("path");
const pkg = require("./package.json");
const nextConfig = {
basePath: "/login",
output: "standalone",
compiler: {
styledComponents: true,
},
generateBuildId: async () => {
// This could be anything, using the latest git hash
return `${pkg.name} - ${pkg.version} `;
},
images: {
unoptimized: true,
},
typescript: {
// !! WARN !!
// Dangerously allow production builds to successfully complete even if
// your project has type errors.
// !! WARN !!
ignoreBuildErrors: true,
},
logging: {
fetches: {
fullUrl: true,
},
},
};
module.exports = {
webpack(config) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.(".svg"),
);
const imageRule = config.module.rules.find(
(rule) => rule.loader === "next-image-loader",
);
imageRule.resourceQuery = {
not: [...fileLoaderRule.resourceQuery.not, /url/],
};
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
type: "asset/resource",
generator: {
emit: false,
filename: "static/chunks/[path][name][ext]?[hash]",
},
test: /\.(svg|png|jpe?g|gif|ico|woff2)$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
loader: "@svgr/webpack",
options: {
prettier: false,
svgo: true,
svgoConfig: {
plugins: [
{
name: "preset-default",
params: {
overrides: { removeViewBox: false },
},
},
],
},
titleProp: true,
},
},
);
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i;
return config;
},
...nextConfig,
};

View File

@ -1,129 +1,36 @@
{
"name": "@docspace/login",
"version": "2.5.1",
"version": "2.5.0",
"private": true,
"homepage": "/login",
"scripts": {
"build": "yarn clean && yarn build:translations && yarn build:client && yarn build:server",
"build:client": "NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --config webpack/webpack.client.js",
"build:server": "NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --config webpack/webpack.server.js",
"build:dev-server": "NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack/webpack.server.js",
"build:dev-client": "NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack/webpack.client.js",
"build-rooms": "yarn clean && yarn build-rooms:client && yarn build-rooms-server",
"build-rooms:client": "NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --env rooms=true --config webpack/webpack.client.js",
"build-rooms:server": "NODE_OPTIONS=--openssl-legacy-provider webpack --mode production --env rooms=true --config webpack/webpack.server.js",
"build-rooms:dev-server": "NODE_OPTIONS=--openssl-legacy-provider webpack --env rooms=true --config webpack/webpack.server.js",
"build-rooms:dev-client": "NODE_OPTIONS=--openssl-legacy-provider webpack --env rooms=true --config webpack/webpack.client.js",
"build:translations": "node scripts/buildTranslations.js",
"clean": "shx rm -rf dist",
"start": "yarn clean && yarn build:translations && npm-run-all --parallel start:client start:server start:common",
"start:client": "NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack/webpack.client.js --watch --no-cache",
"start:server": "NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack/webpack.server.js --watch --no-cache",
"start:common": "yarn build:dev-client && yarn build:dev-server && NODE_OPTIONS=--openssl-legacy-provider nodemon --watch dist/server.js dist/server.js",
"start-rooms": "yarn clean && npm-run-all --parallel start-rooms:client start-rooms:server start-rooms:common",
"start-rooms:client": "NODE_OPTIONS=--openssl-legacy-provider webpack --env rooms=true --config webpack/webpack.client.js --watch --no-cache",
"start-rooms:server": "NODE_OPTIONS=--openssl-legacy-provider webpack --env rooms=true --config webpack/webpack.server.js --watch --no-cache",
"start-rooms:common": "yarn build-rooms:dev-client && yarn build-rooms:dev-server && nodemon --watch dist/server.js dist/server.js",
"start-prod": "NODE_OPTIONS=--openssl-legacy-provider node dist/server.js",
"deploy": "shx --silent mkdir -p ../../../publish/web/login && shx cp -r dist/* ../../../publish/web/login && shx cp -f src/server/config/config-deploy.json ../../../publish/web/login/config.json"
"build": "node ./scripts/buildTranslations.js && next build",
"start": "node ./scripts/buildTranslations.js && NODE_ENV=development node server.js",
"start-prod": "NODE_ENV=production node server.js",
"lint": "next lint",
"clean": "shx rm -rf .next",
"deploy": "shx --silent mkdir -p ../../../publish/web/login && shx --silent mkdir -p ../../../publish/web/login/.next && shx --silent mkdir -p ../../../publish/web/login/node_modules && shx --silent mkdir -p ../../../publish/web/login/.next/static && shx cp -r .next/standalone/node_modules/* ../../../publish/web/login/node_modules && shx cp -r .next/static/* ../../../publish/web/login/.next/static && shx cp -r .next/standalone/packages/login/.next/* ../../../publish/web/login/.next && shx cp -f server.prod.js ../../../publish/web/login/server.js"
},
"old-scripts": {
"build:test": "NODE_OPTIONS=--openssl-legacy-provider webpack --env minimize=false --mode production",
"build:test.translation": "NODE_OPTIONS=--openssl-legacy-provider webpack --env minimize=false hideText=true --mode production",
"test:codeceptjs": "npx codeceptjs run --reporter mocha-multi",
"test:mobile": "cross-env DEVICE_TYPE=mobile yarn test:codeceptjs",
"test:smallTablet": "cross-env DEVICE_TYPE=smallTablet yarn test:codeceptjs",
"test:tablet": "cross-env DEVICE_TYPE=tablet yarn test:codeceptjs",
"test:desktop": "cross-env DEVICE_TYPE=desktop yarn test:codeceptjs ",
"test:mobile:model": "cross-env DEVICE_TYPE=mobile MODEL=true yarn test:codeceptjs",
"test:smallTablet:model": "cross-env DEVICE_TYPE=smallTablet MODEL=true yarn test:codeceptjs",
"test:tablet:model": "cross-env DEVICE_TYPE=tablet MODEL=true yarn test:codeceptjs",
"test:desktop:model": "cross-env DEVICE_TYPE=desktop MODEL=true yarn test:codeceptjs",
"test:chromium:parallel": "run-p -c \"test:mobile --profile chromium \" \"test:smallTablet --profile chromium \" \"test:tablet --profile chromium \" \"test:desktop --profile chromium \" && yarn test:parse-xml",
"test:chromium:sequential": "run-s -c \"test:mobile --profile chromium \" \"test:smallTablet --profile chromium \" \"test:tablet --profile chromium \" \"test:desktop --profile chromium \" && yarn test:parse-xml",
"test:chromium:model": "run-s -c \"test:mobile:model --profile chromium \" \"test:smallTablet:model --profile chromium \" \"test:tablet:model --profile chromium \" \"test:desktop:model --profile chromium \" && yarn test:parse-xml",
"test:firefox:parallel": "run-p -c \"test:mobile --profile firefox \" \"test:smallTablet --profile firefox \" \"test:tablet --profile firefox \" \"test:desktop --profile firefox \" && yarn test:parse-xml",
"test:firefox:sequential": "run-s -c \"test:mobile --profile firefox \" \"test:smallTablet --profile firefox \" \"test:tablet --profile firefox \" \"test:desktop --profile firefox \" && yarn test:parse-xml",
"test:firefox:model": "run-s -c \"test:mobile:model --profile firefox \" \"test:smallTablet:model --profile firefox \" \"test:tablet:model --profile firefox \" \"test:desktop:model --profile firefox \" && yarn test:parse-xml",
"test:webkit:parallel": "run-p -c \"test:mobile --profile webkit \" \"test:smallTablet --profile webkit \" \"test:tablet --profile webkit \" \"test:desktop --profile webkit \" && yarn test:parse-xml",
"test:webkit:sequential": "run-s -c \"test:mobile --profile webkit \" \"test:smallTablet --profile webkit \" \"test:tablet --profile webkit \" \"test:desktop --profile webkit \" && yarn test:parse-xml",
"test:webkit:model": "run-s -c \"test:mobile:model --profile webkit \" \"test:smallTablet:model --profile webkit \" \"test:tablet:model --profile webkit \" \"test:desktop:model --profile webkit \" && yarn test:parse-xml",
"test:parallel": "run-s -c test:chromium:parallel test:firefox:parallel test:webkit:parallel && yarn test:parse-xml",
"test:sequential": "run-s -c test:chromium:sequential test:firefox:sequential test:webkit:sequential && yarn test:parse-xml",
"test:model": "run-s -c test:chromium:model test:firefox:model test:webkit:model && yarn test:parse-xml",
"test:parse-xml": "node tests/helpers/parserXML.js"
"dependencies": {
"i18next": "^20.6.1",
"next": "14.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-i18next": "^13.2.1",
"sass": "^1.59.3",
"styled-components": "^5.3.9"
},
"devDependencies": {
"@babel/core": "^7.21.3",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-export-default-from": "^7.18.10",
"@babel/plugin-transform-runtime": "^7.21.0",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
"@svgr/webpack": "^5.5.0",
"@types/accept-language-parser": "^1.5.3",
"@types/compression": "^1.7.2",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.17",
"@types/i18next-fs-backend": "^1.1.2",
"@types/morgan": "^1.9.4",
"@types/node": "^18.15.5",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/styled-components": "^5.1.26",
"@types/winston": "^2.4.4",
"babel-loader": "^8.3.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.1.0",
"css-loader": "^6.7.3",
"external-remotes-plugin": "^1.0.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "5.5.0",
"json-loader": "^0.5.7",
"mocha": "^9.2.2",
"mocha-junit-reporter": "^2.2.0",
"mocha-multi": "^1.1.7",
"mochawesome": "^7.1.3",
"nodemon": "^2.0.22",
"npm-run-all": "^4.1.5",
"playwright": "^1.32.0",
"sass": "^1.59.3",
"sass-loader": "^12.6.0",
"serve": "14.2.0",
"@svgr/webpack": "^8.1.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-google-recaptcha": "^2.1.9",
"babel-plugin-styled-components": "^2.1.4",
"eslint": "^8",
"eslint-config-next": "14.0.4",
"prettier": "^3.2.4",
"shx": "^0.3.4",
"source-map-loader": "^3.0.2",
"style-loader": "3.3.2",
"terser-webpack-plugin": "^5.3.7",
"typescript": "^4.9.5",
"webpack": "5.76.3",
"webpack-cli": "4.10.0",
"webpack-dev-server": "4.13.1",
"webpack-filter-warnings-plugin": "^1.2.1",
"webpack-manifest-plugin": "^5.0.0",
"webpack-merge": "^5.8.0"
},
"title": "ONLYOFFICE",
"socketPath": "/login/ws",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.297.0",
"@types/ws": "^8.5.4",
"aws-crt": "^1.15.13",
"bufferutil": "^4.0.7",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"date-and-time": "^2.4.3",
"express": "^4.18.2",
"express-xss-sanitizer": "^1.1.6",
"i18next-express-middleware": "^2.0.0",
"i18next-fs-backend": "^1.2.0",
"iconv-lite": "^0.6.3",
"morgan": "^1.10.0",
"nconf": "^0.12.0",
"react-google-recaptcha": "^3.1.0",
"utf-8-validate": "^5.0.10",
"winston": "^3.8.2",
"winston-cloudwatch": "^6.1.1",
"winston-daily-rotate-file": "^4.7.1"
"typescript": "^5"
}
}

View File

@ -1,42 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no, viewport-fit=cover"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="icon" type="image/x-icon" href="/logo.ashx?logotype=3" />
<link rel="mask-icon" href="/logo.ashx?logotype=3" />
<link rel="manifest" href="/manifest.json" />
<!-- Tell the browser it's a PWA -->
<!-- <meta name="mobile-web-app-capable" content="yes" /> -->
<!-- Tell iOS it's a PWA -->
<!-- <meta name="apple-mobile-web-app-capable" content="yes" /> -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body ondragstart="return false">
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script>
console.log("It's Login INIT");
</script>
</body>
</html>

View File

@ -20,6 +20,5 @@
"RegisterTitle": "طلب التسجيل",
"RegistrationEmailWatermark": "بريد إلكتروني",
"RememberHelper": "العمر الافتراضي للجلسة هو 20 دقيقة. حدد هذا الخيار لتعيينه على عام واحد. لتعيين القيمة الخاصة بك ، انتقل إلى الإعدادات.",
"ResendCode": "أعد إرسال الرمز",
"UserIsAlreadyRegistered": "المستخدم <1>{{email}}</1> مسجل بالفعل في DocSpace، أدخل كلمة المرور الخاصة بك أو ارجع للمتابعة باستخدام بريد إلكتروني آخر."
"ResendCode": "أعد إرسال الرمز"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Sorğunun qeydiyyatı",
"RegistrationEmailWatermark": "Elektron poçt",
"RememberHelper": "Susmaya görə sessiya müddəti 20 dəqiqədir. Müddəti 1 ilə uzatmaq üçün qutunu klikləyin. Digər müddəti təyin etmək üçün, Ayarlar bölməsinə keçin.",
"ResendCode": "Kodu yenidən göndərin",
"UserIsAlreadyRegistered": "<1>{{email}}</1> istifadəçisi artıq bu DocSpace-də qeydiyyatdan keçib, parolunuzu daxil edin və ya başqa e-poçtla davam etmək üçün geri qayıdın."
"ResendCode": "Kodu yenidən göndərin"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Заявка за регистрация",
"RegistrationEmailWatermark": "Имейл",
"RememberHelper": "Продължителността на сесията по подразбиране е 20 минути. Проверете тази опция, за да я настроите за 1 година. За да зададете собствена стойност, отидете в Настройки.",
"ResendCode": "Код за препращане",
"UserIsAlreadyRegistered": "Потребителят <1>{{email}}</1> вече е регистриран в този DocSpace, въведете паролата си или се върнете, за да продължите с друг имейл."
"ResendCode": "Код за препращане"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Žádost o registraci",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "Výchozí doba trvání relace je 20 minut. Zaškrtnutím této možnosti ji nastavíte na 1 rok. Chcete-li nastavit vlastní hodnotu, přejděte do Nastavení.",
"ResendCode": "Opětovné zaslání kódu",
"UserIsAlreadyRegistered": "Uživatel <1>{{email}}</1> je již v tomto DocSpace zaregistrován, zadejte své heslo nebo se vraťte zpět a pokračujte jiným e-mailem."
"ResendCode": "Opětovné zaslání kódu"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Registrierungsanfrage",
"RegistrationEmailWatermark": "E-Mail",
"RememberHelper": "Lebensdauer der Sitzung ist standardmäßig 20 Minuten. Wählen Sie diese Option aus, um den Wert 1 Jahr zu setzen. Für benutzerdefinierte Werte öffnen Sie Einstellungen.",
"ResendCode": "Code nochmals senden",
"UserIsAlreadyRegistered": "Benutzer <1>{{email}}</1> ist bereits in diesem DocSpace registriert. Geben Sie Ihr Passwort ein oder gehen Sie zurück, um mit einer anderen E-Mail fortzufahren."
"ResendCode": "Code nochmals senden"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Αίτημα εγγραφής",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "Η προεπιλεγμένη διάρκεια περιόδου λειτουργίας είναι 20 λεπτά. Ενεργοποιήστε αυτή την επιλογή για να την ορίσετε σε 1 έτος. Για να ορίσετε τη δική σας τιμή, μεταβείτε στις Ρυθμίσεις.",
"ResendCode": "Επαναποστολή κωδικού",
"UserIsAlreadyRegistered": "Ο χρήστης <1>{{email}}</1> είναι ήδη εγγεγραμμένος σε αυτό το DocSpace. Πληκτρολογήστε τον κωδικό πρόσβασής σας ή επιστρέψτε για να συνεχίσετε με άλλο email."
"ResendCode": "Επαναποστολή κωδικού"
}

View File

@ -5,7 +5,7 @@
"ErrorNotAllowedOption": "Your pricing plan does not support this option",
"ErrorUserNotFound": "The user could not be found",
"InvalidUsernameOrPassword": "Invalid username or password",
"LoginWithAccountNotFound": "Can't find associated third-party account. You need to connect your social networking account at the profile editing page first.",
"LoginWithAccountNotFound": "Can't find associated third-party account. You need to connect your social networking account at the profile editing page first.",
"LoginWithBruteForce": "Authorization temporarily blocked",
"LoginWithBruteForceCaptcha": "Confirm that you are not a robot",
"RecaptchaInvalid": "Invalid Recaptcha",

View File

@ -8,7 +8,7 @@
"InvalidCode": "This code is invalid. Try again.",
"MessageAuthorize": "Log in to continue",
"MessageEmailConfirmed": "Your email address was activated successfully.",
"MessageSendPasswordRecoveryInstructionsOnEmail": "Please enter the email address you used for registration. The password recovery instructions will be sent to it.",
"MessageSendPasswordRecoveryInstructionsOnEmail": "Please, enter the email address you used for registration. The password recovery instructions will be sent to it.",
"NotFoundCode": "Can't find the code? Check your spam folder.",
"PasswordRecoveryTitle": "Password recovery",
"RecoverAccess": "Recover access",

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Solicitud de registro",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "La duración de la sesión por defecto es de 20 minutos. Marque esta opción para establecerla en 1 año. Para establecer su propio valor, vaya a Ajustes.",
"ResendCode": "Reenviar código",
"UserIsAlreadyRegistered": "El usuario <1>{{email}}</1> ya está registrado en este DocSpace, introduzca su contraseña o regrese para continuar con otro correo electrónico."
"ResendCode": "Reenviar código"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Rekisteröintipyyntö",
"RegistrationEmailWatermark": "sähköposti",
"RememberHelper": "Istunnon oletuskesto on 20 minuuttia. Valitse tämä vaihtoehto, jos haluat asettaa sen 1 vuodeksi. Voit asettaa oman arvon Asetuksissa.",
"ResendCode": "Lähetä koodi uudelleen",
"UserIsAlreadyRegistered": "Käyttäjä <1>{{email}}</1> on jo rekisteröity tähän DocSpaceen, syötä salasanasi tai mene takaisin jatkaaksesi toisella sähköpostilla."
"ResendCode": "Lähetä koodi uudelleen"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Demande d'inscription",
"RegistrationEmailWatermark": "Adresse de courriel",
"RememberHelper": "Par défaut, la durée de validité de la session est de 20 minutes. Cochez cette option pour la définir sur 1 an. Vous pouvez définir votre propre valeur en accédant aux paramètres.",
"ResendCode": "Renvoyer le code",
"UserIsAlreadyRegistered": "L'utilisateur <1>{{email}}</1> est déjà enregistré dans ce DocSpace, saisissez votre mot de passe ou revenez en arrière pour continuer avec un autre e-mail."
"ResendCode": "Renvoyer le code"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Գրանցման հայցում",
"RegistrationEmailWatermark": "Էլ․փոստ",
"RememberHelper": "Նախնական աշխատաշրջանի աշխատաժամը 20 րոպե է: Նշեք այս տարբերակը՝ այն 1 տարի սահմանելու համար: Ձեր սեփական արժեքը սահմանելու համար անցեք Կարգավորումներ:",
"ResendCode": "Կրկին ուղարկել կոդը",
"UserIsAlreadyRegistered": "Օգտվող <1>{{email}}</1>-ն արդեն գրանցված է այս DocSpace-ում, մուտքագրեք ձեր գաղտնաբառը կամ վերադարձեք՝ շարունակելու մեկ այլ էլ։"
"ResendCode": "Կրկին ուղարկել կոդը"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Richiesta di inscrizione ",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "La durata predefinita della sessione è di 20 minuti. Selezionare questa opzione per impostarla su 1 anno. Per impostare il proprio valore, passare a Impostazioni.",
"ResendCode": "Invia nuovamente il codice",
"UserIsAlreadyRegistered": "L'utente <1>{{email}}</1> è già registrato in questo DocSpace, inserisci la tua password o torna indietro per continuare con un'altra email."
"ResendCode": "Invia nuovamente il codice"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "登録申請",
"RegistrationEmailWatermark": "メール",
"RememberHelper": "デフォルトのセッションライフタイムは20分です。このオプションをチェックすると、1年間に設定されます。独自の値を設定するには、「設定」で設定します。",
"ResendCode": "コードの再送信",
"UserIsAlreadyRegistered": "ユーザー<1>{{email}}</1>はすでにこのDocSpaceに登録されています。パスワードを入力するか、前に戻って別のメールアドレスでログインしてください。"
"ResendCode": "コードの再送信"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "가입 요청",
"RegistrationEmailWatermark": "이메일",
"RememberHelper": "기본 세션 기간은 20분입니다. 1년으로 설정하려면 이 옵션을 확인하세요. 원하시는 값으로 설정하려면 설정으로 이동하세요.",
"ResendCode": "코드 재전송",
"UserIsAlreadyRegistered": "<1>{{email}}</1> 사용자는 이미 이 DocSpace에 등록되어 있습니다. 비밀번호를 입력하거나 돌아가서 다른 이메일로 계속 진행하세요."
"ResendCode": "코드 재전송"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Reģistrācijas pieprasījums",
"RegistrationEmailWatermark": "E-pasts",
"RememberHelper": "Noklusējuma sesijas ilgums ir 20 minūtes. Atzīmējiet šo opciju, lai iestatītu to uz vienu gadu. Lai iestatītu savu vērtību, dodieties uz Iestatījumi.",
"ResendCode": "Atkārtoti nosūtīt kodu",
"UserIsAlreadyRegistered": "Lietotājs <1>{{email}}</1> jau ir reģistrēts šajā DocSpace, ievadiet savu paroli vai dodieties atpakaļ, lai turpinātu ar citu e-pasta adresi."
"ResendCode": "Atkārtoti nosūtīt kodu"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Registratieverzoek",
"RegistrationEmailWatermark": "E-mail",
"RememberHelper": "De standaard sessieduur is 20 minuten. Vink deze optie aan om deze in te stellen op 1 jaar. Om uw eigen waarde in te stellen, ga naar Instellingen.",
"ResendCode": "Code opnieuw versturen",
"UserIsAlreadyRegistered": "Gebruiker <1>{{email}}</1> is al geregistreerd in deze DocSpace, voer uw wachtwoord in of ga terug om verder te gaan met een andere e-mail."
"ResendCode": "Code opnieuw versturen"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Wniosek o rejestrację",
"RegistrationEmailWatermark": "E-mail",
"RememberHelper": "Domyślny czas trwania sesji to 20 min. Zaznacz tę opcję, aby ustawić go na 1 rok. Aby ustawić wartość niestandardową, przejdź do Ustawień.",
"ResendCode": "Wyślij kod jeszcze raz",
"UserIsAlreadyRegistered": "Użytkownik <1>{{email}}</1> jest już zarejestrowany w tym DocSpace, wpisz swoje hasło lub wróć, aby kontynuować z innym adresem e-mail."
"ResendCode": "Wyślij kod jeszcze raz"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Pedido de registro",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "A duração padrão da sessão é de 20 minutos. Marque esta opção para defini-la como 1 ano. Para definir seu próprio valor, vá para Configurações",
"ResendCode": "Reenviar código",
"UserIsAlreadyRegistered": "O usuário <1>{{email}}</1> já está cadastrado neste DocSpace, digite sua senha ou volte para continuar com outro e-mail."
"ResendCode": "Reenviar código"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Pedido de registo",
"RegistrationEmailWatermark": "E-mail",
"RememberHelper": "A duração da sessão predefinida é de 20 minutos. Clique nesta opção para configurá-la para 1 ano. Para introduzir um período à sua escolha, aceda às definições.",
"ResendCode": "Reenviar código",
"UserIsAlreadyRegistered": "O usuário <1>{{email}}</1> já está cadastrado neste DocSpace, digite sua senha ou volte para continuar com outro e-mail."
"ResendCode": "Reenviar código"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Solicitarea de înregistrare",
"RegistrationEmailWatermark": "E-mail",
"RememberHelper": "Durata sesiunii implicită este de 20 de minute. Bifați caseta de selectare pentru a prelungi durata până la un an. Pentru stabilirea perioadei personalizate, accesați Setările.",
"ResendCode": "Retrimite codul",
"UserIsAlreadyRegistered": "Utilizatorul <1>{{email}}</1> este deja înregistrat în acest spațiu DocSpace, introduceţi parola dvs sau reveniți pentru a continua cu un alt e-mail."
"ResendCode": "Retrimite codul"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Запрос на регистрацию",
"RegistrationEmailWatermark": "Регистрационный email",
"RememberHelper": "Время существования сессии по умолчанию составляет 20 минут. Отметьте эту опцию, чтобы установить значение 1 год. Чтобы задать собственное значение, перейдите в настройки.",
"ResendCode": "Отправить код повторно",
"UserIsAlreadyRegistered": "Пользователь <1>{{email}}</1> уже зарегистрирован в этом DocSpace. Введите свой пароль или вернитесь назад, чтобы продолжить с другим адресом электронной почты."
"ResendCode": "Отправить код повторно"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Požiadavka registrácie",
"RegistrationEmailWatermark": "E-mail",
"RememberHelper": "Predvolená doba trvania relácie je 20 minút. Začiarknutím tejto možnosti ju nastavíte na 1 rok. Ak chcete nastaviť vlastnú hodnotu, prejdite do časti Nastavenia.",
"ResendCode": "Poslať kód ešte raz",
"UserIsAlreadyRegistered": "Používateľ <1>{{email}}</1> je už zaregistrovaný v tomto priestore DocSpace, zadajte svoje heslo alebo sa vráťte späť a pokračujte s iným e-mailom."
"ResendCode": "Poslať kód ešte raz"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Zahteva za registracijo",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "Privzeta življenjska doba seje je 20 minut. Preverite to možnost, če jo želite nastaviti na 1 leto. Če želite nastaviti poljubno vrednost, pojdite v Nastavitve.",
"ResendCode": "Ponovno pošlji kodo",
"UserIsAlreadyRegistered": "Uporabnik <1>{{email}}</1> je že registriran v tem prostoru DocSpace. Vnesite svoje geslo ali se vrnite nazaj in nadaljujte z drugim e-mail naslovom."
"ResendCode": "Ponovno pošlji kodo"
}

View File

@ -1,20 +0,0 @@
{
"ErrorConfirmURLError": "Nevažeći email ili istekao link",
"ErrorExpiredActivationLink": "Link je istekao",
"ErrorInvalidActivationLink": "Nevažeći aktivacioni link",
"ErrorNotAllowedOption": "Vaš cenovni plan ne podržava ovu opciju",
"ErrorUserNotFound": "Korisnik nije pronađen",
"InvalidUsernameOrPassword": "Nevažeće korisničko ime ili lozinka",
"LoginWithAccountNotFound": "Ne mogu da pronađem povezani nalog treće strane. Prvo morate da povežete svoj nalog na društvenoj mreži na stranici za uređivanje profila.",
"LoginWithBruteForce": "Ovlašćenje je privremeno blokirano",
"LoginWithBruteForceCaptcha": "Potvrdi da nisi robot",
"RecaptchaInvalid": "Nevažeći Recaptcha",
"SsoAttributesNotFound": "Neuspela autentifikacija (atributi tvrdnje nisu pronađeni)",
"SsoAuthFailed": "Autentifikacija neuspela",
"SsoError": "Interna greška servera",
"SsoSettingsCantCreateUser": "Nije moguće kreirati korisnika sa ovim autentifikacionim tokenom",
"SsoSettingsDisabled": "Jedinstvena prijava (Single sign-on) je onemogućena",
"SsoSettingsEmptyToken": "Autentifikacioni token nije pronađen",
"SsoSettingsNotValidToken": "Nevažeći autentifikacioni token",
"SsoSettingsUserTerminated": "Ovaj korisnik je onemogućen"
}

View File

@ -1,25 +0,0 @@
{
"CodeSubtitle": "Poslali smo šestocifreni kod na {{email}}. Kod ima ograničen period važenja, pa ga unesite što je pre moguće.",
"CodeTitle": "Kod vam je poslat email-om",
"CookieSettingsTitle": "Trajanje sesije",
"ErrorInvalidText": "Za 10 sekundi bićete preusmereni na <1>DocSpace</1>",
"ExpiredCode": "Ovaj kod više ne važi. Zatražite novi kod i pokušajte ponovo.",
"ForgotPassword": "Zaboravili ste vašu lozinku?",
"InvalidCode": "Ovaj kod je nevažeći. Pokušajte ponovo.",
"MessageAuthorize": "Prijavite se da biste nastavili",
"MessageEmailConfirmed": "Vaša email adresa je uspešno aktivirana.",
"MessageSendPasswordRecoveryInstructionsOnEmail": "Molim vas unesite email adresu koju ste koristili za registraciju. Uputstva za oporavak lozinke će tamo biti poslata.",
"NotFoundCode": "Ne možete pronaći kod? Proverite vaš spam folder.",
"PasswordRecoveryTitle": "Oporavak lozinke",
"RecoverAccess": "Obnovi pristup",
"RecoverContactEmailPlaceholder": "Kontakt email",
"RecoverTextBody": "Ako ne možete da se prijavite sa svojim postojećim nalogom ili želite da se registrujete kao novi korisnik, kontaktirajte administratora portala.",
"Register": "Registrujte se",
"RegisterTextBodyAfterDomainsList": "Da biste se registrovali, unesite svoju email adresu i kliknite na Pošalji zahtev. Poruka sa linkom za aktivaciju vašeg naloga biće poslata na navedenu adresu.",
"RegisterTextBodyBeforeDomainsList": "Registracija je dostupna korisnicima sa email nalogom na",
"RegisterTitle": "Zahtev za registraciju",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "Podrazumevano trajanje sesije je 20 minuta. Označite ovu opciju da biste je postavili na 1 godinu. Da biste podesili sopstvenu vrednost, idite na Podešavanja.",
"ResendCode": "Pošalji ponovo kod",
"UserIsAlreadyRegistered": "Korisnik <1>{{email}}</1> je već registrovan u ovom DocSpace-u, unesite svoju lozinku ili se vratite da biste nastavili sa drugim email-om."
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Kayıt talebi",
"RegistrationEmailWatermark": "E-posta",
"RememberHelper": "Varsayılan oturum ömrü 20 dakikadır. 1 yıla ayarlamak için bu seçeneği işaretleyin. Kendi sürenizi ayarlamak için Ayarlar'a gidin.",
"ResendCode": "Kodu yeniden gönder",
"UserIsAlreadyRegistered": "Kullanıcı <1>{{email}}</1> bu DocSpace'e zaten kayıtlı, şifrenizi girin veya başka bir e-posta ile devam etmek için geri dönün."
"ResendCode": "Kodu yeniden gönder"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Запит на реєстрацію",
"RegistrationEmailWatermark": "Електронна пошта",
"RememberHelper": "Термін дії сеансу за замовчуванням складає 20 хвилин. Оберіть цей параметр, щоб задати для нього значення 1 рік. Щоб задати власне значення, перейдіть до налаштувань.",
"ResendCode": "Надіслати код повторно",
"UserIsAlreadyRegistered": "Користувач <1>{{email}}</1> вже зареєстрований у цьому просторі DocSpace. Введіть свій пароль або поверніться й продовжте з іншою адресою електронної пошти."
"ResendCode": "Надіслати код повторно"
}

View File

@ -20,6 +20,5 @@
"RegisterTitle": "Yêu cầu đăng ký",
"RegistrationEmailWatermark": "Email",
"RememberHelper": "Thời lượng của phiên mặc định là 20 phút. Hãy chọn tùy chọn này để đặt thành 1 năm. Để đặt giá trị của riêng bạn, hãy đi đến Cài đặt.",
"ResendCode": "Gửi lại mã",
"UserIsAlreadyRegistered": "Người dùng <1>{{email}} </1> đã được đăng ký trong DocSpace này, hãy nhập mật khẩu của bạn hoặc quay lại để tiếp tục với một email khác."
"ResendCode": "Gửi lại mã"
}

Some files were not shown because too many files have changed in this diff Show More