|
|
@@ -7,25 +7,24 @@ import { createSubscribedElement } from '../../UnstatedUtils';
|
|
|
import AppContainer from '../../../services/AppContainer';
|
|
|
// import { toastSuccess, toastError } from '../../../util/apiNotification';
|
|
|
|
|
|
-class ExportPage extends React.Component {
|
|
|
+class ExportAsZip extends React.Component {
|
|
|
|
|
|
constructor(props) {
|
|
|
super(props);
|
|
|
|
|
|
this.state = {
|
|
|
- files: {},
|
|
|
+ zipFileStats: [],
|
|
|
collections: new Set(),
|
|
|
};
|
|
|
|
|
|
this.toggleCheckbox = this.toggleCheckbox.bind(this);
|
|
|
- this.exportMultiple = this.exportMultiple.bind(this);
|
|
|
+ this.export = this.export.bind(this);
|
|
|
this.deleteZipFile = this.deleteZipFile.bind(this);
|
|
|
}
|
|
|
|
|
|
async componentDidMount() {
|
|
|
- const res = await this.props.appContainer.apiGet('/v3/export/status', {});
|
|
|
-
|
|
|
- this.setState({ files: res.files });
|
|
|
+ const { zipFileStats } = await this.props.appContainer.apiGet('/v3/export/status', {});
|
|
|
+ this.setState({ zipFileStats });
|
|
|
}
|
|
|
|
|
|
toggleCheckbox(e) {
|
|
|
@@ -45,7 +44,7 @@ class ExportPage extends React.Component {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- async exportMultiple() {
|
|
|
+ async export() {
|
|
|
// TODO use appContainer.apiv3.post
|
|
|
const res = await this.props.appContainer.apiPost('/v3/export', { collections: Array.from(this.state.collections) });
|
|
|
// TODO toastSuccess, toastError
|
|
|
@@ -59,21 +58,21 @@ class ExportPage extends React.Component {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- async deleteZipFile() {
|
|
|
+ async deleteZipFile(zipFile) {
|
|
|
// TODO use appContainer.apiv3.delete
|
|
|
+ await this.props.appContainer.apiRequest('delete', `/v3/export/${zipFile}`, {});
|
|
|
// TODO toastSuccess, toastError
|
|
|
}
|
|
|
|
|
|
render() {
|
|
|
// const { t } = this.props;
|
|
|
+ const collections = ['pages', 'revisions'];
|
|
|
|
|
|
return (
|
|
|
<Fragment>
|
|
|
<h2>Export Data as Zip</h2>
|
|
|
<form className="my-5">
|
|
|
- {Object.keys(this.state.files).map((collectionName) => {
|
|
|
- const disabled = !(collectionName === 'pages' || collectionName === 'revisions');
|
|
|
- const stat = this.state.files[collectionName] || {};
|
|
|
+ {collections.map((collectionName) => {
|
|
|
return (
|
|
|
<div className="checkbox checkbox-info" key={collectionName}>
|
|
|
<input
|
|
|
@@ -82,40 +81,63 @@ class ExportPage extends React.Component {
|
|
|
name={collectionName}
|
|
|
className="form-check-input"
|
|
|
value={collectionName}
|
|
|
- disabled={disabled}
|
|
|
checked={this.state.collections.has(collectionName)}
|
|
|
onChange={this.toggleCheckbox}
|
|
|
/>
|
|
|
- <label className={`form-check-label ml-3 ${disabled ? 'text-muted' : ''}`} htmlFor={collectionName}>
|
|
|
- {collectionName} ({stat.name || 'not found'}) ({stat.mtime ? format(new Date(stat.mtime), 'yyyy/MM/dd HH:mm:ss') : ''})
|
|
|
+ <label className="form-check-label ml-3" htmlFor={collectionName}>
|
|
|
+ {collectionName}
|
|
|
</label>
|
|
|
</div>
|
|
|
);
|
|
|
})}
|
|
|
</form>
|
|
|
- <button type="button" className="btn btn-sm btn-default" onClick={this.exportMultiple}>Generate</button>
|
|
|
- <a href="/_api/v3/export">
|
|
|
- <button type="button" className="btn btn-sm btn-primary ml-2">Download</button>
|
|
|
- </a>
|
|
|
- {/* <button type="button" className="btn btn-sm btn-danger ml-2" onClick={this.deleteZipFile}>Clear</button> */}
|
|
|
+ <button type="button" className="btn btn-sm btn-default" onClick={this.export}>Generate</button>
|
|
|
+
|
|
|
+ <table className="table table-bordered">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>File</th>
|
|
|
+ <th>Growi Version</th>
|
|
|
+ <th>Collections</th>
|
|
|
+ <th>Exported At</th>
|
|
|
+ <th></th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ {this.state.zipFileStats.map(({ meta, fileName, fileStats }) => {
|
|
|
+ return (
|
|
|
+ <tr key={meta}>
|
|
|
+ <th>{fileName}</th>
|
|
|
+ <td>{meta.version}</td>
|
|
|
+ <td>{fileStats.map(fileStat => fileStat.collectionName).join(', ')}</td>
|
|
|
+ <td>{meta.exportedAt ? format(new Date(meta.exportedAt), 'yyyy/MM/dd HH:mm:ss') : ''}</td>
|
|
|
+ <td>
|
|
|
+ <a href="/_api/v3/export">
|
|
|
+ <button type="button" className="btn btn-sm btn-primary ml-2">Download</button>
|
|
|
+ </a>
|
|
|
+ <button type="button" className="btn btn-sm btn-danger ml-2" onClick={() => this.deleteZipFile(fileName)}>Delete</button>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
</Fragment>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-ExportPage.propTypes = {
|
|
|
+ExportAsZip.propTypes = {
|
|
|
t: PropTypes.func.isRequired, // i18next
|
|
|
appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
|
-
|
|
|
- isAclEnabled: PropTypes.bool,
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* Wrapper component for using unstated
|
|
|
*/
|
|
|
-const ExportPageWrapper = (props) => {
|
|
|
- return createSubscribedElement(ExportPage, props, [AppContainer]);
|
|
|
+const ExportAsZipWrapper = (props) => {
|
|
|
+ return createSubscribedElement(ExportAsZip, props, [AppContainer]);
|
|
|
};
|
|
|
|
|
|
-export default withTranslation()(ExportPageWrapper);
|
|
|
+export default withTranslation()(ExportAsZipWrapper);
|