Merge branch 'release/v1.0.0' of https://github.com/ONLYOFFICE/DocSpace into release/v1.0.0

This commit is contained in:
Maria Sukhova 2023-04-03 18:10:00 +03:00
commit 7716e32573
34 changed files with 776 additions and 263 deletions

View File

@ -26,7 +26,7 @@
"SetAppDescription": "Включена двухфакторная аутентификация. Чтобы продолжить работу в DocSpace, настройте приложение для аутентификации. Вы можете использовать Google Authenticator для <1>Android</1> и <4>iOS</4> или Authenticator для <8>Windows Phone</8>.",
"SetAppInstallDescription": "Для подключения приложения отсканируйте QR-код или вручную введите секретный ключ <1>{{ secretKey }}</1>, а затем введите 6-значный код из приложения в поле ниже.",
"SetAppTitle": "Настройте приложение для аутентификации",
"SuccessDeactivate": "Ваш аккаунт успешно деактивирован. Через 10 секунд вы будете перенаправлены на сайт <1>site</1>.",
"SuccessReactivate": "Ваша учетная запись успешно повторно активирована. Через 10 секунд вы будете перенаправлены на <1>portal</1>.",
"SuccessDeactivate": "Ваш аккаунт успешно деактивирован. Через 10 секунд вы будете перенаправлены на <1>сайт</1>.",
"SuccessReactivate": "Ваша учетная запись успешно повторно активирована. Через 10 секунд вы будете перенаправлены на <1>портал</1>.",
"WelcomeUser": "Добро пожаловать на DocSpace!\nЧтобы приступить к работе зарегистрируйтесь, или войдите через социальную сеть."
}

View File

