Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V11 multi office suite #7353

Merged
merged 5 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.8"

Expand Down
110 changes: 110 additions & 0 deletions frontend/src/components/dialog/repo-office-suite-dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalBody, ModalFooter, TabContent, TabPane, ModalHeader} from 'reactstrap';
import makeAnimated from 'react-select/animated';
import { userAPI } from '../../utils/user-api';
import { gettext, isPro } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import { SeahubSelect } from '../common/select';
import '../../css/repo-office-suite-dialog.css';

const propTypes = {
itemName: PropTypes.string.isRequired,
toggleDialog: PropTypes.func.isRequired,
submit: PropTypes.func.isRequired,
repoID: PropTypes.string.isRequired,
};

class OfficeSuiteDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOption: null,
errorMsg: []
};
this.options = [];
}

handleSelectChange = (option) => {
this.setState({ selectedOption: option });
};

submit = () => {
let suite_id = this.state.selectedOption.value;
this.props.submit(suite_id);
};

componentDidMount() {
if (isPro) {
userAPI.getOfficeSuite(this.props.repoID).then((res) => {
this.updateOptions(res);
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
}

updateOptions = (officeSuites) => {
officeSuites.data.suites_info.forEach(item => {
let option = {
value: item.id,
label: item.name,
is_selected: item.is_selected,
};
this.options.push(option);
});
let selectedOption = this.options.find(op => op.is_selected);
this.setState({ selectedOption });
};


renderOfficeSuiteContent = () => {
return (
<div className="repo-office-suite-dialog-main">
<TabContent>
{isPro &&
<TabPane role="tabpanel" id="office-suite-panel">
<SeahubSelect
isClearable
maxMenuHeight={200}
hideSelectedOptions={true}
components={makeAnimated()}
placeholder={gettext('Select a office suite')}
options={this.options}
onChange={this.handleSelectChange}
value={this.state.selectedOption}
className="repo-select-office-suite"
/>
</TabPane>
}
</TabContent>
</div>
);
};

render() {
const { itemName: repoName } = this.props;
let title = gettext('{library_name} Office Suite');
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
return (
<Modal isOpen={true} toggle={this.props.toggleDialog} className="repo-office-suite-dialog">
<ModalHeader toggle={this.props.toggleDialog}>
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
</ModalHeader>
<ModalBody className="repo-office-suite-dialog-content" role="tablist">
{this.renderOfficeSuiteContent()}
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.submit}>{gettext('Submit')}</Button>
</ModalFooter>
</Modal>
);
}
}

OfficeSuiteDialog.propTypes = propTypes;

export default OfficeSuiteDialog;
28 changes: 28 additions & 0 deletions frontend/src/css/repo-office-suite-dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.repo-office-suite-dialog .repo-office-suite-dialog-content {
padding: 0;
min-height: 5.5rem;
min-width: 10rem;
display: flex;
flex-direction: column;
}

@media (min-width: 268px) {
.repo-office-suite-dialog .repo-office-suite-dialog-content {
flex-direction: column;
}
}

.repo-office-suite-dialog-content .repo-office-suite-dialog-main {
display: flex;
flex-basis: 48%;
padding: 1rem;
}

.repo-office-suite-dialog-content .repo-office-suite-dialog-main .tab-content {
flex: 1;
}


.repo-office-suite-dialog-content .repo-office-suite-dialog-main .repo-select-office-suite {
padding: 8px 0;
}
1 change: 1 addition & 0 deletions frontend/src/models/repo-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class RepoInfo {
this.lib_need_decrypt = object.lib_need_decrypt;
this.last_modified= object.last_modified;
this.status = object.status;
this.enable_onlyoffice = object.enable_onlyoffice;
}
}

Expand Down
36 changes: 36 additions & 0 deletions frontend/src/pages/my-libs/mylib-repo-list-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog';
import RepoSeaTableIntegrationDialog from '../../components/dialog/repo-seatable-integration-dialog';
import RepoShareAdminDialog from '../../components/dialog/repo-share-admin-dialog';
import LibOldFilesAutoDelDialog from '../../components/dialog/lib-old-files-auto-del-dialog';
import OfficeSuiteDialog from '../../components/dialog/repo-office-suite-dialog';
import RepoMonitoredIcon from '../../components/repo-monitored-icon';
import { userAPI } from '../../utils/user-api';

const propTypes = {
repo: PropTypes.object.isRequired,
Expand Down Expand Up @@ -57,6 +59,7 @@ class MylibRepoListItem extends React.Component {
isRepoShareAdminDialogOpen: false,
isRepoDeleted: false,
isOldFilesAutoDelDialogOpen: false,
isOfficeSuiteDialogShow: false,
};
}

Expand Down Expand Up @@ -137,6 +140,9 @@ class MylibRepoListItem extends React.Component {
case 'SeaTable integration':
this.onSeaTableIntegrationToggle();
break;
case 'Office Suite':
this.onOfficeSuiteToggle();
break;
default:
break;
}
Expand Down Expand Up @@ -249,6 +255,10 @@ class MylibRepoListItem extends React.Component {
this.setState({isSeaTableIntegrationShow: !this.state.isSeaTableIntegrationShow});
};

