Merge pull request #207 from ONLYOFFICE/feature/EditorIntegration
Feature/editor integration
@ -1,3 +0,0 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 0C7.67157 0 7 0.671573 7 1.5V7H1.5C0.671573 7 0 7.67157 0 8.5C0 9.32843 0.671573 10 1.5 10H7V15.5C7 16.3284 7.67157 17 8.5 17C9.32843 17 10 16.3284 10 15.5V10H15.5C16.3284 10 17 9.32843 17 8.5C17 7.67157 16.3284 7 15.5 7H10V1.5C10 0.671573 9.32843 0 8.5 0Z" fill="#A3A9AE"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 432 B |
@ -29,5 +29,7 @@
|
||||
|
||||
"ShareEveryone": "Everyone",
|
||||
"ShareEmailSubject": "You have been granted access to the {{itemName}} document",
|
||||
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}"
|
||||
"ShareEmailBody": "You have been granted access to the {{itemName}} document. Click the link below to open the document right now: {{shareLink}}",
|
||||
|
||||
"LoadingLabel": "Loading... Please wait..."
|
||||
}
|
||||
|
@ -29,5 +29,7 @@
|
||||
|
||||
"ShareEveryone": "Все",
|
||||
"ShareEmailSubject": "Вам предоставлен доступ к документу {{itemName}}",
|
||||
"ShareEmailBody": "Вам предоставлен доступ к документу {{itemName}}. Нажмите на ссылку ниже, чтобы открыть документ прямо сейчас: {{shareLink}}"
|
||||
"ShareEmailBody": "Вам предоставлен доступ к документу {{itemName}}. Нажмите на ссылку ниже, чтобы открыть документ прямо сейчас: {{shareLink}}",
|
||||
|
||||
"LoadingLabel": "Загрузка... Пожалуйста подождите..."
|
||||
}
|
||||
|
@ -649,7 +649,7 @@ class FilesRowContent extends React.PureComponent {
|
||||
{canWebEdit && !isTrashFolder && accessToEdit && (
|
||||
<IconButton
|
||||
onClick={this.onFilesClick}
|
||||
iconName="images/access.edit.react.svg"
|
||||
iconName="/static/images/access.edit.react.svg"
|
||||
className="badge"
|
||||
size="small"
|
||||
isfill={true}
|
||||
|
@ -367,7 +367,7 @@ const SimpleFilesRow = (props) => {
|
||||
return {
|
||||
key: option,
|
||||
label: t("Edit"),
|
||||
icon: "images/access.edit.react.svg",
|
||||
icon: "/static/images/access.edit.react.svg",
|
||||
onClick: onClickLinkEdit,
|
||||
disabled: false,
|
||||
};
|
||||
@ -431,7 +431,7 @@ const SimpleFilesRow = (props) => {
|
||||
return {
|
||||
key: option,
|
||||
label: t("ThirdPartyInfo"),
|
||||
icon: "images/access.edit.react.svg",
|
||||
icon: "/static/images/access.edit.react.svg",
|
||||
onClick: onChangeThirdPartyInfo,
|
||||
disabled: false,
|
||||
};
|
||||
|
@ -0,0 +1,44 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-http-backend";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
import config from "../../../../package.json";
|
||||
const homepage = config.homepage;
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
newInstance.use(Backend).init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: `${homepage}/locales/{{lng}}/EmbeddingPanel.json`,
|
||||
},
|
||||
|
||||
ns: ["EmbeddingPanel"],
|
||||
defaultNS: "EmbeddingPanel",
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default newInstance;
|
@ -9,7 +9,7 @@ import Link from "@appserver/components/link";
|
||||
import TextInput from "@appserver/components/text-input";
|
||||
import Textarea from "@appserver/components/textarea";
|
||||
import toastr from "studio/toastr";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withTranslation, I18nextProvider } from "react-i18next";
|
||||
import {
|
||||
StyledEmbeddingPanel,
|
||||
StyledContent,
|
||||
@ -17,6 +17,7 @@ import {
|
||||
StyledBody,
|
||||
} from "../StyledPanels";
|
||||
import copy from "copy-to-clipboard";
|
||||
import i18n from "./i18n";
|
||||
|
||||
class EmbeddingPanelComponent extends React.Component {
|
||||
constructor(props) {
|
||||
@ -128,7 +129,7 @@ class EmbeddingPanelComponent extends React.Component {
|
||||
<StyledHeaderContent>
|
||||
<IconButton
|
||||
size="16"
|
||||
iconName="ArrowPathIcon"
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
onClick={this.onArrowClick}
|
||||
color="#A3A9AE"
|
||||
/>
|
||||
@ -215,4 +216,12 @@ EmbeddingPanelComponent.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withTranslation("EmbeddingPanel")(EmbeddingPanelComponent);
|
||||
const EmbeddingPanel = withTranslation("EmbeddingPanel")(
|
||||
EmbeddingPanelComponent
|
||||
);
|
||||
|
||||
export default (props) => (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<EmbeddingPanel {...props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
|
@ -0,0 +1,80 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Provider as MobxProvider } from "mobx-react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { getShareFiles } from "@appserver/common/api/files";
|
||||
import SharingPanel from "../SharingPanel";
|
||||
|
||||
import initFilesStore from "../../../store/InitFilesStore";
|
||||
import filesStore from "../../../store/FilesStore";
|
||||
import uploadDataStore from "../../../store/UploadDataStore";
|
||||
import dialogsStore from "../../../store/DialogsStore";
|
||||
import treeFoldersStore from "../../../store/TreeFoldersStore";
|
||||
import store from "studio/store";
|
||||
|
||||
const { auth: authStore } = store;
|
||||
|
||||
const SharingDialog = ({
|
||||
sharingObject,
|
||||
onSuccess,
|
||||
isVisible,
|
||||
setSharingPanelVisible,
|
||||
onCancel,
|
||||
setSelection,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
setSharingPanelVisible(isVisible);
|
||||
}, [isVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelection([sharingObject]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<SharingPanel
|
||||
key="sharing-panel"
|
||||
uploadPanelVisible={false}
|
||||
onSuccess={onSuccess}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SharingDialogWrapper = inject(({ dialogsStore, filesStore }) => {
|
||||
const { getShareUsers, setSelection, selection } = filesStore;
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
return {
|
||||
setSharingPanelVisible,
|
||||
getShareUsers,
|
||||
setSelection,
|
||||
selection,
|
||||
};
|
||||
})(observer(SharingDialog));
|
||||
|
||||
class SharingModal extends React.Component {
|
||||
static getSharingSettings = (fileId) => {
|
||||
return getShareFiles([+fileId], []).then((users) =>
|
||||
SharingPanel.convertSharingUsers(users)
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<MobxProvider
|
||||
auth={authStore}
|
||||
initFilesStore={initFilesStore}
|
||||
filesStore={filesStore}
|
||||
dialogsStore={dialogsStore}
|
||||
treeFoldersStore={treeFoldersStore}
|
||||
uploadDataStore={uploadDataStore}
|
||||
>
|
||||
<SharingDialogWrapper {...this.props} />
|
||||
</MobxProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SharingModal;
|
@ -31,7 +31,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("FullAccess") && (
|
||||
<DropDownItem
|
||||
label={t("FullAccess")}
|
||||
icon="images/access.edit.react.svg"
|
||||
icon="/static/images/access.edit.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={FullAccess}
|
||||
onClick={onAccessChange}
|
||||
@ -41,7 +41,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("FilterEditing") && (
|
||||
<DropDownItem
|
||||
label={t("CustomFilter")}
|
||||
icon="images/custom.filter.react.svg"
|
||||
icon="/static/images/custom.filter.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={CustomFilter}
|
||||
onClick={onAccessChange}
|
||||
@ -51,7 +51,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("Review") && (
|
||||
<DropDownItem
|
||||
label={t("Review")}
|
||||
icon="images/access.review.react.svg"
|
||||
icon="/static/images/access.review.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={Review}
|
||||
onClick={onAccessChange}
|
||||
@ -61,7 +61,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("FormFilling") && (
|
||||
<DropDownItem
|
||||
label={t("FormFilling")}
|
||||
icon="images/access.form.react.svg"
|
||||
icon="/static/images/access.form.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={FormFilling}
|
||||
onClick={onAccessChange}
|
||||
@ -71,7 +71,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("Comment") && (
|
||||
<DropDownItem
|
||||
label={t("Comment")}
|
||||
icon="images/access.comment.react.svg"
|
||||
icon="/static/images/access.comment.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={Comment}
|
||||
onClick={onAccessChange}
|
||||
@ -91,7 +91,7 @@ const AccessComboBox = (props) => {
|
||||
{accessOptions.includes("DenyAccess") && (
|
||||
<DropDownItem
|
||||
label={t("DenyAccess")}
|
||||
icon="images/access.none.react.svg"
|
||||
icon="/static/images/access.none.react.svg"
|
||||
data-id={itemId}
|
||||
data-access={DenyAccess}
|
||||
onClick={onAccessChange}
|
||||
|
@ -44,6 +44,7 @@ class SharingRow extends React.Component {
|
||||
onShareEmail = () => {
|
||||
const { selection, item, t } = this.props;
|
||||
const { shareLink } = item.sharedTo;
|
||||
|
||||
const itemName = selection.title ? selection.title : selection[0].title;
|
||||
const subject = t("ShareEmailSubject", { itemName });
|
||||
const body = t("ShareEmailBody", { itemName, shareLink });
|
||||
@ -228,7 +229,7 @@ class SharingRow extends React.Component {
|
||||
!shareLink &&
|
||||
!isLocked && (
|
||||
<IconButton
|
||||
iconName="images/remove.react.svg"
|
||||
iconName="/static/images/remove.react.svg"
|
||||
id={id}
|
||||
{...onRemoveUserProp}
|
||||
className="sharing_panel-remove-icon"
|
||||
|
@ -0,0 +1,44 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-http-backend";
|
||||
import { LANGUAGE } from "@appserver/common/constants";
|
||||
import config from "../../../../package.json";
|
||||
const homepage = config.homepage;
|
||||
|
||||
//import LanguageDetector from "i18next-browser-languagedetector";
|
||||
// not like to use this?
|
||||
// have a look at the Quick start guide
|
||||
// for passing in lng and translations on init
|
||||
|
||||
const languages = ["en", "ru"];
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
newInstance.use(Backend).init({
|
||||
lng: localStorage.getItem(LANGUAGE) || "en",
|
||||
supportedLngs: languages,
|
||||
whitelist: languages,
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
//debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: `${homepage}/locales/{{lng}}/SharingPanel.json`,
|
||||
},
|
||||
|
||||
ns: ["SharingPanel"],
|
||||
defaultNS: "SharingPanel",
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default newInstance;
|
@ -8,6 +8,8 @@ import Button from "@appserver/components/button";
|
||||
import DropDown from "@appserver/components/drop-down";
|
||||
import DropDownItem from "@appserver/components/drop-down-item";
|
||||
import Textarea from "@appserver/components/textarea";
|
||||
import Loader from "@appserver/components/loader";
|
||||
import Text from "@appserver/components/text";
|
||||
import { withRouter } from "react-router";
|
||||
import { withTranslation, Trans } from "react-i18next";
|
||||
import toastr from "studio/toastr";
|
||||
@ -23,13 +25,14 @@ import { AddUsersPanel, AddGroupsPanel, EmbeddingPanel } from "../index";
|
||||
import SharingRow from "./SharingRow";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import config from "../../../../package.json";
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
|
||||
const SharingBodyStyle = { height: `calc(100vh - 156px)` };
|
||||
|
||||
class SharingPanelComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showActionPanel: false,
|
||||
isNotifyUsers: false,
|
||||
@ -104,10 +107,11 @@ class SharingPanelComponent extends React.Component {
|
||||
isDesktop,
|
||||
setEncryptionAccess,
|
||||
setShareFiles,
|
||||
onSuccess,
|
||||
} = this.props;
|
||||
|
||||
const folderIds = [];
|
||||
const fileIds = [];
|
||||
let folderIds = [];
|
||||
let fileIds = [];
|
||||
const share = [];
|
||||
|
||||
let externalAccess = null;
|
||||
@ -150,6 +154,7 @@ class SharingPanelComponent extends React.Component {
|
||||
folderIds.push(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
const owner = shareDataItems.find((x) => x.isOwner);
|
||||
const ownerId =
|
||||
filesOwnerId !== owner.sharedTo.id ? owner.sharedTo.id : null;
|
||||
@ -196,10 +201,10 @@ class SharingPanelComponent extends React.Component {
|
||||
}
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then(() => onSuccess && onSuccess())
|
||||
.catch((err) => toastr.error(err))
|
||||
.finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
onNotifyUsersChange = () =>
|
||||
this.setState({ isNotifyUsers: !this.state.isNotifyUsers });
|
||||
|
||||
@ -251,9 +256,6 @@ class SharingPanelComponent extends React.Component {
|
||||
};
|
||||
|
||||
getShareData = () => {
|
||||
const returnValue = this.getData();
|
||||
const folderId = returnValue[0];
|
||||
const fileId = returnValue[1];
|
||||
const {
|
||||
getAccessOption,
|
||||
getExternalAccessOption,
|
||||
@ -262,12 +264,17 @@ class SharingPanelComponent extends React.Component {
|
||||
getShareUsers,
|
||||
} = this.props;
|
||||
|
||||
const returnValue = this.getData();
|
||||
const folderId = returnValue[0];
|
||||
const fileId = returnValue[1];
|
||||
|
||||
if (folderId.length !== 0 || fileId.length !== 0) {
|
||||
setIsLoading(true);
|
||||
getShareUsers(folderId, fileId)
|
||||
.then((shareDataItems) => {
|
||||
const baseShareData = JSON.parse(JSON.stringify(shareDataItems));
|
||||
const accessOptions = getAccessOption(selection);
|
||||
|
||||
const externalAccessOptions = getExternalAccessOption(selection);
|
||||
const filesOwner = shareDataItems.find((x) => x.isOwner);
|
||||
const filesOwnerId = filesOwner ? filesOwner.sharedTo.id : null;
|
||||
@ -277,10 +284,11 @@ class SharingPanelComponent extends React.Component {
|
||||
shareDataItems,
|
||||
accessOptions,
|
||||
externalAccessOptions,
|
||||
showPanel: true,
|
||||
//showPanel: true,
|
||||
filesOwnerId,
|
||||
});
|
||||
})
|
||||
|
||||
.catch((err) => {
|
||||
toastr.error(err);
|
||||
this.onClose();
|
||||
@ -291,6 +299,7 @@ class SharingPanelComponent extends React.Component {
|
||||
|
||||
getInternalLink = () => {
|
||||
const { homepage, selection } = this.props;
|
||||
|
||||
const item = selection[0];
|
||||
const isFile = !!item.fileExst;
|
||||
|
||||
@ -326,8 +335,11 @@ class SharingPanelComponent extends React.Component {
|
||||
setShareDataItems = (shareDataItems) => this.setState({ shareDataItems });
|
||||
|
||||
onClose = () => {
|
||||
this.props.setSharingPanelVisible(false);
|
||||
this.props.selectUploadedFile([]);
|
||||
const { onCancel, setSharingPanelVisible, selectUploadedFile } = this.props;
|
||||
|
||||
setSharingPanelVisible(false);
|
||||
selectUploadedFile([]);
|
||||
onCancel && onCancel();
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -382,6 +394,8 @@ class SharingPanelComponent extends React.Component {
|
||||
canShareOwnerChange,
|
||||
isLoading,
|
||||
uploadPanelVisible,
|
||||
documentTitle,
|
||||
sharingPanelVisible,
|
||||
} = this.props;
|
||||
const {
|
||||
showActionPanel,
|
||||
@ -393,12 +407,12 @@ class SharingPanelComponent extends React.Component {
|
||||
showEmbeddingPanel,
|
||||
showChangeOwnerPanel,
|
||||
shareLink,
|
||||
showPanel,
|
||||
//showPanel,
|
||||
accessOptions,
|
||||
externalAccessOptions,
|
||||
} = this.state;
|
||||
|
||||
const visible = showPanel;
|
||||
const visible = sharingPanelVisible;
|
||||
const zIndex = 310;
|
||||
const onPlusClickProp = !isLoading ? { onClick: this.onPlusClick } : {};
|
||||
const internalLink = selection.length === 1 && this.getInternalLink();
|
||||
@ -417,7 +431,7 @@ class SharingPanelComponent extends React.Component {
|
||||
{uploadPanelVisible && (
|
||||
<IconButton
|
||||
size="16"
|
||||
iconName="ArrowPathIcon"
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
onClick={this.onClose}
|
||||
color="A3A9AE"
|
||||
/>
|
||||
@ -429,7 +443,7 @@ class SharingPanelComponent extends React.Component {
|
||||
<div ref={this.ref} className="sharing_panel-drop-down-wrapper">
|
||||
<IconButton
|
||||
size="17"
|
||||
iconName="images/actions.header.touch.react.svg"
|
||||
iconName="/static/images/actions.header.touch.react.svg"
|
||||
className="sharing_panel-plus-icon"
|
||||
{...onPlusClickProp}
|
||||
color="A3A9AE"
|
||||
@ -466,26 +480,38 @@ class SharingPanelComponent extends React.Component {
|
||||
stype="mediumBlack"
|
||||
style={SharingBodyStyle}
|
||||
>
|
||||
{shareDataItems.map((item, index) => (
|
||||
<SharingRow
|
||||
t={t}
|
||||
index={index}
|
||||
key={`${item.sharedTo.id}_${index}`}
|
||||
selection={selection}
|
||||
item={item}
|
||||
isMyId={isMyId}
|
||||
accessOptions={accessOptions}
|
||||
externalAccessOptions={externalAccessOptions}
|
||||
canShareOwnerChange={canShareOwnerChange}
|
||||
onChangeItemAccess={this.onChangeItemAccess}
|
||||
internalLink={internalLink}
|
||||
onRemoveUserClick={this.onRemoveUserItemClick}
|
||||
onShowEmbeddingPanel={this.onShowEmbeddingPanel}
|
||||
onToggleLink={this.onToggleLink}
|
||||
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
))}
|
||||
{!isLoading ? (
|
||||
shareDataItems.map((item, index) => (
|
||||
<SharingRow
|
||||
t={t}
|
||||
index={index}
|
||||
key={`${item.sharedTo.id}_${index}`}
|
||||
selection={selection}
|
||||
item={item}
|
||||
isMyId={isMyId}
|
||||
accessOptions={accessOptions}
|
||||
externalAccessOptions={externalAccessOptions}
|
||||
canShareOwnerChange={canShareOwnerChange}
|
||||
onChangeItemAccess={this.onChangeItemAccess}
|
||||
internalLink={internalLink}
|
||||
onRemoveUserClick={this.onRemoveUserItemClick}
|
||||
onShowEmbeddingPanel={this.onShowEmbeddingPanel}
|
||||
onToggleLink={this.onToggleLink}
|
||||
onShowChangeOwnerPanel={this.onShowChangeOwnerPanel}
|
||||
isLoading={isLoading}
|
||||
documentTitle={documentTitle}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div key="loader" className="sharing-panel-loader-wrapper">
|
||||
<Loader
|
||||
type="oval"
|
||||
size="16px"
|
||||
className="sharing-panel-loader"
|
||||
/>
|
||||
<Text as="span">{t("LoadingLabel")}</Text>
|
||||
</div>
|
||||
)}
|
||||
{isNotifyUsers && (
|
||||
<div className="sharing_panel-text-area">
|
||||
<Textarea
|
||||
@ -565,9 +591,7 @@ class SharingPanelComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const SharingPanel = withTranslation("SharingPanel")(SharingPanelComponent);
|
||||
|
||||
export default inject(
|
||||
const SharingPanel = inject(
|
||||
(
|
||||
{
|
||||
auth,
|
||||
@ -581,6 +605,7 @@ export default inject(
|
||||
) => {
|
||||
const { replaceFileStream, setEncryptionAccess } = auth;
|
||||
const { customNames, isDesktopClient } = auth.settingsStore;
|
||||
|
||||
const { setIsLoading, isLoading } = initFilesStore;
|
||||
const {
|
||||
selection,
|
||||
@ -593,26 +618,27 @@ export default inject(
|
||||
setShareFiles,
|
||||
} = filesStore;
|
||||
const { isPrivacyFolder } = treeFoldersStore;
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
const { setSharingPanelVisible, sharingPanelVisible } = dialogsStore;
|
||||
const {
|
||||
uploadSelection,
|
||||
selectedUploadFile,
|
||||
selectUploadedFile,
|
||||
updateUploadedItem,
|
||||
} = uploadDataStore;
|
||||
|
||||
return {
|
||||
isMyId: auth.userStore.user.id,
|
||||
isMyId: auth.userStore.user && auth.userStore.user.id,
|
||||
groupsCaption: customNames.groupsCaption,
|
||||
isDesktop: isDesktopClient,
|
||||
homepage: config.homepage,
|
||||
selection: uploadPanelVisible ? uploadSelection : selection,
|
||||
selection: uploadPanelVisible ? selectedUploadFile : selection,
|
||||
isLoading,
|
||||
isPrivacy: isPrivacyFolder,
|
||||
uploadSelection,
|
||||
selectedUploadFile,
|
||||
canShareOwnerChange,
|
||||
|
||||
setIsLoading,
|
||||
setSharingPanelVisible,
|
||||
sharingPanelVisible,
|
||||
selectUploadedFile,
|
||||
updateUploadedItem,
|
||||
replaceFileStream,
|
||||
@ -625,4 +651,48 @@ export default inject(
|
||||
setShareFiles,
|
||||
};
|
||||
}
|
||||
)(withRouter(observer(SharingPanel)));
|
||||
)(observer(withTranslation("SharingPanel")(SharingPanelComponent)));
|
||||
|
||||
class Panel extends React.Component {
|
||||
static convertSharingUsers = (shareDataItems) => {
|
||||
const t = i18n.getFixedT(null, "SharingPanel");
|
||||
let sharingSettings = [];
|
||||
for (let i = 1; i < shareDataItems.length; i++) {
|
||||
let resultAccess =
|
||||
shareDataItems[i].access === 1
|
||||
? t("FullAccess")
|
||||
: shareDataItems[i].access === 2
|
||||
? t("ReadOnly")
|
||||
: shareDataItems[i].access === 3
|
||||
? t("DenyAccess")
|
||||
: shareDataItems[i].access === 5
|
||||
? t("Review")
|
||||
: shareDataItems[i].access === 6
|
||||
? t("Comment")
|
||||
: shareDataItems[i].access === 7
|
||||
? t("FormFilling")
|
||||
: shareDataItems[i].access === 8
|
||||
? t("CustomFilter")
|
||||
: "";
|
||||
|
||||
let obj = {
|
||||
user:
|
||||
shareDataItems[i].sharedTo.displayName ||
|
||||
shareDataItems[i].sharedTo.name,
|
||||
permissions: resultAccess,
|
||||
};
|
||||
sharingSettings.push(obj);
|
||||
}
|
||||
return sharingSettings;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<SharingPanel {...this.props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Panel;
|
||||
|
@ -6,7 +6,7 @@ import ToggleButton from "@appserver/components/toggle-button";
|
||||
import { StyledLinkRow } from "../StyledPanels";
|
||||
import AccessComboBox from "./AccessComboBox";
|
||||
import { ShareAccessRights } from "@appserver/common/constants";
|
||||
import AccessEditIcon from "../../../../public/images/access.edit.react.svg";
|
||||
import AccessEditIcon from "../../../../../../../public/images/access.edit.react.svg";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
|
||||
const StyledAccessEditIcon = styled(AccessEditIcon)`
|
||||
|
@ -195,6 +195,15 @@ const StyledContent = styled.div`
|
||||
min-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.sharing-panel-loader-wrapper {
|
||||
margin-top: 8px;
|
||||
padding-left: 32px;
|
||||
}
|
||||
.sharing-panel-loader {
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledHeaderContent = styled.div`
|
||||
|
@ -7,21 +7,21 @@ export const presentInArray = (array, search, caseInsensitive = false) => {
|
||||
export const getAccessIcon = (access) => {
|
||||
switch (access) {
|
||||
case 1:
|
||||
return "images/access.edit.react.svg";
|
||||
return "/static/images/access.edit.react.svg";
|
||||
case 2:
|
||||
return "/static/images/eye.react.svg";
|
||||
case 3:
|
||||
return "images/access.none.react.svg";
|
||||
return "/static/images/access.none.react.svg";
|
||||
case 4:
|
||||
return "images/catalog.question.react.svg";
|
||||
case 5:
|
||||
return "images/access.review.react.svg";
|
||||
return "/static/images/access.review.react.svg";
|
||||
case 6:
|
||||
return "images/access.comment.react.svg";
|
||||
return "/static/images/access.comment.react.svg";
|
||||
case 7:
|
||||
return "images/access.form.react.svg";
|
||||
return "/static/images/access.form.react.svg";
|
||||
case 8:
|
||||
return "images/custom.filter.react.svg";
|
||||
return "/static/images/custom.filter.react.svg";
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -753,24 +753,29 @@ class FilesStore {
|
||||
}
|
||||
|
||||
getOptions = (selection, externalAccess = false) => {
|
||||
let AccessOptions = [];
|
||||
|
||||
AccessOptions.push("ReadOnly", "DenyAccess");
|
||||
|
||||
const webEdit = selection.find((x) => canWebEdit(x.fileExst));
|
||||
|
||||
const webComment = selection.find((x) => canWebComment(x.fileExst));
|
||||
|
||||
const webReview = selection.find((x) => canWebReview(x.fileExst));
|
||||
|
||||
const formFillingDocs = selection.find((x) =>
|
||||
canFormFillingDocs(x.fileExst)
|
||||
);
|
||||
|
||||
const webFilter = selection.find((x) => canWebFilterEditing(x.fileExst));
|
||||
|
||||
let AccessOptions = [];
|
||||
|
||||
if (webEdit || !externalAccess) AccessOptions.push("FullAccess");
|
||||
|
||||
AccessOptions.push("ReadOnly", "DenyAccess");
|
||||
|
||||
if (webComment) AccessOptions.push("Comment");
|
||||
if (webReview) AccessOptions.push("Review");
|
||||
if (formFillingDocs) AccessOptions.push("FormFilling");
|
||||
if (webFilter) AccessOptions.push("FilterEditing");
|
||||
|
||||
return AccessOptions;
|
||||
};
|
||||
|
||||
|
@ -144,6 +144,7 @@ var config = {
|
||||
},
|
||||
exposes: {
|
||||
"./app": "./src/Files.jsx",
|
||||
"./SharingDialog": "./src/components/panels/SharingDialog",
|
||||
},
|
||||
shared: {
|
||||
...deps,
|
||||
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 520 B |
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 608 B After Width: | Height: | Size: 608 B |
Before Width: | Height: | Size: 839 B After Width: | Height: | Size: 839 B |
Before Width: | Height: | Size: 157 B After Width: | Height: | Size: 157 B |
Before Width: | Height: | Size: 723 B After Width: | Height: | Size: 723 B |
3
web/ASC.Web.Editor/public/locales/en/Editor.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"FileLocation": "Open file location"
|
||||
}
|
3
web/ASC.Web.Editor/public/locales/ru/Editor.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"FileLocation": "Открыть расположение файла"
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import Toast from "@appserver/components/toast";
|
||||
import toastr from "@appserver/components/toast/toastr";
|
||||
import toastr from "studio/toastr";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
import Box from "@appserver/components/box";
|
||||
import { regDesktop } from "@appserver/common/desktop";
|
||||
import Loaders from "@appserver/common/components/Loaders";
|
||||
@ -16,21 +19,34 @@ import {
|
||||
openEdit,
|
||||
setEncryptionKeys,
|
||||
getEncryptionAccess,
|
||||
getFileInfo,
|
||||
} from "@appserver/common/api/files";
|
||||
import { checkIsAuthenticated } from "@appserver/common/api/user";
|
||||
import { getUser } from "@appserver/common/api/people";
|
||||
import FilesFilter from "@appserver/common/api/files/filter";
|
||||
|
||||
import throttle from "lodash/throttle";
|
||||
import { isIOS, deviceType } from "react-device-detect";
|
||||
import { homepage } from "../package.json";
|
||||
|
||||
import "./custom.scss";
|
||||
import { AppServerConfig } from "@appserver/common/constants";
|
||||
import SharingDialog from "files/SharingDialog";
|
||||
|
||||
import i18n from "./i18n";
|
||||
|
||||
let documentIsReady = false;
|
||||
|
||||
let docTitle = null;
|
||||
let fileType = null;
|
||||
|
||||
let config;
|
||||
let docSaved = null;
|
||||
let docEditor;
|
||||
let fileInfo;
|
||||
const url = window.location.href;
|
||||
const filesUrl = url.substring(0, url.indexOf("/doceditor"));
|
||||
|
||||
toast.configure();
|
||||
|
||||
const Editor = () => {
|
||||
const urlParams = getObjectByLocation(window.location);
|
||||
@ -48,6 +64,18 @@ const Editor = () => {
|
||||
500
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
const updateUsersRightsList = () => {
|
||||
SharingDialog.getSharingSettings(fileId).then((sharingSettings) => {
|
||||
docEditor.setSharingSettings({
|
||||
sharingSettings,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
if (!fileId) return;
|
||||
@ -72,8 +100,9 @@ const Editor = () => {
|
||||
setIsAuthenticated(success);
|
||||
}
|
||||
}
|
||||
fileInfo = await getFileInfo(fileId);
|
||||
|
||||
const config = await openEdit(fileId, doc);
|
||||
config = await openEdit(fileId, doc);
|
||||
|
||||
if (isDesktop) {
|
||||
const isEncryption =
|
||||
@ -110,6 +139,18 @@ const Editor = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
config &&
|
||||
config.document.permissions.edit &&
|
||||
config.document.permissions.modifyFilter
|
||||
) {
|
||||
const sharingSettings = await SharingDialog.getSharingSettings(fileId);
|
||||
config.document.info = {
|
||||
...config.document.info,
|
||||
sharingSettings,
|
||||
};
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
loadDocApi(docApiUrl, () => onLoad(config));
|
||||
@ -124,10 +165,6 @@ const Editor = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
const isIPad = () => {
|
||||
return isIOS && deviceType === "tablet";
|
||||
};
|
||||
@ -195,6 +232,7 @@ const Editor = () => {
|
||||
|
||||
const onLoad = (config) => {
|
||||
console.log("Editor config: ", config);
|
||||
|
||||
try {
|
||||
console.log(config);
|
||||
|
||||
@ -208,6 +246,20 @@ const Editor = () => {
|
||||
config.type = "mobile";
|
||||
}
|
||||
|
||||
const filterObj = FilesFilter.getDefault();
|
||||
filterObj.folder = fileInfo.folderId;
|
||||
const urlFilter = filterObj.toUrlParams();
|
||||
|
||||
config.editorConfig.customization = {
|
||||
...config.editorConfig.customization,
|
||||
goback: {
|
||||
blank: true,
|
||||
requestClose: false,
|
||||
text: i18n.t("FileLocation"),
|
||||
url: `${combineUrl(filesUrl, `/filter?${urlFilter}`)}`,
|
||||
},
|
||||
};
|
||||
|
||||
const events = {
|
||||
events: {
|
||||
onAppReady: onSDKAppReady,
|
||||
@ -217,6 +269,10 @@ const Editor = () => {
|
||||
onInfo: onSDKInfo,
|
||||
onWarning: onSDKWarning,
|
||||
onError: onSDKError,
|
||||
...(config.document.permissions.edit &&
|
||||
config.document.permissions.modifyFilter && {
|
||||
onRequestSharingSettings: onSDKRequestSharingSettings,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
@ -224,9 +280,7 @@ const Editor = () => {
|
||||
|
||||
if (!window.DocsAPI) throw new Error("DocsAPI is not defined");
|
||||
|
||||
//hideLoader();
|
||||
|
||||
window.DocsAPI.DocEditor("editor", newConfig);
|
||||
docEditor = window.DocsAPI.DocEditor("editor", newConfig);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error.message, null, 0, true);
|
||||
@ -243,6 +297,16 @@ const Editor = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const onSDKRequestSharingSettings = () => {
|
||||
setIsVisible(true);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
const onSDKWarning = (event) => {
|
||||
console.log(
|
||||
"ONLYOFFICE Document Editor reports a warning: code " +
|
||||
@ -288,7 +352,16 @@ const Editor = () => {
|
||||
<Toast />
|
||||
|
||||
{!isLoading ? (
|
||||
<div id="editor"></div>
|
||||
<>
|
||||
<div id="editor"></div>
|
||||
|
||||
<SharingDialog
|
||||
isVisible={isVisible}
|
||||
sharingObject={fileInfo}
|
||||
onCancel={onCancel}
|
||||
onSuccess={updateUsersRightsList}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Box paddingProp="16px">
|
||||
<Loaders.Rectangle height="96vh" />
|
||||
|
@ -14,10 +14,10 @@ newInstance
|
||||
.use(initReactI18next)
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: "ru",
|
||||
lng: lng,
|
||||
supportedLngs: languages,
|
||||
//whitelist: languages,
|
||||
fallbackLng: "ru",
|
||||
fallbackLng: "en",
|
||||
load: "languageOnly",
|
||||
debug: true,
|
||||
|
||||
@ -35,8 +35,8 @@ newInstance
|
||||
crossDomain: false,
|
||||
},
|
||||
|
||||
ns: ["Login"],
|
||||
defaultNS: "Login",
|
||||
ns: ["Editor"],
|
||||
defaultNS: "Editor",
|
||||
|
||||
react: {
|
||||
useSuspense: true,
|
||||
|
@ -5,8 +5,8 @@ const ModuleFederationPlugin = require("webpack").container
|
||||
.ModuleFederationPlugin;
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const { InjectManifest } = require("workbox-webpack-plugin");
|
||||
//const combineUrl = require("@appserver/common/utils/combineUrl");
|
||||
//const AppServerConfig = require("@appserver/common/constants/AppServerConfig");
|
||||
const combineUrl = require("@appserver/common/utils/combineUrl");
|
||||
const AppServerConfig = require("@appserver/common/constants/AppServerConfig");
|
||||
|
||||
const path = require("path");
|
||||
const pkg = require("./package.json");
|
||||
@ -133,7 +133,16 @@ const config = {
|
||||
new ModuleFederationPlugin({
|
||||
name: "editor",
|
||||
filename: "remoteEntry.js",
|
||||
remotes: {},
|
||||
remotes: {
|
||||
studio: `studio@${combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
"/remoteEntry.js"
|
||||
)}`,
|
||||
files: `files@${combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
"/products/files/remoteEntry.js"
|
||||
)}`,
|
||||
},
|
||||
exposes: {
|
||||
"./app": "./src/Editor.jsx",
|
||||
},
|
||||
|