@ -43,7 +43,8 @@ const StyledRowContainer = styled(RowContainer)`
.user-row {
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
margin-top: -3px;
margin-top: -4px;
${marginStyles}
}
}
@ -52,7 +53,8 @@ const StyledRowContainer = styled(RowContainer)`
.user-row {
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
margin-top: -3px;
margin-top: -4px;
${marginStyles}
}
}
@ -68,6 +70,7 @@ const StyledRowContainer = styled(RowContainer)`
border-bottom: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
padding-bottom: 1px;
padding-top: 1px;
${marginStyles}
}
.user-row::after {
@ -78,10 +81,47 @@ const StyledRowContainer = styled(RowContainer)`
.user-row {
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
margin-top: -3px;
margin-top: -4px;
${marginStyles}
}
}
.row-selected {
.user-row {
.styled-element {
padding-top: 1px;
}
}
}
@media (max-width: 1024px) {
.row-selected {
.user-row {
margin-top: -3px !important;
padding-bottom: 0.8px !important;
padding-top: 0.8px !important;
.styled-element {
padding-bottom: 0.8px;
.owner_icon {
padding-bottom: 0.8px;
}
}
.expandButton {
padding-bottom: 0.8px;
}
.mainIcons {
.paid-badge {
margin-top: -1px;
}
}
}
}
}
`;
const PeopleRowContainer = ({
@ -120,7 +160,7 @@ const PeopleRowContainer = ({
hasMoreFiles={hasMoreAccounts}
itemCount={filterTotal}
filesLength={peopleList.length}
itemHeight={58}
itemHeight={57.6}
>
{peopleList.map((item) => (
<SimpleUserRow

View File

@ -70,9 +70,48 @@ const StyledSimpleUserRow = styled(Row)`
cursor: pointer;
${checkedStyle}
margin-top: -3px;
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
@media (min-width: 1024px) {
margin-top: -4px !important;
padding-top: 1px;
padding-bottom: 1px;
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
border-bottom: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
.styled-checkbox-container {
.styled-element {
padding-top: 1px;
}
}
}
@media (max-width: 1024px) {
margin-top: -3px !important;
padding-top: 0.8px;
padding-bottom: 0.8px;
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
border-bottom: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
.owner_icon {
padding-bottom: 0.8px;
}
.mainIcons {
.paid-badge {
margin-top: -1px;
}
}
.expandButton {
padding-bottom: 0.8px;
}
}
}
`}

View File

@ -43,6 +43,24 @@ const StyledTableContainer = styled(TableContainer)`
.table-container_row-context-menu-wrapper {
${contextCss}
}
.table-container_cell {
.paid-badge {
p {
padding-top: 1px;
}
}
}
}
:hover {
.table-container_cell {
.paid-badge {
p {
padding-top: 1px;
}
}
}
}
.table-row-selected + .table-row-selected {

View File

@ -29,6 +29,12 @@ const StyledPeopleRow = styled(TableRow)`
border-top: ${(props) =>
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
margin-top: -1px;
.paid-badge {
p {
padding-top: 1px;
}
}
}
.table-container_user-name-cell {
@ -338,6 +344,7 @@ const PeopleTableRow = (props) => {
hasAccess={true}
className="table-container_row-checkbox-wrapper"
checked={isChecked}
style={{ borderBottom: "none" }}
>
<div className="table-container_element">{element}</div>
<Checkbox

View File

@ -1,13 +1,27 @@
import React from "react";
import styled from "styled-components";
import ErrorContainer from "@docspace/common/components/ErrorContainer";
import { I18nextProvider, useTranslation } from "react-i18next";
import i18n from "./i18n";
import DocspaceLogo from "../../../DocspaceLogo";
const ErrorUnavailable = () => {
const StyledWrapper = styled.div`
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 64px;
`;
const ErrorUnavailable = ({ logoUrl }) => {
const { t, ready } = useTranslation("Errors");
return ready ? (
<ErrorContainer headerText={t("ErrorUnavailableText")} />
<StyledWrapper>
<DocspaceLogo />
<ErrorContainer headerText={t("ErrorUnavailableText")} />
</StyledWrapper>
) : (
<></>
);

View File

@ -47,6 +47,17 @@ const StyledRowContainer = styled(RowContainer)`
}
}
.row-hotkey-border {
.files-row {
margin-top: -3px;
padding-top: 2px;
.row_content {
padding-top: 1px;
}
}
}
.row-selected:last-child {
.files-row {
border-bottom: ${(props) =>
@ -61,10 +72,18 @@ const StyledRowContainer = styled(RowContainer)`
.files-row {
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
margin-top: -2px;
margin-top: -2.2px;
padding-top: 1px;
padding-bottom: 1px;
${marginStyles};
.row_content {
padding-top: 1px;
.mainIcons {
padding-bottom: 1px;
}
}
}
}
`;
@ -135,7 +154,7 @@ const FilesRowContainer = ({
hasMoreFiles={hasMoreFiles}
draggable
useReactWindow={!withPaging}
itemHeight={59}
itemHeight={59.2}
>
{filesListNode}
</StyledRowContainer>

View File

@ -40,13 +40,27 @@ const StyledSimpleFilesRow = styled(Row)`
cursor: pointer;
${checkedStyle}
margin-top: -2px;
margin-top: -2.2px;
padding-top: 1px;
padding-bottom: 1px;
border-top: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
border-bottom: ${(props) =>
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
.row_content {
padding-top: 1px;
.mainIcons {
padding-bottom: 1px;
}
}
.styled-checkbox-container {
.styled-element {
padding-top: 1px;
}
}
}
`};

View File

@ -48,12 +48,14 @@ const StyledTableRow = styled(TableRow)`
background: ${(props) =>
`${props.theme.filesSection.tableView.row.backgroundActive} !important`};
margin-top: ${(props) => (props.showHotkeyBorder ? "-2px" : "-1px")};
margin-top: ${(props) =>
props.showHotkeyBorder ? "-2px" : "-0.8px"};
${(props) =>
!props.showHotkeyBorder &&
css`
border-top: ${(props) =>
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
`0.8px solid ${props.theme.filesSection.tableView.row.borderColor}`};
`}
}
.table-container_file-name-cell {

View File

@ -31,6 +31,10 @@ const StyledTableContainer = styled(TableContainer)`
.table-row-selected {
.table-container_file-name-cell {
${fileNameCss}
.table-container_element-container,.additional-badges {
padding-top: 1px;
}
}
.table-container_row-context-menu-wrapper {
@ -77,6 +81,14 @@ const StyledTableContainer = styled(TableContainer)`
}
}
.table-hotkey-border {
.table-row {
.tablet-badge {
padding-top: 1px;
}
}
}
.files-item:not(.table-row-selected) + .table-row-selected {
.table-row {
.table-container_file-name-cell {
@ -228,7 +240,7 @@ const Table = ({
useReactWindow={!withPaging}
infoPanelVisible={infoPanelVisible}
columnInfoPanelStorageName={columnInfoPanelStorageName}
itemHeight={49}
itemHeight={48.8}
>
{filesListNode}
</TableBody>

View File

@ -36,6 +36,7 @@ const FileNameCell = ({
className="table-container_element-wrapper"
hasAccess={true}
checked={checked}
style={{ borderBottom: "none" }}
>
<div className="table-container_element-container">
<div className="table-container_element">{element}</div>

View File

@ -44,6 +44,7 @@ const RoomsRowDataComponent = (props) => {
checked={checkedProps}
element={element}
inProgress={inProgress}
isRoomTable={true}
{...props}
/>
<StyledBadgesContainer showHotkeyBorder={showHotkeyBorder}>

View File

@ -428,6 +428,7 @@ class Tile extends React.PureComponent {
this.cm = React.createRef();
this.tile = React.createRef();
this.checkboxContainerRef = React.createRef();
}
onError = () => {
@ -503,7 +504,8 @@ class Tile extends React.PureComponent {
e.target.nodeName !== "INPUT" &&
e.target.nodeName !== "rect" &&
e.target.nodeName !== "path" &&
e.target.nodeName !== "svg"
e.target.nodeName !== "svg" &&
!this.checkboxContainerRef.current?.contains(e.target)
) {
setSelection && setSelection([]);
}
@ -637,7 +639,10 @@ class Tile extends React.PureComponent {
{renderElement && !(!fileExst && id === -1) && !isEdit && (
<>
{!inProgress ? (
<div className="file-icon_container">
<div
className="file-icon_container"
ref={this.checkboxContainerRef}
>
<StyledElement
className="file-icon"
isRoom={isRoom}

View File

@ -81,26 +81,28 @@ const StyledBody = styled.div`
}
`;
const PaymentContainer = ({
isFreeTariff,
isGracePeriod,
theme,
isNotPaidPeriod,
payerEmail,
user,
isPaidPeriod,
currencySymbol,
startValue,
currentTariffPlanTitle,
tariffPlanTitle,
expandArticle,
gracePeriodEndDate,
delayDaysCount,
const PaymentContainer = (props) => {
const {
isFreeTariff,
isGracePeriod,
theme,
isNotPaidPeriod,
payerEmail,
user,
isPaidPeriod,
currencySymbol,
startValue,
currentTariffPlanTitle,
tariffPlanTitle,
expandArticle,
gracePeriodEndDate,
delayDaysCount,
isAlreadyPaid,
paymentDate,
t,
}) => {
isAlreadyPaid,
paymentDate,
t,
isNonProfit,
} = props;
const renderTooltip = () => {
return (
<>
@ -164,6 +166,8 @@ const PaymentContainer = ({
};
const planSuggestion = () => {
if (isNonProfit) return;
if (isFreeTariff) {
return (
<Text
@ -228,6 +232,41 @@ const PaymentContainer = ({
return;
};
const planDescription = () => {
if (isFreeTariff || isNonProfit) return;
if (isGracePeriod)
return (
<Text noSelect fontSize={"14px"} lineHeight={"16px"}>
<Trans t={t} i18nKey="GracePeriodActivatedInfo" ns="Payments">
Grace period activated
<strong>
from {{ fromDate: paymentDate }} to
{{ byDate: gracePeriodEndDate }}
</strong>
(days remaining: {{ delayDaysCount }})
</Trans>{" "}
<Text as="span" fontSize="14px" lineHeight="16px">
{t("GracePeriodActivatedDescription")}
</Text>
</Text>
);
if (isPaidPeriod)
return (
<Text
noSelect
fontSize={"14px"}
lineHeight={"16px"}
className="payment-info_managers-price"
>
<Trans t={t} i18nKey="BusinessFinalDateInfo" ns="Payments">
{{ finalDate: paymentDate }}
</Trans>
</Text>
);
};
const isPayer = user.email === payerEmail;
const isFreeAfterPaidPeriod = isFreeTariff && payerEmail?.length !== 0;
@ -244,7 +283,7 @@ const PaymentContainer = ({
? expiredTitleSubscriptionWarning()
: currentPlanTitle()}
{isAlreadyPaid && (
{!isNonProfit && isAlreadyPaid && (
<PayerInformationContainer
isPayer={isPayer}
isFreeAfterPaidPeriod={isFreeAfterPaidPeriod}
@ -254,59 +293,37 @@ const PaymentContainer = ({
<CurrentTariffContainer />
{planSuggestion()}
{planDescription()}
{isGracePeriod && (
<Text noSelect fontSize={"14px"} lineHeight={"16px"}>
<Trans t={t} i18nKey="GracePeriodActivatedInfo" ns="Payments">
Grace period activated
<strong>
from {{ fromDate: paymentDate }} to
{{ byDate: gracePeriodEndDate }}
</strong>
(days remaining: {{ delayDaysCount }})
</Trans>{" "}
<Text as="span" fontSize="14px" lineHeight="16px">
{t("GracePeriodActivatedDescription")}
</Text>
</Text>
)}
{!isNonProfit &&
!isGracePeriod &&
!isNotPaidPeriod &&
!isFreeAfterPaidPeriod && (
<div className="payment-info_wrapper">
<Text
noSelect
fontWeight={600}
fontSize={"14px"}
className="payment-info_managers-price"
>
<Trans t={t} i18nKey="PerUserMonth" ns="Common">
From {{ currencySymbol }}
{{ price: startValue }} per admin/power user /month
</Trans>
</Text>
{isPaidPeriod && !isFreeTariff && (
<Text
noSelect
fontSize={"14px"}
lineHeight={"16px"}
className="payment-info_managers-price"
>
<Trans t={t} i18nKey="BusinessFinalDateInfo" ns="Payments">
{{ finalDate: paymentDate }}
</Trans>
</Text>
)}
{renderTooltip()}
</div>
)}
{!isGracePeriod && !isNotPaidPeriod && !isFreeAfterPaidPeriod && (
<div className="payment-info_wrapper">
<Text
noSelect
fontWeight={600}
fontSize={"14px"}
className="payment-info_managers-price"
>
<Trans t={t} i18nKey="PerUserMonth" ns="Common">
From {{ currencySymbol }}
{{ price: startValue }} per admin/power user /month
</Trans>
</Text>
{renderTooltip()}
</div>
)}
<div className="payment-info">
<PriceCalculation
t={t}
isPayer={isPayer}
isFreeAfterPaidPeriod={isFreeAfterPaidPeriod}
/>
{!isNonProfit && (
<PriceCalculation
t={t}
isPayer={isPayer}
isFreeAfterPaidPeriod={isFreeAfterPaidPeriod}
/>
)}
<BenefitsContainer t={t} />
</div>
@ -327,7 +344,9 @@ export default inject(({ auth, payments }) => {
} = auth;
const { showText: expandArticle } = settingsStore;
const { isFreeTariff, currentTariffPlanTitle } = currentQuotaStore;
const { isFreeTariff, currentTariffPlanTitle, isNonProfit } =
currentQuotaStore;
const {
isNotPaidPeriod,
isPaidPeriod,
@ -372,5 +391,6 @@ export default inject(({ auth, payments }) => {
currentTariffPlanTitle,
portalTariffStatus,
portalPaymentQuotas,
isNonProfit,
};
})(withRouter(observer(PaymentContainer)));

View File

@ -35,7 +35,7 @@ class HotkeyStore {
scrollToCaret = () => {
const { offsetTop, item } = this.getItemOffset();
const scroll = document.getElementsByClassName("section-scroll")[0];
const scrollRect = scroll.getBoundingClientRect();
const scrollRect = scroll?.getBoundingClientRect();
if (item && item[0]) {
const el = item[0];

View File

@ -3,8 +3,10 @@ import HotkeysReactSvgUrl from "PUBLIC_DIR/images/hotkeys.react.svg?url";
import ProfileReactSvgUrl from "PUBLIC_DIR/images/profile.react.svg?url";
import PaymentsReactSvgUrl from "PUBLIC_DIR/images/payments.react.svg?url";
import HelpCenterReactSvgUrl from "PUBLIC_DIR/images/help.center.react.svg?url";
import SupportReactSvgUrl from "PUBLIC_DIR/images/support.react.svg?url";
import VideoGuidesReactSvgUrl from "PUBLIC_DIR/images/video.guides.react.svg?url";
import EmailReactSvgUrl from "PUBLIC_DIR/images/email.react.svg?url";
import LiveChatReactSvgUrl from "PUBLIC_DIR/images/support.react.svg?url";
import BookTrainingReactSvgUrl from "PUBLIC_DIR/images/book.training.react.svg?url";
//import VideoGuidesReactSvgUrl from "PUBLIC_DIR/images/video.guides.react.svg?url";
import InfoOutlineReactSvgUrl from "PUBLIC_DIR/images/info.outline.react.svg?url";
import LogoutReactSvgUrl from "PUBLIC_DIR/images/logout.react.svg?url";
import { makeAutoObservable } from "mobx";
@ -93,6 +95,10 @@ class ProfileActionsStore {
window.open(helpUrl, "_blank");
};
onLiveChatClick = () => {
//window.open(supportUrl, "_blank");
};
onSupportClick = () => {
const supportUrl = this.authStore.settingsStore.additionalResourcesData
?.feedbackAndSupportUrl;
@ -100,6 +106,13 @@ class ProfileActionsStore {
window.open(supportUrl, "_blank");
};
onBookTraining = () => {
const trainingEmail = this.authStore.settingsStore.additionalResourcesData
?.trainingEmail;
trainingEmail && window.open(`mailto:${trainingEmail}`, "_blank");
};
// onVideoGuidesClick = () => {
// window.open(VIDEO_GUIDES_URL, "_blank");
// };
@ -173,6 +186,29 @@ class ProfileActionsStore {
};
}
// }
let liveChat = null;
if (!isMobile) {
liveChat = {
key: "user-menu-live-chat",
icon: LiveChatReactSvgUrl,
label: t("Common:LiveChat"),
onClick: this.onLiveChatClick,
};
}
let bookTraining = null;
if (!isMobile) {
bookTraining = {
key: "user-menu-book-training",
icon: BookTrainingReactSvgUrl,
label: t("Common:BookTraining"),
onClick: this.onBookTraining,
};
}
const actions = [
{
key: "user-menu-profile",
@ -187,18 +223,16 @@ class ProfileActionsStore {
label: t("Common:PaymentsTitle"),
onClick: this.onPaymentsClick,
},
{
isSeparator: true,
key: "separator1",
},
{
key: "user-menu-help-center",
icon: HelpCenterReactSvgUrl,
label: t("Common:HelpCenter"),
onClick: this.onHelpCenterClick,
},
{
key: "user-menu-support",
icon: SupportReactSvgUrl,
label: t("Common:FeedbackAndSupport"),
onClick: this.onSupportClick,
},
// {
// key: "user-menu-video",
// icon: VideoGuidesReactSvgUrl,
@ -206,6 +240,18 @@ class ProfileActionsStore {
// onClick: this.onVideoGuidesClick,
// },
hotkeys,
{
isSeparator: true,
key: "separator2",
},
liveChat,
{
key: "user-menu-support",
icon: EmailReactSvgUrl,
label: t("Common:FeedbackAndSupport"),
onClick: this.onSupportClick,
},
bookTraining,
{
key: "user-menu-about",
icon: InfoOutlineReactSvgUrl,
@ -214,7 +260,7 @@ class ProfileActionsStore {
},
{
isSeparator: true,
key: "separator",
key: "separator3",
},
{
key: "user-menu-logout",
@ -226,7 +272,7 @@ class ProfileActionsStore {
];
if (debugInfo) {
actions.splice(3, 0, {
actions.splice(4, 0, {
key: "user-menu-debug",
icon: InfoOutlineReactSvgUrl,
label: "Debug Info",

View File

@ -0,0 +1,23 @@
import styled, { css } from "styled-components";
const StyledAlertComponent = styled.div`
position: relative;
border: ${(props) => `1px solid ${props.borderColor}`};
border-radius: 6px;
margin: 32px 0px;
padding: 12px;
${(props) => !!props.onClick && "cursor:pointer"};
display: grid;
grid-template-columns: ${(props) =>
props.needArrowIcon ? "1fr 16px" : "1fr"};
.alert-component_title {
color: ${(props) => props.titleColor};
}
.alert-component_icons {
margin: auto 0;
}
`;
export { StyledAlertComponent };

View File

@ -0,0 +1,106 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
import styled from "styled-components";
import Text from "@docspace/components/text";
import commonIconsStyles from "@docspace/components/utils/common-icons-style";
import ArrowRightIcon from "PUBLIC_DIR/images/arrow.right.react.svg";
import CrossReactSvg from "PUBLIC_DIR/images/cross.react.svg";
import Loaders from "../Loaders";
import { StyledAlertComponent } from "./StyledComponent";
import Link from "@docspace/components/link";
const StyledArrowRightIcon = styled(ArrowRightIcon)`
margin: auto 0;
path {
fill: ${(props) => props.theme.alertComponent.iconColor};
}
`;
const StyledCrossIcon = styled(CrossReactSvg)`
position: absolute;
right: 0px;
margin-right: 8px;
margin-top: 8px;
cursor: pointer;
${commonIconsStyles}
path {
fill: ${(props) => props.color};
}
`;
const AlertComponent = (props) => {
const {
id,
description,
title,
titleFontSize,
additionalDescription,
needArrowIcon = false,
needCloseIcon = false,
link,
linkColor,
linkTitle,
onAlertClick,
onCloseClick,
titleColor,
borderColor,
theme,
} = props;
return (
<StyledAlertComponent
theme={theme}
titleColor={titleColor}
borderColor={borderColor}
onClick={onAlertClick}
needArrowIcon={needArrowIcon}
id={id}
>
<div>
<Text
className="alert-component_title"
fontSize={titleFontSize ?? "12px"}
fontWeight={600}
>
{title}
</Text>
{additionalDescription && (
<Text fontWeight={600}>{additionalDescription}</Text>
)}
<Text
noSelect
fontSize="12px"
color={theme.alertComponent.descriptionColor}
>
{description}
</Text>
{link && (
<Link type="page" href={link} noHover color={linkColor}>
{linkTitle}
</Link>
)}
</div>
{needCloseIcon && (
<StyledCrossIcon size="extraSmall" onClick={onCloseClick} />
)}
{needArrowIcon && (
<StyledArrowRightIcon className="alert-component_arrow" />
)}
</StyledAlertComponent>
);
};
export default withRouter(
inject(({ auth }) => {
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme,
};
})(observer(AlertComponent))
);

View File

@ -15,6 +15,7 @@ import SubArticleMainButton from "./sub-components/article-main-button";
import SubArticleBody from "./sub-components/article-body";
import ArticleProfile from "./sub-components/article-profile";
import ArticlePaymentAlert from "./sub-components/article-payment-alert";
import ArticleTeamTrainingAlert from "./sub-components/article-team-training";
import { StyledArticle } from "./styled-article";
import HideArticleMenuButton from "./sub-components/article-hide-menu-button";
import Portal from "@docspace/components/portal";
@ -40,6 +41,8 @@ const Article = ({
withSendAgain,
mainBarVisible,
isBannerVisible,
isNonProfit,
isTeamTrainingAlertAvailable,
...rest
}) => {
const [articleHeaderContent, setArticleHeaderContent] = React.useState(null);
@ -178,6 +181,7 @@ const Article = ({
<ArticleProfile showText={showText} />
)}
{isPaymentPageAvailable &&
!isNonProfit &&
(isFreeTariff || isGracePeriod) &&
showText && (
<ArticlePaymentAlert
@ -185,6 +189,9 @@ const Article = ({
toggleArticleOpen={toggleArticleOpen}
/>
)}
{isTeamTrainingAlertAvailable && showText && (
<ArticleTeamTrainingAlert />
)}
</SubArticleBody>
</StyledArticle>
{articleOpen && (isMobileOnly || window.innerWidth <= 375) && (
@ -247,9 +254,10 @@ export default inject(({ auth }) => {
currentTariffStatusStore,
userStore,
isPaymentPageAvailable,
isTeamTrainingAlertAvailable,
bannerStore,
} = auth;
const { isFreeTariff } = currentQuotaStore;
const { isFreeTariff, isNonProfit } = currentQuotaStore;
const { isGracePeriod } = currentTariffStatusStore;
const { withSendAgain } = userStore;
@ -283,5 +291,7 @@ export default inject(({ auth }) => {
withSendAgain,
mainBarVisible,
isBannerVisible,
isNonProfit,
isTeamTrainingAlertAvailable,
};
})(observer(Article));

View File

@ -2,20 +2,11 @@ import React, { useEffect } from "react";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
import { useTranslation, Trans } from "react-i18next";
import Text from "@docspace/components/text";
import ArrowRightIcon from "PUBLIC_DIR/images/arrow.right.react.svg";
import { StyledArticlePaymentAlert } from "../styled-article";
import styled from "styled-components";
import { combineUrl } from "@docspace/common/utils";
import history from "@docspace/common/history";
import Loaders from "../../Loaders";
const StyledArrowRightIcon = styled(ArrowRightIcon)`
margin: auto 0;
path {
fill: ${(props) => props.color};
}
`;
import AlertComponent from "../../AlertComponent";
const PROXY_BASE_URL = combineUrl(
window.DocSpaceConfig?.proxy?.url,
@ -52,52 +43,47 @@ const ArticlePaymentAlert = ({
toggleArticleOpen();
};
const title = isFreeTariff ? (
<Trans t={t} i18nKey="FreeStartupPlan" ns="Common">
{{ planName: currentTariffPlanTitle }}
</Trans>
) : (
t("Common:LatePayment")
);
const description = isFreeTariff
? pricePerManager && (
<Trans t={t} i18nKey="PerUserMonth" ns="Common">
From {{ currencySymbol }}
{{ price: pricePerManager }} per admin/power user /month
</Trans>
)
: t("Common:PayBeforeTheEndGracePeriod");
const additionalDescription = isFreeTariff
? t("Common:ActivateBusinessPlan", { planName: tariffPlanTitle })
: t("Common:GracePeriodActivated");
const color = isFreeTariff
? theme.catalog.paymentAlert.color
: theme.catalog.paymentAlert.warningColor;
const isShowLoader = !ready;
return isShowLoader ? (
<Loaders.Rectangle width="210px" height="88px" />
) : (
<StyledArticlePaymentAlert
onClick={onClick}
isFreeTariff={isFreeTariff}
theme={theme}
<AlertComponent
id="document_catalog-payment-alert"
>
<div>
<Text className="article-payment_border">
{isFreeTariff ? (
<Trans t={t} i18nKey="FreeStartupPlan" ns="Common">
{{ planName: currentTariffPlanTitle }}
</Trans>
) : (
t("Common:LatePayment")
)}
</Text>
<Text fontWeight={600}>
{isFreeTariff
? t("Common:ActivateBusinessPlan", { planName: tariffPlanTitle })
: t("Common:GracePeriodActivated")}
</Text>
<Text noSelect fontSize={"12px"}>
{isFreeTariff ? (
<>
{pricePerManager ? (
<Trans t={t} i18nKey="PerUserMonth" ns="Common">
From {{ currencySymbol }}
{{ price: pricePerManager }} per admin/power user /month
</Trans>
) : (
<></>
)}
</>
) : (
t("Common:PayBeforeTheEndGracePeriod")
)}
</Text>
</div>
<StyledArrowRightIcon />
</StyledArticlePaymentAlert>
borderColor={color}
titleColor={color}
onAlertClick={onClick}
title={title}
titleFontSize="11px"
description={description}
additionalDescription={additionalDescription}
needArrowIcon
/>
);
};
@ -105,7 +91,7 @@ export default withRouter(
inject(({ auth }) => {
const { paymentQuotasStore, currentQuotaStore, settingsStore } = auth;
const { currentTariffPlanTitle } = currentQuotaStore;
const { theme } = auth;
const { theme } = settingsStore;
const {
setPortalPaymentQuotas,
planCost,

View File

@ -0,0 +1,56 @@
import React, { useState } from "react";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router";
import { useTranslation } from "react-i18next";
import AlertComponent from "../../AlertComponent";
const ArticleTeamTrainingAlert = ({
theme,
trainingEmail,
organizationName,
}) => {
const { t, ready } = useTranslation("Common");
const [isClose, setIsClose] = useState(
localStorage.getItem("teamTrainingAlertClose")
);
const onClick = () => {
localStorage.setItem("teamTrainingAlertClose", true);
setIsClose(true);
};
if (isClose) return <></>;
const isShowLoader = !ready;
return isShowLoader ? (
<Loaders.Rectangle width="210px" height="88px" />
) : (
<AlertComponent
titleColor={theme.catalog.teamTrainingAlert.titleColor}
linkColor={theme.catalog.teamTrainingAlert.linkColor}
borderColor={theme.catalog.teamTrainingAlert.borderColor}
title={t("Common:UseLikePro", { organizationName })}
description={t("Common:BookTeamTraining")}
link={`mailto:${trainingEmail}`}
linkTitle={t("Common:BookNow")}
onCloseClick={onClick}
needCloseIcon
/>
);
};
export default withRouter(
inject(({ auth }) => {
const { settingsStore } = auth;
const { theme, additionalResourcesData, organizationName } = settingsStore;
return {
theme,
trainingEmail: additionalResourcesData.trainingEmail,
organizationName,
};
})(observer(ArticleTeamTrainingAlert))
);

View File

@ -130,7 +130,9 @@ class AuthStore {
if (!user) return false;
return !user.isAdmin && !user.isOwner && !user.isVisitor;
return (
!user.isAdmin && !user.isOwner && !user.isVisitor && !user.isCollaborator
);
}
get isPaymentPageAvailable() {
@ -141,6 +143,14 @@ class AuthStore {
return user.isOwner || user.isAdmin;
}
get isTeamTrainingAlertAvailable() {
const { user } = this.userStore;
if (!user) return false;
return user.isOwner || user.isAdmin || this.isRoomAdmin;
}
login = async (user, hash, session = true) => {
try {
const response = await api.user.login(user, hash, session);

View File

@ -207,6 +207,10 @@ class QuotasStore {
);
}
get isNonProfit() {
return this.currentPortalQuota?.nonProfit;
}
setPortalQuotaValue = (res) => {
this.currentPortalQuota = res;
this.currentPortalQuotaFeatures = res.features;

View File

@ -389,6 +389,9 @@ class SettingsStore {
setAdditionalResourcesData = (data) => {
this.additionalResourcesData = data;
if (!this.additionalResourcesData?.trainingEmail) {
this.additionalResourcesData.trainingEmail = "training@onlyoffice.com";
}
};
setAdditionalResourcesIsDefault = (additionalResourcesIsDefault) => {

View File

@ -1912,9 +1912,18 @@ const Base = {
paymentAlert: {
color: "#ed7309",
warningColor: "#F21C0E",
border: "1px solid #ed7309",
warningBorder: "1px solid #F21C0E",
},
teamTrainingAlert: {
titleColor: "#388BDE",
borderColor: "#388BDE",
linkColor: "#5299E0",
},
},
alertComponent: {
descriptionColor: "#555F65",
iconColor: "#657077",
},
catalogItem: {

View File

@ -1903,9 +1903,18 @@ const Dark = {
paymentAlert: {
color: "#ed7309",
warningColor: "#E06451",
border: "1px solid #ed7309",
warningBorder: "1px solid #E06451",
},
teamTrainingAlert: {
titleColor: "#FFFFFF",
borderColor: "#388BDE",
linkColor: "#5299E0",
},
},
alertComponent: {
descriptionColor: "#ADADAD",
iconColor: "#ADADAD",
},
catalogItem: {

View File

@ -1,6 +1,7 @@
import { css } from "styled-components";
const iconSizes = {
extraSmall: 8,
small: 12,
medium: 16,
big: 24,
@ -14,6 +15,7 @@ const getSizeStyle = (size) => {
height: 100%;
}
`;
case "extraSmall":
case "small":
case "medium":
case "big":

View File

@ -60,10 +60,17 @@ public class CountRoomCheckerStatistic : ITenantQuotaFeatureStat<CountRoomFeatur
public async Task<int> GetValue()
{
var daoFactory = _serviceProvider.GetService<IDaoFactory>();
var folderDao = _serviceProvider.GetService<IFolderDao<int>>();
var globalFolderHelper = _serviceProvider.GetService<GlobalFolderHelper>();
var globalFolder = _serviceProvider.GetService<GlobalFolder>();
var parentId = await globalFolderHelper.GetFolderVirtualRooms<int>();
var parentId = await globalFolder.GetFolderVirtualRoomsAsync(daoFactory, false);
if (parentId == default)
{
return 0;
}
return await folderDao.GetFoldersAsync(parentId).CountAsync();
}
}

View File

@ -31,7 +31,7 @@ public class GlobalNotify
{
public ILogger Logger { get; set; }
private readonly ICacheNotify<AscCacheItem> _notify;
public GlobalNotify(ICacheNotify<AscCacheItem> notify, ILoggerProvider options, CoreBaseSettings coreBaseSettings)
{
_notify = notify;
@ -41,7 +41,7 @@ public class GlobalNotify
ClearCache();
}
}
private void ClearCache()
{
try
@ -105,7 +105,7 @@ public class Global
private readonly DisplayUserSettingsHelper _displayUserSettingsHelper;
private readonly CustomNamingPeople _customNamingPeople;
private readonly FileSecurityCommon _fileSecurityCommon;
public Global(
IConfiguration configuration,
AuthContext authContext,
@ -133,17 +133,17 @@ public class Global
}
}
#region Property
#region Property
public DocThumbnailExtension DocThumbnailExtension;
public ThumbnailExtension ThumbnailExtension;
public const int MaxTitle = 170;
public static readonly Regex InvalidTitleChars = new Regex("[\t*\\+:\"<>?|\\\\/\\p{Cs}]");
public bool EnableUploadFilter => bool.TrueString.Equals(_configuration["files:upload-filter"] ?? "false", StringComparison.InvariantCultureIgnoreCase);
public TimeSpan StreamUrlExpire
{
get
@ -157,27 +157,27 @@ public class Global
return TimeSpan.FromMinutes(validateTimespan);
}
}
public bool IsDocSpaceAdministrator => _fileSecurityCommon.IsDocSpaceAdministrator(_authContext.CurrentAccount.ID);
public string GetDocDbKey()
{
const string dbKey = "UniqueDocument";
var resultKey = _coreSettings.GetSetting(dbKey);
if (!string.IsNullOrEmpty(resultKey))
{
return resultKey;
}
resultKey = Guid.NewGuid().ToString();
_coreSettings.SaveSetting(dbKey, resultKey);
return resultKey;
}
#endregion
#endregion
public static string ReplaceInvalidCharsAndTruncate(string title)
{
if (string.IsNullOrEmpty(title))
@ -201,27 +201,27 @@ public class Global
return InvalidTitleChars.Replace(title, "_");
}
public string GetUserName(Guid userId, bool alive = false)
{
if (userId.Equals(_authContext.CurrentAccount.ID))
{
return FilesCommonResource.Author_Me;
}
if (userId.Equals(ASC.Core.Configuration.Constants.Guest.ID))
{
return FilesCommonResource.Guest;
}
var userInfo = _userManager.GetUsers(userId);
if (userInfo.Equals(Constants.LostUser))
{
return alive ? FilesCommonResource.Guest : _customNamingPeople.Substitute<FilesCommonResource>("ProfileRemoved");
}
}
return userInfo.DisplayUserName(false, _displayUserSettingsHelper);
}
}
}
[Scope]
@ -229,18 +229,18 @@ public class GlobalStore
{
private readonly StorageFactory _storageFactory;
private readonly TenantManager _tenantManager;
public GlobalStore(StorageFactory storageFactory, TenantManager tenantManager)
{
_storageFactory = storageFactory;
_tenantManager = tenantManager;
}
public IDataStore GetStore(bool currentTenant = true)
{
return _storageFactory.GetStorage(currentTenant ? _tenantManager.GetCurrentTenant().Id : null, FileConstant.StorageModule);
}
public IDataStore GetStoreTemplate()
{
return _storageFactory.GetStorage(null, FileConstant.StorageTemplate);
@ -252,18 +252,18 @@ public class GlobalSpace
{
private readonly FilesSpaceUsageStatManager _filesSpaceUsageStatManager;
private readonly AuthContext _authContext;
public GlobalSpace(FilesSpaceUsageStatManager filesSpaceUsageStatManager, AuthContext authContext)
{
_filesSpaceUsageStatManager = filesSpaceUsageStatManager;
_authContext = authContext;
}
public Task<long> GetUserUsedSpaceAsync()
{
return GetUserUsedSpaceAsync(_authContext.CurrentAccount.ID);
}
public Task<long> GetUserUsedSpaceAsync(Guid userId)
{
return _filesSpaceUsageStatManager.GetUserSpaceUsageAsync(userId);
@ -283,7 +283,7 @@ public class GlobalFolder
private readonly GlobalStore _globalStore;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
public GlobalFolder(
CoreBaseSettings coreBaseSettings,
WebItemManager webItemManager,
@ -294,56 +294,56 @@ public class GlobalFolder
SettingsManager settingsManager,
GlobalStore globalStore,
ILoggerProvider options,
IServiceProvider serviceProvider
IServiceProvider serviceProvider
)
{
_coreBaseSettings = coreBaseSettings;
_webItemManager = webItemManager;
_webItemSecurity = webItemSecurity;
_authContext = authContext;
_tenantManager = tenantManager;
_userManager = userManager;
_settingsManager = settingsManager;
_globalStore = globalStore;
_serviceProvider = serviceProvider;
_coreBaseSettings = coreBaseSettings;
_webItemManager = webItemManager;
_webItemSecurity = webItemSecurity;
_authContext = authContext;
_tenantManager = tenantManager;
_userManager = userManager;
_settingsManager = settingsManager;
_globalStore = globalStore;
_serviceProvider = serviceProvider;
_logger = options.CreateLogger("ASC.Files");
}
internal static readonly IDictionary<int, int> ProjectsRootFolderCache =
new ConcurrentDictionary<int, int>(); /*Use SYNCHRONIZED for cross thread blocks*/
public async ValueTask<int> GetFolderProjectsAsync(IDaoFactory daoFactory)
{
if (_coreBaseSettings.Personal)
{
return default;
}
if (_webItemManager[WebItemManager.ProjectsProductID].IsDisabled(_webItemSecurity, _authContext))
{
return default;
}
var folderDao = daoFactory.GetFolderDao<int>();
if (!ProjectsRootFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var result))
{
result = await folderDao.GetFolderIDProjectsAsync(true);
ProjectsRootFolderCache[_tenantManager.GetCurrentTenant().Id] = result;
}
}
return result;
}
public async ValueTask<T> GetFolderProjectsAsync<T>(IDaoFactory daoFactory)
{
return (T)Convert.ChangeType(await GetFolderProjectsAsync(daoFactory), typeof(T));
}
internal static readonly ConcurrentDictionary<string, int> DocSpaceFolderCache =
new ConcurrentDictionary<string, int>();
public async ValueTask<int> GetFolderVirtualRoomsAsync(IDaoFactory daoFactory)
public async ValueTask<int> GetFolderVirtualRoomsAsync(IDaoFactory daoFactory, bool createIfNotExist = true)
{
if (_coreBaseSettings.DisableDocSpace)
{
@ -352,10 +352,15 @@ public class GlobalFolder
var key = $"vrooms/{_tenantManager.GetCurrentTenant().Id}";
if (!DocSpaceFolderCache.TryGetValue(key, out var result))
if (DocSpaceFolderCache.TryGetValue(key, out var result))
{
result = await daoFactory.GetFolderDao<int>().GetFolderIDVirtualRooms(true);
return result;
}
result = await daoFactory.GetFolderDao<int>().GetFolderIDVirtualRooms(createIfNotExist);
if (result != default)
{
DocSpaceFolderCache[key] = result;
}
@ -390,22 +395,22 @@ public class GlobalFolder
{
return (T)Convert.ChangeType(await GetFolderArchiveAsync(daoFactory), typeof(T));
}
internal static readonly ConcurrentDictionary<string, Lazy<int>> UserRootFolderCache =
new ConcurrentDictionary<string, Lazy<int>>(); /*Use SYNCHRONIZED for cross thread blocks*/
public T GetFolderMy<T>(FileMarker fileMarker, IDaoFactory daoFactory)
{
return (T)Convert.ChangeType(GetFolderMy(fileMarker, daoFactory), typeof(T));
}
public int GetFolderMy(FileMarker fileMarker, IDaoFactory daoFactory)
{
if (!_authContext.IsAuthenticated)
{
return default;
}
if (_userManager.IsUser(_authContext.CurrentAccount.ID))
{
return default;
@ -417,8 +422,8 @@ public class GlobalFolder
return myFolderId.Value;
}
protected internal void SetFolderMy(object value)
protected internal void SetFolderMy(object value)
{
var cacheKey = string.Format("my/{0}/{1}", _tenantManager.GetCurrentTenant().Id, value);
UserRootFolderCache.Remove(cacheKey, out _);
@ -441,23 +446,23 @@ public class GlobalFolder
return false;
}
internal static readonly IDictionary<int, int> CommonFolderCache =
new ConcurrentDictionary<int, int>(); /*Use SYNCHRONIZED for cross thread blocks*/
public async ValueTask<T> GetFolderCommonAsync<T>(FileMarker fileMarker, IDaoFactory daoFactory)
{
return (T)Convert.ChangeType(await GetFolderCommonAsync(fileMarker, daoFactory), typeof(T));
}
public async ValueTask<int> GetFolderCommonAsync(FileMarker fileMarker, IDaoFactory daoFactory)
{
if (_coreBaseSettings.Personal)
{
return default;
}
if (!CommonFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var commonFolderId))
if (!CommonFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var commonFolderId))
{
commonFolderId = await GetFolderIdAndProccessFirstVisitAsync(fileMarker, daoFactory, false);
if (!Equals(commonFolderId, 0))
@ -471,7 +476,7 @@ public class GlobalFolder
internal static readonly IDictionary<int, int> ShareFolderCache =
new ConcurrentDictionary<int, int>(); /*Use SYNCHRONIZED for cross thread blocks*/
public async ValueTask<int> GetFolderShareAsync(IDaoFactory daoFactory)
{
if (_coreBaseSettings.Personal)
@ -483,12 +488,12 @@ public class GlobalFolder
{
return default;
}
if (!ShareFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var sharedFolderId))
if (!ShareFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var sharedFolderId))
{
sharedFolderId = await daoFactory.GetFolderDao<int>().GetFolderIDShareAsync(true);
if (!sharedFolderId.Equals(default))
if (!sharedFolderId.Equals(default))
{
ShareFolderCache[_tenantManager.GetCurrentTenant().Id] = sharedFolderId;
}
@ -496,7 +501,7 @@ public class GlobalFolder
return sharedFolderId;
}
public async ValueTask<T> GetFolderShareAsync<T>(IDaoFactory daoFactory)
{
return (T)Convert.ChangeType(await GetFolderShareAsync(daoFactory), typeof(T));
@ -615,10 +620,10 @@ public class GlobalFolder
return privacyFolderId;
}
internal static readonly IDictionary<string, object> TrashFolderCache =
new ConcurrentDictionary<string, object>(); /*Use SYNCHRONIZED for cross thread blocks*/
public async Task<T> GetFolderTrashAsync<T>(IDaoFactory daoFactory)
{
return (T)Convert.ChangeType(await GetFolderTrashAsync(daoFactory), typeof(T));
@ -630,8 +635,8 @@ public class GlobalFolder
{
return null;
}
var cacheKey = string.Format("trash/{0}/{1}", _tenantManager.GetCurrentTenant().Id, _authContext.CurrentAccount.ID);
var cacheKey = string.Format("trash/{0}/{1}", _tenantManager.GetCurrentTenant().Id, _authContext.CurrentAccount.ID);
if (!TrashFolderCache.TryGetValue(cacheKey, out var trashFolderId))
{
@ -641,8 +646,8 @@ public class GlobalFolder
return trashFolderId;
}
protected internal void SetFolderTrash(object value)
protected internal void SetFolderTrash(object value)
{
var cacheKey = string.Format("trash/{0}/{1}", _tenantManager.GetCurrentTenant().Id, value);
TrashFolderCache.Remove(cacheKey);
@ -652,30 +657,30 @@ public class GlobalFolder
{
var folderDao = (FolderDao)daoFactory.GetFolderDao<int>();
var fileDao = (FileDao)daoFactory.GetFileDao<int>();
var id = my ? await folderDao.GetFolderIDUserAsync(false) : await folderDao.GetFolderIDCommonAsync(false);
if (Equals(id, 0)) //TODO: think about 'null'
{
id = my ? await folderDao.GetFolderIDUserAsync(true) : await folderDao.GetFolderIDCommonAsync(true);
//Copy start document
if (_settingsManager.LoadForDefaultTenant<AdditionalWhiteLabelSettings>().StartDocsEnabled)
{
try
{
var storeTemplate = _globalStore.GetStoreTemplate();
var culture = my ? _userManager.GetUsers(_authContext.CurrentAccount.ID).GetCulture() : _tenantManager.GetCurrentTenant().GetCulture();
var path = FileConstant.StartDocPath + culture + "/";
if (!await storeTemplate.IsDirectoryAsync(path))
{
path = FileConstant.StartDocPath + "en-US/";
}
path += my ? "my/" : "corporate/";
await SaveStartDocumentAsync(fileMarker, folderDao, fileDao, id, path, storeTemplate);
}
catch (Exception ex)
@ -684,31 +689,31 @@ public class GlobalFolder
}
}
}
return id;
}
private async Task SaveStartDocumentAsync(FileMarker fileMarker, FolderDao folderDao, FileDao fileDao, int folderId, string path, IDataStore storeTemplate)
{
var files = await storeTemplate.ListFilesRelativeAsync("", path, "*", false).ToListAsync();
foreach (var file in files)
foreach (var file in files)
{
await SaveFileAsync(fileMarker, fileDao, folderId, path + file, storeTemplate, files);
await SaveFileAsync(fileMarker, fileDao, folderId, path + file, storeTemplate, files);
}
await foreach (var folderName in storeTemplate.ListDirectoriesRelativeAsync(path, false))
{
var folder = _serviceProvider.GetService<Folder<int>>();
folder.Title = folderName;
folder.ParentId = folderId;
var subFolderId = await folderDao.SaveFolderAsync(folder);
await SaveStartDocumentAsync(fileMarker, folderDao, fileDao, subFolderId, path + folderName + "/", storeTemplate);
}
}
private async Task SaveFileAsync(FileMarker fileMarker, FileDao fileDao, int folder, string filePath, IDataStore storeTemp, IEnumerable<string> files)
private async Task SaveFileAsync(FileMarker fileMarker, FileDao fileDao, int folder, string filePath, IDataStore storeTemp, IEnumerable<string> files)
{
try
{
@ -718,7 +723,7 @@ public class GlobalFolder
if (FileUtility.GetFileExtension(filePath) == "." + ext
&& files.Contains(Regex.Replace(fileName, "\\." + ext + "$", "")))
return;
}
}
var file = _serviceProvider.GetService<File<int>>();
@ -739,7 +744,7 @@ public class GlobalFolder
_logger.ErrorSaveFile(ex);
}
}
public bool IsOutsider => _userManager.IsOutsider(_authContext.CurrentAccount.ID);
}
@ -749,14 +754,14 @@ public class GlobalFolderHelper
private readonly FileMarker _fileMarker;
private readonly IDaoFactory _daoFactory;
private readonly GlobalFolder _globalFolder;
public GlobalFolderHelper(FileMarker fileMarker, IDaoFactory daoFactory, GlobalFolder globalFolder)
{
_fileMarker = fileMarker;
_daoFactory = daoFactory;
_globalFolder = globalFolder;
}
public ValueTask<int> FolderProjectsAsync => _globalFolder.GetFolderProjectsAsync(_daoFactory);
public ValueTask<int> FolderCommonAsync => _globalFolder.GetFolderCommonAsync(_fileMarker, _daoFactory);
public int FolderMy => _globalFolder.GetFolderMy(_fileMarker, _daoFactory);
@ -806,16 +811,16 @@ public class GlobalFolderHelper
{
_globalFolder.SetFolderMy(val);
}
public async ValueTask<T> GetFolderShareAsync<T>()
{
return (T)Convert.ChangeType(await FolderShareAsync, typeof(T));
}
public ValueTask<int> FolderShareAsync => _globalFolder.GetFolderShareAsync(_daoFactory);
public object FolderTrash
{
get => _globalFolder.GetFolderTrashAsync(_daoFactory).Result;
set => _globalFolder.SetFolderTrash(value);
}
}
}
}

