diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js
index bb65662c80..1f6fc7de6b 100644
--- a/frontend/src/components/dialog/generate-share-link.js
+++ b/frontend/src/components/dialog/generate-share-link.js
@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
import moment from 'moment';
import copy from 'copy-to-clipboard';
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
-import { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
+import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
-import SharedLinkInfo from '../../models/shared-link-info';
+import ShareLink from '../../models/share-link';
import toaster from '../toast';
import Loading from '../loading';
import SendLink from '../send-link';
@@ -43,7 +43,7 @@ class GenerateShareLink extends React.Component {
'can_edit': false,
'can_download': true
};
- this.isOfficeFile = Utils.isOfficeFile(this.props.itemPath);
+ this.isOfficeFile = Utils.isEditableOfficeFile(this.props.itemPath);
}
componentDidMount() {
@@ -51,7 +51,7 @@ class GenerateShareLink extends React.Component {
let repoID = this.props.repoID;
seafileAPI.getShareLink(repoID, path).then((res) => {
if (res.data.length !== 0) {
- let sharedLinkInfo = new SharedLinkInfo(res.data[0]);
+ let sharedLinkInfo = new ShareLink(res.data[0]);
this.setState({
isLoading: false,
sharedLinkInfo: sharedLinkInfo
@@ -133,11 +133,10 @@ class GenerateShareLink extends React.Component {
this.setState({errorInfo: ''});
let { itemPath, repoID } = this.props;
let { password, isExpireChecked, expireDays } = this.state;
- let permissions = this.permissions;
- permissions = JSON.stringify(permissions);
+ let permissions = isPro ? JSON.stringify(this.permissions) : '';
const expireDaysSent = isExpireChecked ? expireDays : '';
seafileAPI.createShareLink(repoID, itemPath, password, expireDaysSent, permissions).then((res) => {
- let sharedLinkInfo = new SharedLinkInfo(res.data);
+ let sharedLinkInfo = new ShareLink(res.data);
this.setState({sharedLinkInfo: sharedLinkInfo});
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
@@ -402,6 +401,8 @@ class GenerateShareLink extends React.Component {
)}
+ {isPro && (
+
}
+
+ )}
{this.state.errorInfo && {gettext(this.state.errorInfo)}}
diff --git a/frontend/src/components/dialog/share-link-permission-select.js b/frontend/src/components/dialog/share-link-permission-select.js
new file mode 100644
index 0000000000..3855ee7e1a
--- /dev/null
+++ b/frontend/src/components/dialog/share-link-permission-select.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Modal, ModalBody } from 'reactstrap';
+import { Utils } from '../../utils/utils';
+
+const propTypes = {
+ currentPerm: PropTypes.string.isRequired,
+ permissions: PropTypes.array.isRequired,
+ changePerm: PropTypes.func.isRequired,
+ toggleDialog: PropTypes.func.isRequired
+};
+
+class ShareLinkPermissionSelect extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ currentOption: this.props.currentPerm
+ };
+ }
+
+ switchOption = (e) => {
+ if (!e.target.checked) {
+ return;
+ }
+
+ const currentOption = e.target.value;
+ this.setState({
+ currentOption: currentOption
+ });
+
+ this.props.changePerm(currentOption);
+ this.props.toggleDialog();
+ }
+
+ render() {
+ const options = this.props.permissions;
+ const { currentOption } = this.state;
+
+ return (
+
+
+ {options.map((item, index) => {
+ return (
+
+
+
+
+ );
+ })}
+
+
+ );
+ }
+}
+
+ShareLinkPermissionSelect.propTypes = propTypes;
+
+export default ShareLinkPermissionSelect;
diff --git a/frontend/src/components/select-editor/share-links-permission-editor.js b/frontend/src/components/select-editor/share-link-permission-editor.js
similarity index 66%
rename from frontend/src/components/select-editor/share-links-permission-editor.js
rename to frontend/src/components/select-editor/share-link-permission-editor.js
index d83c2d5167..6f97855214 100644
--- a/frontend/src/components/select-editor/share-links-permission-editor.js
+++ b/frontend/src/components/select-editor/share-link-permission-editor.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { gettext } from '../../utils/constants';
+import { Utils } from '../../utils/utils';
import SelectEditor from './select-editor';
const propTypes = {
@@ -11,16 +11,10 @@ const propTypes = {
onPermissionChanged: PropTypes.func.isRequired
};
-class ShareLinksPermissionEditor extends React.Component {
+class ShareLinkPermissionEditor extends React.Component {
translatePermission = (permission) => {
- if (permission === 'Preview only') {
- return gettext('Preview only');
- }
-
- if (permission === 'Preview and download') {
- return gettext('Preview and download');
- }
+ return Utils.getShareLinkPermissionObject(permission).text;
}
render() {
@@ -37,6 +31,6 @@ class ShareLinksPermissionEditor extends React.Component {
}
}
-ShareLinksPermissionEditor.propTypes = propTypes;
+ShareLinkPermissionEditor.propTypes = propTypes;
-export default ShareLinksPermissionEditor;
+export default ShareLinkPermissionEditor;
diff --git a/frontend/src/models/shared-link-info.js b/frontend/src/models/share-link.js
similarity index 90%
rename from frontend/src/models/shared-link-info.js
rename to frontend/src/models/share-link.js
index 9f887d3b70..47dd5afd39 100644
--- a/frontend/src/models/shared-link-info.js
+++ b/frontend/src/models/share-link.js
@@ -1,4 +1,4 @@
-class SharedLinkInfo {
+class ShareLink {
constructor(object) {
this.repo_id = object.repo_id;
@@ -18,4 +18,4 @@ class SharedLinkInfo {
}
-export default SharedLinkInfo;
+export default ShareLink;
diff --git a/frontend/src/pages/share-admin/share-links.js b/frontend/src/pages/share-admin/share-links.js
index 6ae94bb67f..1e22395917 100644
--- a/frontend/src/pages/share-admin/share-links.js
+++ b/frontend/src/pages/share-admin/share-links.js
@@ -1,15 +1,18 @@
import React, { Component, Fragment } from 'react';
import { Link } from '@reach/router';
import moment from 'moment';
+import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import copy from '@seafile/seafile-editor/dist//utils/copy-to-clipboard';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
-import { gettext, siteRoot, loginUrl, canGenerateUploadLink } from '../../utils/constants';
-import SharedLinkInfo from '../../models/shared-link-info';
-import ShareLinksPermissionEditor from '../../components/select-editor/share-links-permission-editor';
+import { isPro, gettext, siteRoot, loginUrl, canGenerateUploadLink } from '../../utils/constants';
+import ShareLink from '../../models/share-link';
+import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
+import Loading from '../../components/loading';
import toaster from '../../components/toast';
import EmptyTip from '../../components/empty-tip';
+import ShareLinkPermissionSelect from '../../components/dialog/share-link-permission-select';
class Content extends Component {
@@ -62,7 +65,7 @@ class Content extends Component {
const { loading, errorMsg, items, sortBy, sortOrder } = this.props;
if (loading) {
- return ;
+ return ;
} else if (errorMsg) {
return
{errorMsg}
;
} else {
@@ -78,23 +81,34 @@ class Content extends Component {
const sortByTime = sortBy == 'time';
const sortIcon = sortOrder == 'asc' ? : ;
+ const isDesktop = Utils.isDesktop();
+ // only for some columns
+ const columnWidths = isPro ? ['14%', '7%', '14%'] : ['21%', '14%', '20%'];
const table = (
-
+
@@ -121,20 +135,76 @@ class Item extends Component {
constructor(props) {
super(props);
- let item = this.props.item;
+ const item = this.props.item;
+
+ if (isPro) {
+ this.editOption = 'edit_download';
+ this.permissionOptions = ['preview_download', 'preview_only'];
+ this.updatePermissionOptions();
+ }
+
this.state = {
- currentPermission: item.permissions.can_download ? 'Preview and download' : 'Preview only',
- showOpIcon: false,
+ currentPermission: isPro ? this.getCurrentPermission() : '',
+ isOpIconShown: false,
+ isOpMenuOpen: false, // for mobile
+ isPermSelectDialogOpen: false // for mobile
};
- this.permissionOptions = ['Preview only', 'Preview and download'];
+ }
+
+ updatePermissionOptions = () => {
+ const item = this.props.item;
+ let options = this.permissionOptions;
+
+ if (!Utils.isEditableOfficeFile(item.obj_name)) {
+ return ;
+ }
+
+ if (item.permissions.can_edit) {
+ options.push(this.editOption);
+ return ;
+ }
+
+ seafileAPI.getFileInfo(item.repo_id, item.path).then((res) => {
+ if (res.data.can_edit) {
+ options.push(this.editOption);
+ return ;
+ }
+ }).catch(error => {
+ return ;
+ });
+ }
+
+ getCurrentPermission = () => {
+ const options = this.permissionOptions;
+ const { can_edit, can_download } = this.props.item.permissions;
+ switch (`${can_edit} ${can_download}`) {
+ case 'false true':
+ return options[0];
+ case 'false false':
+ return options[1];
+ case 'true true':
+ return this.editOption;
+ }
+ }
+
+ toggleOpMenu = () => {
+ this.setState({
+ isOpMenuOpen: !this.state.isOpMenuOpen
+ });
+ }
+
+ togglePermSelectDialog = () => {
+ this.setState({
+ isPermSelectDialogOpen: !this.state.isPermSelectDialogOpen
+ });
}
handleMouseOver = () => {
- this.setState({showOpIcon: true});
+ this.setState({isOpIconShown: true});
}
handleMouseOut = () => {
- this.setState({showOpIcon: false});
+ this.setState({isOpIconShown: false});
}
viewLink = (e) => {
@@ -147,22 +217,6 @@ class Item extends Component {
this.props.onRemoveLink(this.props.item);
}
- getLinkParams = () => {
- let item = this.props.item;
- let iconUrl = '';
- let linkUrl = '';
- if (item.is_dir) {
- let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1);
- iconUrl = Utils.getFolderIconUrl(false);
- linkUrl = `${siteRoot}library/${item.repo_id}/${item.repo_name}${Utils.encodePath(path)}`;
- } else {
- iconUrl = Utils.getFileIconUrl(item.obj_name);
- linkUrl = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`;
- }
-
- return { iconUrl, linkUrl };
- }
-
renderExpriedData = () => {
let item = this.props.item;
if (!item.expire_date) {
@@ -181,64 +235,112 @@ class Item extends Component {
);
}
- changePerm = (changed_to_permissions) => {
+ changePerm = (permission) => {
const item = this.props.item;
- let permissions = item.permissions;
-
- if (changed_to_permissions === 'Preview only')
- permissions.can_download = false;
- else if (changed_to_permissions === 'Preview and download')
- permissions.can_download = true;
-
- seafileAPI.updateShareLink(item.token, JSON.stringify(permissions)).then(() => {
+ const permissionDetails = Utils.getShareLinkPermissionObject(permission).permissionDetails;
+ seafileAPI.updateShareLink(item.token, JSON.stringify(permissionDetails)).then(() => {
this.setState({
- currentPermission: changed_to_permissions,
+ currentPermission: permission
});
- let message = gettext("Successfully modified permission.");
+ let message = gettext('Successfully modified permission.');
toaster.success(message);
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
- if (errMessage === gettext('Error')) {
- errMessage = gettext("Failed to modify permission.");
- }
toaster.danger(errMessage);
});
}
render() {
const item = this.props.item;
- let { iconUrl, linkUrl } = this.getLinkParams();
+ const { currentPermission, isOpIconShown, isPermSelectDialogOpen } = this.state;
- let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
- let linkIconClassName = 'sf2-icon-link action-icon' + iconVisibility;
- let deleteIconClassName = 'sf2-icon-delete action-icon' + iconVisibility;
- return (
+ let iconUrl, linkUrl;
+ if (item.is_dir) {
+ let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1);
+ iconUrl = Utils.getFolderIconUrl(false);
+ linkUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`;
+ } else {
+ iconUrl = Utils.getFileIconUrl(item.obj_name);
+ linkUrl = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`;
+ }
+
+ const desktopItem = (
-  |
+  |
{item.is_dir ?
{item.obj_name} :
{item.obj_name}
}
|
- {item.repo_name} |
+ {item.repo_name} |
+ {isPro &&
-
|
+ }
{item.view_cnt} |
{this.renderExpriedData()} |
-
-
+
+
|
);
+
+ const mobileItem = (
+
+
+  |
+
+ {item.is_dir ?
+ {item.obj_name} :
+ {item.obj_name}
+ }
+ {isPro && {Utils.getShareLinkPermissionObject(currentPermission).text}}
+
+ {item.repo_name}
+ {item.view_cnt}({gettext('Visits')})
+ {this.renderExpriedData()}({gettext('Expiration')})
+ |
+
+
+
+
+
+
+ {(isPro && !item.is_expired) && {gettext('Permission')}}
+ {gettext('View')}
+ {gettext('Remove')}
+
+
+
+ |
+
+ {isPermSelectDialogOpen &&
+
+ }
+
+ );
+
+ return this.props.isDesktop ? desktopItem : mobileItem;
}
}
@@ -305,9 +407,8 @@ class ShareAdminShareLinks extends Component {
componentDidMount() {
seafileAPI.listShareLinks().then((res) => {
- // res: {data: Array(2), status: 200, statusText: "OK", headers: {…}, config: {…}, …}
let items = res.data.map(item => {
- return new SharedLinkInfo(item);
+ return new ShareLink(item);
});
this.setState({
loading: false,
@@ -327,7 +428,6 @@ class ShareAdminShareLinks extends Component {
errorMsg: gettext('Error')
});
}
-
} else {
this.setState({
loading: false,
@@ -343,13 +443,10 @@ class ShareAdminShareLinks extends Component {
return uploadItem.token !== item.token;
});
this.setState({items: items});
- let message = gettext("Successfully deleted share link.");
+ let message = gettext('Successfully deleted 1 item.');
toaster.success(message);
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
- if (errMessage === gettext('Error')) {
- errMessage = gettext("Failed to delete share link.");
- }
toaster.danger(errMessage);
});
}
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index ce8ce32c48..3ed85188c6 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -108,13 +108,13 @@ export const Utils = {
}
},
- isOfficeFile: function(filename) {
+ isEditableOfficeFile: function(filename) {
// no file ext
if (filename.lastIndexOf('.') == -1) {
return false;
}
var file_ext = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
- var exts = ['doc', 'ppt', 'xls', 'docx', 'pptx', 'xlsx'];
+ var exts = ['docx', 'pptx', 'xlsx'];
if (exts.indexOf(file_ext) != -1) {
return true;
} else {
@@ -421,6 +421,38 @@ export const Utils = {
return title;
},
+ getShareLinkPermissionObject: function(permission) {
+ switch (permission) {
+ case 'preview_download':
+ return {
+ value: permission,
+ text: gettext('Preview and download'),
+ permissionDetails: {
+ 'can_edit': false,
+ "can_download": true
+ }
+ };
+ case 'preview_only':
+ return {
+ value: permission,
+ text: gettext('Preview only'),
+ permissionDetails: {
+ 'can_edit': false,
+ "can_download": false
+ }
+ };
+ case 'edit_download':
+ return {
+ value: permission,
+ text: gettext('Edit on cloud and download'),
+ permissionDetails: {
+ 'can_edit': true,
+ "can_download": true
+ }
+ };
+ }
+ },
+
formatSize: function(options) {
/*
* param: {bytes, precision}