1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-16 23:29:49 +00:00

Merge branch '7.0'

This commit is contained in:
plt
2019-10-21 16:44:49 +08:00
26 changed files with 197 additions and 125 deletions

View File

@@ -135,8 +135,13 @@ class App extends Component {
navigate(url, {repalce: true});
} else {
let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path);
let newWindow = window.open('about:blank');
newWindow.location.href = url;
let isWeChat = Utils.isWeChat();
if (!isWeChat) {
let newWindow = window.open('about:blank');
newWindow.location.href = url;
} else {
location.href = url;
}
}
}

View File

@@ -25,11 +25,6 @@ class GenerateShareLink extends React.Component {
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0);
this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault;
if (isPro) {
this.editOption = 'edit_download';
this.permissionOptions = ['preview_download', 'preview_only'];
}
this.state = {
isValidate: false,
isShowPasswordInput: false,
@@ -42,7 +37,8 @@ class GenerateShareLink extends React.Component {
sharedLinkInfo: null,
isNoticeMessageShow: false,
isLoading: true,
currentPermission: isPro ? this.permissionOptions[0] : '',
permissionOptions: [],
currentPermission: '',
isSendLinkShown: false
};
}
@@ -65,15 +61,32 @@ class GenerateShareLink extends React.Component {
toaster.danger(errMessage);
});
if (isPro && Utils.isEditableOfficeFile(path)) {
seafileAPI.getFileInfo(repoID, path).then((res) => {
if (res.data.can_edit) {
this.permissionOptions.push(this.editOption);
if (isPro) {
if (this.props.itemType === 'library') {
let permissionOptions = Utils.getShareLinkPermissionList(this.props.itemType, '', path);
this.setState({
permissionOptions: permissionOptions,
currentPermission: permissionOptions[0],
});
} else {
let getDirentInfoAPI;
if (this.props.itemType === 'file') {
getDirentInfoAPI = seafileAPI.getFileInfo(repoID, path);
} else if (this.props.itemType === 'dir') {
getDirentInfoAPI = seafileAPI.getDirInfo(repoID, path);
}
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
getDirentInfoAPI.then((res) => {
let permission = res.data.permission;
let permissionOptions = Utils.getShareLinkPermissionList(this.props.itemType, permission, path);
this.setState({
permissionOptions: permissionOptions,
currentPermission: permissionOptions[0],
});
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
}
}
@@ -260,7 +273,6 @@ class GenerateShareLink extends React.Component {
}
render() {
if (this.state.isLoading) {
return <Loading />;
}
@@ -394,7 +406,7 @@ class GenerateShareLink extends React.Component {
<span>{gettext('Set permission')}</span>
</Label>
</FormGroup>
{this.permissionOptions.map((item, index) => {
{this.state.permissionOptions.map((item, index) => {
return (
<FormGroup check className="permission" key={index}>
<Label className="form-check-label">

View File

@@ -129,6 +129,7 @@ class ShareDialog extends React.Component {
itemPath={this.props.itemPath}
repoID={this.props.repoID}
closeShareDialog={this.props.toggleDialog}
itemType={itemType}
/>
</TabPane>
}
@@ -166,6 +167,7 @@ class ShareDialog extends React.Component {
renderFileContent = () => {
let activeTab = this.state.activeTab;
const { itemType } = this.props;
return (
<Fragment>
@@ -190,6 +192,7 @@ class ShareDialog extends React.Component {
itemPath={this.props.itemPath}
repoID={this.props.repoID}
closeShareDialog={this.props.toggleDialog}
itemType={itemType}
/>
</TabPane>
{activeTab === 'internalLink' &&

View File

@@ -494,11 +494,13 @@ class DirentListItem extends React.Component {
}
renderItemOperation = () => {
let { dirent, selectedDirentList, currentRepoInfo } = this.props;
if (currentRepoInfo.permission === 'cloud-edit' || currentRepoInfo.permission === 'preview') {
return '';
}
let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent);
let { dirent, selectedDirentList } = this.props;
// no need to check whether show shareBtn or not.
// according to specification below, shareBtn aways show.
// check for "generate uploadlink" or other tabs should put inside the shareDialog.
// https://dev.seafile.com/seahub/lib/d6f300e7-bb2b-4722-b83e-cf45e370bfbc/file/seaf-server%20%E5%8A%9F%E8%83%BD%E8%AE%BE%E8%AE%A1/%E6%9D%83%E9%99%90%E7%9B%B8%E5%85%B3/%E8%B5%84%E6%96%99%E5%BA%93%E6%9D%83%E9%99%90%E8%A7%84%E8%8C%83.md
// let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent);
return (
<Fragment>
@@ -512,11 +514,9 @@ class DirentListItem extends React.Component {
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
</li>
)}
{showShareBtn && (
<li className="operation-group-item">
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
</li>
)}
<li className="operation-group-item">
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
</li>
{dirent.permission === 'rw' && (
<li className="operation-group-item">
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
@@ -546,7 +546,7 @@ class DirentListItem extends React.Component {
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
</li>
)}
{showShareBtn && (
{(
<li className="operation-group-item">
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
</li>

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@reach/router';
import { Badge } from 'reactstrap';
import { gettext, siteRoot, enableWiki, canAddRepo, canGenerateShareLink, canGenerateUploadLink, canInvitePeople, dtableWebServer } from '../utils/constants';
import { gettext, siteRoot, canPublishRepo, canAddRepo, canGenerateShareLink, canGenerateUploadLink, canInvitePeople } from '../utils/constants';
import { seafileAPI } from '../utils/seafile-api';
import { Utils } from '../utils/utils';
import toaster from './toast';
@@ -65,8 +65,8 @@ class MainSideNav extends React.Component {
}
tabItemClick = (e, param, id) => {
if (window.uploader &&
window.uploader.isUploadProgressDialogShow &&
if (window.uploader &&
window.uploader.isUploadProgressDialogShow &&
window.uploader.totalProgress !== 100) {
if (!window.confirm(gettext('A file is being uploaded. Are you sure you want to leave this page?'))) {
e.preventDefault();
@@ -81,7 +81,7 @@ class MainSideNav extends React.Component {
let url = dtableWebServer;
window.open(url);
}
getActiveClass = (tab) => {
return this.props.currentTab === tab ? 'active' : '';
}
@@ -93,7 +93,7 @@ class MainSideNav extends React.Component {
}
return (
<ul className={`nav sub-nav nav-pills flex-column grp-list ${this.state.groupsExtended ? 'side-panel-slide' : 'side-panel-slide-up'}`} style={style}>
<li className="nav-item">
<li className="nav-item">
<Link to={siteRoot + 'groups/'} className={`nav-link ellipsis ${this.getActiveClass('groups')}`} onClick={(e) => this.tabItemClick(e, 'groups')}>
<span className="sharp" aria-hidden="true">#</span>
<span className="nav-text">{gettext('All Groups')}</span>
@@ -101,7 +101,7 @@ class MainSideNav extends React.Component {
</li>
{this.state.groupItems.map(item => {
return (
<li key={item.id} className="nav-item">
<li key={item.id} className="nav-item">
<Link to={siteRoot + 'group/' + item.id + '/'} className={`nav-link ellipsis ${this.getActiveClass(item.name)}`} onClick={(e) => this.tabItemClick(e, item.name, item.id)}>
<span className="sharp" aria-hidden="true">#</span>
<span className="nav-text">{item.name}</span>
@@ -180,7 +180,7 @@ class MainSideNav extends React.Component {
}
render() {
let showActivity = isDocs || isPro;
let showActivity = isDocs || isPro;
return (
<div className="side-nav">
<div className="side-nav-con">
@@ -235,7 +235,7 @@ class MainSideNav extends React.Component {
</Link>
</li>
}
{enableWiki &&
{canPublishRepo &&
<li className="nav-item">
<Link className={`nav-link ellipsis ${this.getActiveClass('published')}`} to={siteRoot + 'published/'} title={gettext('Published Libraries')} onClick={(e) => this.tabItemClick(e, 'published')}>
<span className="sf2-icon-wiki-view" aria-hidden="true"></span>
@@ -248,7 +248,7 @@ class MainSideNav extends React.Component {
<Link className={`nav-link ellipsis ${this.getActiveClass('drafts')}`} to={siteRoot + 'drafts/'} title={gettext('Drafts')}>
<span className="sf2-icon-edit" aria-hidden="true"></span>
<span className="draft-info nav-text">
{gettext('Drafts')}
{gettext('Drafts')}
{this.props.draftCounts === 0 ? '' : <Badge color="info" pill>{this.props.draftCounts}</Badge>}
</span>
</Link>

View File

@@ -46,6 +46,10 @@ class SelectEditor extends React.Component {
});
}
componentWillReceiveProps() {
this.setOptions();
}
componentWillUnmount() {
document.removeEventListener('click', this.onHideSelect);
}

View File

@@ -68,6 +68,7 @@ img[src=""] {
.cur-view-content {
display: flex;
flex-direction: column;
overflow: hidden;
}
.cur-view-content .wiki-page-container {

View File

@@ -1068,9 +1068,14 @@ class LibContentView extends React.Component {
if (this.state.currentMode === 'column' && Utils.isMarkdownFile(direntPath)) {
this.showColumnMarkdownFile(direntPath);
} else {
const w=window.open('about:blank');
const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath);
w.location.href = url;
let isWeChat = Utils.isWeChat();
if (!isWeChat) {
let newWindow = window.open('about:blank');
newWindow.location.href = url;
} else {
location.href = url;
}
}
}
}

View File

@@ -91,56 +91,60 @@ class Item extends Component {
constructor(props) {
super(props);
const item = this.props.item;
if (isPro) {
this.editOption = 'edit_download';
this.permissionOptions = ['preview_download', 'preview_only'];
this.updatePermissionOptions();
}
this.state = {
currentPermission: isPro ? this.getCurrentPermission() : '',
isOpIconShown: false,
isOpMenuOpen: false, // for mobile
isPermSelectDialogOpen: false, // for mobile
isLinkDialogOpen: false
isLinkDialogOpen: false,
permissionOptions: [],
currentPermission: '',
};
}
componentDidMount() {
if (isPro) {
this.updatePermissionOptions();
}
}
updatePermissionOptions = () => {
const item = this.props.item;
let options = this.permissionOptions;
if (!Utils.isEditableOfficeFile(item.obj_name)) {
return ;
if (item.is_dir && item.path === '/') {
let permissionOptions = Utils.getShareLinkPermissionList('library', '', item.path);
this.setState({
permissionOptions: permissionOptions,
});
} else {
let { repo_id, path } = item;
let getDirentInfoAPI = item.is_dir ? seafileAPI.getDirInfo(repo_id, path) : seafileAPI.getFileInfo(repo_id, path);
getDirentInfoAPI.then((res) => {
let itemType = item.is_dir ? 'dir' : 'file';
let permission = res.data.permission;
let permissionOptions = Utils.getShareLinkPermissionList(itemType, permission, item.path);
this.setState({
permissionOptions: permissionOptions,
});
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
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 ;
this.setState({
currentPermission: this.getCurrentPermission(),
});
}
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];
return 'preview_download';
case 'false false':
return options[1];
return 'preview_only';
case 'true true':
return this.editOption;
return 'edit_download';
case 'true false':
return 'cloud_edit';
}
}
@@ -214,7 +218,7 @@ class Item extends Component {
render() {
const item = this.props.item;
const { currentPermission, isOpIconShown, isPermSelectDialogOpen, isLinkDialogOpen } = this.state;
const { currentPermission, permissionOptions , isOpIconShown, isPermSelectDialogOpen, isLinkDialogOpen } = this.state;
let iconUrl, objUrl;
if (item.is_dir) {
@@ -242,7 +246,7 @@ class Item extends Component {
isTextMode={true}
isEditIconShow={isOpIconShown && !item.is_expired}
currentPermission={currentPermission}
permissionOptions={this.permissionOptions}
permissionOptions={permissionOptions}
onPermissionChanged={this.changePerm}
/>
</td>
@@ -294,7 +298,7 @@ class Item extends Component {
{isPermSelectDialogOpen &&
<ShareLinkPermissionSelect
currentPerm={currentPermission}
permissions={this.permissionOptions}
permissions={permissionOptions}
changePerm={this.changePerm}
toggleDialog={this.togglePermSelectDialog}
/>

View File

@@ -208,7 +208,7 @@ class Item extends Component {
let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
let shareIconClassName = 'op-icon sf2-icon-share repo-share-btn' + iconVisibility;
let leaveShareIconClassName = 'op-icon sf2-icon-x3' + iconVisibility;
let shareRepoUrl =`${siteRoot}library/${data.repo_id}/${Utils.encodePath(data.repo_name)}/`;
const desktopItem = (
<Fragment>
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
@@ -217,7 +217,7 @@ class Item extends Component {
{this.state.isStarred && <i className="fas fa-star cursor-pointer" onClick={this.onStarRepo}></i>}
</td>
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<td><Link to={`${siteRoot}library/${data.repo_id}/${data.repo_name}/`}>{data.repo_name}</Link></td>
<td><Link to={shareRepoUrl}>{data.repo_name}</Link></td>
<td>
{(isPro && data.is_admin) &&
<a href="#" className={shareIconClassName} title={gettext('Share')} onClick={this.share}></a>
@@ -251,7 +251,7 @@ class Item extends Component {
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<td>
<Link to={`${siteRoot}library/${data.repo_id}/${data.repo_name}/`}>{data.repo_name}</Link><br />
<Link to={shareRepoUrl}>{data.repo_name}</Link><br />
<span className="item-meta-info" title={data.owner_contact_email}>{data.owner_name}</span>
<span className="item-meta-info">{data.size}</span>
<span className="item-meta-info" title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</span>

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Button } from 'reactstrap';
import MediaQuery from 'react-responsive';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, loginUrl, canPublishRepo } from '../../utils/constants';
import { gettext, loginUrl } from '../../utils/constants';
import toaster from '../../components/toast';
import ModalPortal from '../../components/modal-portal';
import EmptyTip from '../../components/empty-tip';
@@ -130,14 +130,14 @@ class Wikis extends Component {
<div className="cur-view-toolbar">
<span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu" onClick={this.props.onShowSidePanel}></span>
<div className="operation">
{canPublishRepo && <Fragment>
<Fragment>
<MediaQuery query="(min-width: 768px)">
<Button className="btn btn-secondary operation-item" onClick={this.onSelectToggle}>{gettext('Publish a Library')}</Button>
</MediaQuery>
<MediaQuery query="(max-width: 767.8px)">
<span className="sf2-icon-plus mobile-toolbar-icon" title={gettext('Publish a Library')} onClick={this.onSelectToggle}></span>
</MediaQuery>
</Fragment>}
</Fragment>
</div>
</div>
<CommonToolbar onSearchedClick={this.props.onSearchedClick} />

View File

@@ -47,7 +47,6 @@ export const shareLinkExpireDaysMin = window.app.pageOptions.shareLinkExpireDays
export const shareLinkExpireDaysMax = window.app.pageOptions.shareLinkExpireDaysMax;
export const shareLinkExpireDaysDefault = window.app.pageOptions.shareLinkExpireDaysDefault;
export const maxFileName = window.app.pageOptions.maxFileName;
export const enableWiki = window.app.pageOptions.enableWiki;
export const canPublishRepo = window.app.pageOptions.canPublishRepo;
export const enableEncryptedLibrary = window.app.pageOptions.enableEncryptedLibrary;
export const enableRepoHistorySetting = window.app.pageOptions.enableRepoHistorySetting;

View File

@@ -38,6 +38,13 @@ export const Utils = {
return window.innerWidth >= 768;
},
isWeChat: function() {
let ua = window.navigator.userAgent.toLowerCase();
let isWeChat = ua.match(/MicroMessenger/i) == 'micromessenger';
let isEnterpriseWeChat = ua.match(/MicroMessenger/i) == 'micromessenger' && ua.match(/wxwork/i) == 'wxwork';
return isEnterpriseWeChat || isWeChat;
},
FILEEXT_ICON_MAP: {
// text file
@@ -109,6 +116,39 @@ export const Utils = {
}
},
getShareLinkPermissionList: function(itemType, permission, path) {
// itemType: library, dir, file
// permission: rw, r, admin, cloud-edit, preview
// if item is library, can preview and download, no need to check
// if item is dir, check can download
// if item is file, check can download and check can edit
let editDownloadOption = 'edit_download';
let editOnly = 'cloud_edit';
let downloadOption = 'preview_download';
let permissionOptions = ['preview_only'];
if (itemType === 'library') {
permissionOptions.push(downloadOption);
} else if (itemType === 'dir') {
if (permission == 'rw' || permission == 'admin' || permission == 'r') {
permissionOptions.push(downloadOption);
}
} else if (itemType === 'file') {
if (permission == 'rw' || permission == 'admin' || permission == 'r') {
permissionOptions.push(downloadOption);
}
if (this.isEditableOfficeFile(path) && (permission == 'rw' || permission == 'admin')) {
permissionOptions.push(editDownloadOption);
}
if (this.isEditableOfficeFile(path) && (permission == 'cloud-edit')) {
permissionOptions.push(editOnly);
}
}
return permissionOptions;
},
isEditableOfficeFile: function(filename) {
// no file ext
if (filename.lastIndexOf('.') == -1) {
@@ -572,7 +612,19 @@ export const Utils = {
"can_download": true
}
};
case 'cloud_edit':
return {
value: permission,
text: gettext('Edit on cloud'),
permissionDetails: {
'can_edit': true,
"can_download": false
}
};
}
return {
text: '',
};
},
getDTableShareLinkPermissionObject: function(permission) {