onOfficeSuiteToggle = () => {
this.setState({ isOfficeSuiteDialogShow: !this.state.isOfficeSuiteDialogShow });
};

toggleRepoShareAdminDialog = () => {
this.setState({isRepoShareAdminDialogOpen: !this.state.isRepoShareAdminDialogOpen});
};
Expand Down Expand Up @@ -298,6 +308,21 @@ class MylibRepoListItem extends React.Component {
this.onTransferToggle();
};

onOfficeSuiteChange = (suiteID) => {
let repoID = this.props.repo.repo_id;
userAPI.setOfficeSuite(repoID, suiteID).then(res => {
let message = gettext('Successfully change office suite.');
toaster.success(message);
}).catch(error => {
if (error.response) {
toaster.danger(error.response.data.error_msg || gettext('Error'), { duration: 3 });
} else {
toaster.danger(gettext('Failed. Please check the network.'), { duration: 3 });
}
});
this.onOfficeSuiteToggle();
};

onDeleteRepo = (repo) => {
seafileAPI.deleteRepo(repo.repo_id).then((res) => {

Expand Down Expand Up @@ -544,6 +569,17 @@ class MylibRepoListItem extends React.Component {
</ModalPortal>
)}

{this.state.isOfficeSuiteDialogShow && (
<ModalPortal>
<OfficeSuiteDialog
repoID={repo.repo_id}
itemName={repo.repo_name}
submit={this.onOfficeSuiteChange}
toggleDialog={this.onOfficeSuiteToggle}
/>
</ModalPortal>
)}

</Fragment>
);
}
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/pages/my-libs/mylib-repo-menu.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
import { gettext, isPro, folderPermEnabled, enableRepoSnapshotLabel, enableResetEncryptedRepoPassword, isEmailConfigured, enableRepoAutoDel, enableSeaTableIntegration } from '../../utils/constants';
import { gettext, isPro, folderPermEnabled, enableRepoSnapshotLabel, enableResetEncryptedRepoPassword, isEmailConfigured, enableRepoAutoDel, enableSeaTableIntegration, enableMultipleOfficeSuite } from '../../utils/constants';
import { Utils } from '../../utils/utils';

const propTypes = {
Expand Down Expand Up @@ -126,6 +126,9 @@ class MylibRepoMenu extends React.Component {
if (enableSeaTableIntegration) {
operations.push('SeaTable integration');
}
if (enableMultipleOfficeSuite && isPro) {
operations.push('Office Suite');
}
return operations;
};

Expand Down Expand Up @@ -186,6 +189,9 @@ class MylibRepoMenu extends React.Component {
case 'SeaTable integration':
translateResult = gettext('SeaTable integration');
break;
case 'Office Suite':
translateResult = gettext('Office Suite');
break;
default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const ocmRemoteServers = window.app.pageOptions.ocmRemoteServers;
export const enableOCMViaWebdav = window.app.pageOptions.enableOCMViaWebdav;
export const enableSSOToThirdpartWebsite = window.app.pageOptions.enableSSOToThirdpartWebsite;
export const enableSeadoc = window.app.pageOptions.enableSeadoc;
export const enableMultipleOfficeSuite = window.app.pageOptions.enableMultipleOfficeSuite;

export const curNoteMsg = window.app.pageOptions.curNoteMsg;
export const curNoteID = window.app.pageOptions.curNoteID;
Expand Down
54 changes: 54 additions & 0 deletions frontend/src/utils/user-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import axios from 'axios';
import cookie from 'react-cookies';
import { siteRoot } from './constants';

class UserAPI {

init({ server, username, password, token }) {
this.server = server;
this.username = username;
this.password = password;
this.token = token;
if (this.token && this.server) {
this.req = axios.create({
baseURL: this.server,
headers: { 'Authorization': 'Token ' + this.token },
});
}
return this;
}

initForSeahubUsage({ siteRoot, xcsrfHeaders }) {
if (siteRoot && siteRoot.charAt(siteRoot.length - 1) === '/') {
var server = siteRoot.substring(0, siteRoot.length - 1);
this.server = server;
} else {
this.server = siteRoot;
}

this.req = axios.create({
headers: {
'X-CSRFToken': xcsrfHeaders,
}
});
return this;
}

getOfficeSuite(repoID) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/office-suite/';
return this.req.get(url);
}

setOfficeSuite(repoID, suiteID) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/office-suite/';
const form = new FormData();
form.append('suite_id', suiteID);
return this.req.put(url, form);
}
}

let userAPI = new UserAPI();
let xcsrfHeaders = cookie.load('sfcsrftoken');
userAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });

export { userAPI };
4 changes: 2 additions & 2 deletions frontend/src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice, enableSeadoc } from './constants';
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableSeadoc } from './constants';
import TextTranslation from './text-translation';
import React from 'react';
import toaster from '../components/toast';
Expand Down Expand Up @@ -640,7 +640,7 @@ export const Utils = {
list.push(HISTORY);
}

if (permission == 'rw' && enableOnlyoffice &&
if (permission == 'rw' && currentRepoInfo.enable_onlyoffice &&
onlyofficeConverterExtensions.includes(this.getFileExtension(dirent.name, false))) {
list.push(ONLYOFFICE_CONVERT);
}
Expand Down
Loading
Loading