View File

@ -842,6 +842,15 @@ namespace ASC.Files.Core.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to archive the room.
/// </summary>
public static string ErrorMessage_SecurityException_ArchiveRoom {
get {
return ResourceManager.GetString("ErrorMessage_SecurityException_ArchiveRoom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have permission to copy to this folder.
/// </summary>
@ -870,11 +879,11 @@ namespace ASC.Files.Core.Resources {
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to archive the room.
/// Looks up a localized string similar to You don&apos;t have enough permission to unarchive the room.
/// </summary>
public static string ErrorMessage_UnarchiveRoom {
public static string ErrorMessage_SecurityException_UnarchiveRoom {
get {
return ResourceManager.GetString("ErrorMessage_UnarchiveRoom", resourceCulture);
return ResourceManager.GetString("ErrorMessage_SecurityException_UnarchiveRoom", resourceCulture);
}
}

View File

@ -375,8 +375,8 @@
<data name="ErrorMessage_SecurityException_MoveToFolder" xml:space="preserve">
<value>You don't have permission to move to this folder</value>
</data>
<data name="ErrorMessage_UnarchiveRoom" xml:space="preserve">
<value>You don't have enough permission to archive the room</value>
<data name="ErrorMessage_SecurityException_UnarchiveRoom" xml:space="preserve">
<value>You don't have enough permission to unarchive the room</value>
</data>
<data name="ErrorMessage_UpdateArchivedRoom" xml:space="preserve">
<value>You cannot edit archived rooms</value>
@ -472,4 +472,7 @@ Highest compatibility with docx, xlsx, pptx. </value>
<data name="AceStatusEnum_Collaborator" xml:space="preserve">
<value>Collaborator</value>
</data>
<data name="ErrorMessage_SecurityException_ArchiveRoom" xml:space="preserve">
<value>You don't have enough permission to archive the room</value>
</data>
</root>

View File

@ -146,6 +146,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
if (0 < Folders.Count)
{
var firstFolder = await FolderDao.GetFolderAsync(Folders[0]);
var isRoom = DocSpaceHelper.IsRoom(firstFolder.FolderType);
if (_copy && !await FilesSecurity.CanCopyAsync(firstFolder))
{
@ -155,7 +156,17 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
if (!_copy && !await FilesSecurity.CanMoveAsync(firstFolder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
if (isRoom)
{
this[Err] = toFolder.FolderType == FolderType.Archive
? FilesCommonResource.ErrorMessage_SecurityException_ArchiveRoom
: FilesCommonResource.ErrorMessage_SecurityException_UnarchiveRoom;
}
else
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
return;
}
@ -187,7 +198,9 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
if (!_copy && !await fileSecurity.CanMoveToAsync(toFolder))
{
this[Err] = FilesCommonResource.ErrorMessage_SecurityException_MoveToFolder;
this[Err] = toFolder.FolderType == FolderType.VirtualRooms ? FilesCommonResource.ErrorMessage_SecurityException_UnarchiveRoom :
toFolder.FolderType == FolderType.Archive ? FilesCommonResource.ErrorMessage_SecurityException_UnarchiveRoom :
FilesCommonResource.ErrorMessage_SecurityException_MoveToFolder;
return;
}
@ -270,7 +283,16 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
else if (!copy && checkPermissions && !canMoveOrCopy)
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
if (isRoom)
{
this[Err] = toFolder.FolderType == FolderType.Archive
? FilesCommonResource.ErrorMessage_SecurityException_ArchiveRoom
: FilesCommonResource.ErrorMessage_SecurityException_UnarchiveRoom;
}
else
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
}
else if (!isRoom && (toFolder.FolderType == FolderType.VirtualRooms || toFolder.RootFolderType == FolderType.Archive))
{
@ -278,7 +300,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
else if (isRoom && toFolder.FolderType != FolderType.VirtualRooms && toFolder.FolderType != FolderType.Archive)
{
this[Err] = FilesCommonResource.ErrorMessage_UnarchiveRoom;
this[Err] = FilesCommonResource.ErrorMessage_SecurityException_UnarchiveRoom;
}
else if (!isRoom && folder.Private && !await CompliesPrivateRoomRulesAsync(folder, toFolderParents))
{

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.25807 1.11309C7.53755 0.967522 7.86929 0.962209 8.1533 1.09875L15.4333 4.59875C15.7797 4.76529 16 5.11564 16 5.5C16 5.88436 15.7797 6.23471 15.4333 6.40125L8.1533 9.90125C7.86929 10.0378 7.53755 10.0325 7.25807 9.88691L2 7.14834V9C2 9.55228 1.55228 10 1 10C0.447715 10 0 9.55228 0 9V5.5C0 5.12717 0.207401 4.78531 0.538066 4.61309L7.25807 1.11309ZM7.73791 7.88182L12.6921 5.5L7.73791 3.11818L3.16481 5.5L7.73791 7.88182Z" fill="#333333"/>
<path d="M2 12C2 11.4477 1.55228 11 1 11C0.447715 11 0 11.4477 0 12C0 12.5523 0.447715 13 1 13C1.55228 13 2 12.5523 2 12Z" fill="#333333"/>
<path d="M13 9.01632L11 9.97786V12.4812C9.92129 13.198 8.93284 13.5144 7.99797 13.5156C7.06354 13.5167 6.07649 13.2032 5 12.4832V10.1471L3 9.10542V13C3 13.3154 3.14878 13.6123 3.40141 13.8011C4.88035 14.9062 6.40699 15.5176 8.0005 15.5156C9.59296 15.5135 11.1183 14.8991 12.5958 13.8032C12.8501 13.6145 13 13.3166 13 13V9.01632Z" fill="#333333"/>
<path d="M5 9H4.96241L5 9.01958V9Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -20,6 +20,9 @@
"Audio": "Audio",
"BarMaintenanceDescription": "We apologize for any short-term technical issues in service functioning that may appear on {{targetDate}} during the update of {{productName}}.",
"BarMaintenanceDisclaimer": "Please, make sure that all the changes are successfully saved during this day.",
"BookTraining": "Book a training",
"BookTeamTraining": "Book a team training session with our best specialists",
"BookNow": "Book now",
"ByFirstNameSorting": "By first name",
"ByLastNameSorting": "By last name",
"Bytes": "bytes",
@ -124,6 +127,7 @@
"LastName": "Last name",
"LatePayment": "Late payment",
"LearnMore": "Learn more",
"LiveChat": "Live chat",
"Load": "Load",
"LoadingDescription": "Please wait...",
"LoadingProcessing": "Loading...",
@ -236,6 +240,7 @@
"UnknownError": "Unknown error",
"User": "User",
"UsersInvited": "Users invited",
"UseLikePro": "Use {{organizationName}} like a pro",
"Version": "Version",
"Video": "Video",
"View": "View",