1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-13 10:25:46 +00:00

Merge branch 'master' into 13.0

This commit is contained in:
Michael An 2025-02-19 15:02:15 +08:00
commit 71966dd729
17 changed files with 96 additions and 73 deletions

View File

@ -17,7 +17,7 @@ function EmptyTip({ className = '', title, text, children }) {
EmptyTip.propTypes = {
className: PropTypes.string,
title: PropTypes.string,
text: PropTypes.string,
text: PropTypes.any,
children: PropTypes.any,
};

View File

@ -19,7 +19,7 @@ const propTypes = {
repoID: PropTypes.string,
onDirentItemClick: PropTypes.func,
onRepoItemClick: PropTypes.func,
mode: PropTypes.string.isRequired,
mode: PropTypes.string,
fileSuffixes: PropTypes.arrayOf(PropTypes.string),
currentPath: PropTypes.string,
searchResults: PropTypes.array,

View File

@ -2,10 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import TreeListView from './tree-list-view';
import TreeNode from '../../components/tree-view/tree-node';
import Dirent from '../../models/dirent';
import { seafileAPI } from '../../utils/seafile-api';
import treeHelper from '../../components/tree-view/tree-helper';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import Dirent from '../../models/dirent';
import toaster from '../toast';
const propTypes = {
@ -54,7 +54,7 @@ class RepoListItem extends React.Component {
return;
}
if (repo && repo.repo_id === this.props.selectedRepo.repo_id || isCurrentRepo) {
if (this.props.selectedRepo && repo && repo.repo_id === this.props.selectedRepo.repo_id || isCurrentRepo) {
this.loadRepoDirentList(repo);
this.loadRepoTimer = setTimeout(() => {
const repoID = repo.repo_id;
@ -69,7 +69,7 @@ class RepoListItem extends React.Component {
componentDidUpdate(prevProps) {
const { repo, selectedRepo, selectedPath, newFolderName } = this.props;
// create new folder in selected repo or folder
if (repo.repo_id === selectedRepo.repo_id && prevProps.selectedRepo !== selectedRepo) {
if (repo && selectedRepo && repo.repo_id === selectedRepo.repo_id && prevProps.selectedRepo !== selectedRepo) {
seafileAPI.listDir(repo.repo_id, selectedPath).then(res => {
if (!this.isComponentMounted) return;
const direntData = res.data.dirent_list.find(item => item.type === 'dir' && item.name === newFolderName);

View File

@ -136,3 +136,7 @@ body {
border-radius: 3px;
margin: 0;
}
.sf-save-file .file-chooser-scroll-wrapper .file-chooser-list-view-header {
display: none;
}

View File

@ -98,8 +98,9 @@
}
.system-statistic-button {
height: 31px;
margin-left: 1rem;
height: 30px;
font-weight: normal;
}
.rc-calendar table {

View File

@ -125,7 +125,7 @@ class StatisticCommonTool extends React.Component {
value={endValue}
onChange={this.onChange.bind(this, 'endValue')}
/>
<Button color="primary" className="operation-item system-statistic-button" onClick={this.onSubmit}>{gettext('Submit')}</Button>
<Button color="primary" size="sm" className="system-statistic-button" onClick={this.onSubmit}>{gettext('Submit')}</Button>
</div>
</div>
</Fragment>

View File

@ -93,25 +93,6 @@ Content.propTypes = {
class Item extends Component {
constructor(props) {
super(props);
this.state = {
isOpIconShown: false,
};
}
handleMouseOver = () => {
this.setState({
isOpIconShown: true
});
};
handleMouseOut = () => {
this.setState({
isOpIconShown: false
});
};
getActionTextByEType = (etype) => {
if (etype.indexOf('add') != -1) {
return gettext('Add');
@ -129,9 +110,8 @@ class Item extends Component {
case 'user':
return <UserLink email={item.to_user_email} name={item.to_user_name} />;
case 'group':
return <Link to={`${siteRoot}sys/groups/${item.to_group_id}/libraries/`}>{item.to_group_name}</Link>;
case 'department':
return <Link to={`${siteRoot}sys/departments/${item.to_group_id}/`}>{item.to_group_name}</Link>;
return <Link to={`${siteRoot}sys/groups/${item.to_group_id}/libraries/`}>{item.to_group_name}</Link>;
case 'all':
return <Link to={`${siteRoot}org/`}>{gettext('All')}</Link>;
default:
@ -142,7 +122,7 @@ class Item extends Component {
render() {
let { item } = this.props;
return (
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
<tr>
<td><UserLink email={item.from_user_email} name={item.from_user_name} /></td>
<td>{this.getShareTo(item)}</td>
<td>{this.getActionTextByEType(item.etype)}</td>

View File

@ -334,7 +334,7 @@ class Item extends Component {
<td>{repo.id}</td>
<td>
{isGroupOwnedRepo ?
<Link to={`${siteRoot}sys/departments/${departmentID}/`}>{repo.owner_name}</Link> :
<Link to={`${siteRoot}sys/groups/${departmentID}/libraries/`}>{repo.owner_name}</Link> :
<UserLink email={repo.owner_email} name={repo.owner_name} />
}
</td>

View File

@ -125,7 +125,7 @@ class StatisticCommonTool extends React.Component {
value={endValue}
onChange={this.onChange.bind(this, 'endValue')}
/>
<Button color="primary" className="operation-item system-statistic-button" onClick={this.onSubmit}>{gettext('Submit')}</Button>
<Button color="primary" size="sm" className="system-statistic-button" onClick={this.onSubmit}>{gettext('Submit')}</Button>
</div>
</div>
</Fragment>

View File

@ -79,7 +79,6 @@ class Item extends Component {
constructor(props) {
super(props);
this.state = {
isOpIconShown: false,
highlight: false,
};
}
@ -87,7 +86,6 @@ class Item extends Component {
handleMouseEnter = () => {
if (!this.props.isItemFreezed) {
this.setState({
isOpIconShown: true,
highlight: true
});
}
@ -96,7 +94,6 @@ class Item extends Component {
handleMouseLeave = () => {
if (!this.props.isItemFreezed) {
this.setState({
isOpIconShown: false,
highlight: false
});
}
@ -129,9 +126,7 @@ class Item extends Component {
render() {
const { item } = this.props;
const url = item.parent_group_id == 0 ?
`${siteRoot}sys/groups/${item.id}/libraries/` :
`${siteRoot}sys/departments/${item.id}/`;
const url = `${siteRoot}sys/groups/${item.id}/libraries/`;
return (
<Fragment>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>

View File

@ -5,7 +5,7 @@ import { siteRoot, gettext } from '../../../utils/constants';
const propTypes = {
email: PropTypes.string,
userName: PropTypes.string.isRequired,
userName: PropTypes.string,
currentItem: PropTypes.string.isRequired
};

View File

@ -24,27 +24,25 @@ class Content extends Component {
return <p className="error text-center mt-4">{errorMsg}</p>;
} else {
const table = (
<Fragment>
<table className="table-hover">
<thead>
<tr>
<th width="5%"></th>
<th width="35%">{gettext('Name')}</th>
<th width="20%">{gettext('Share From')}</th>
<th width="20%">{gettext('Size')}</th>
<th width="20%">{gettext('Last Update')}</th>
</tr>
</thead>
<tbody>
{items.map((item, index) => {
return (<Item
key={index}
item={item}
/>);
})}
</tbody>
</table>
</Fragment>
<table className="table-hover">
<thead>
<tr>
<th width="5%"></th>
<th width="35%">{gettext('Name')}</th>
<th width="20%">{gettext('Share From')}</th>
<th width="20%">{gettext('Size')}</th>
<th width="20%">{gettext('Last Update')}</th>
</tr>
</thead>
<tbody>
{items.map((item, index) => {
return (<Item
key={index}
item={item}
/>);
})}
</tbody>
</table>
);
return items.length ? table : <EmptyTip text={gettext('No libraries')} />;
}
@ -82,7 +80,7 @@ class Item extends Component {
link = <UserLink email={item.owner_email} name={item.owner_name} />;
} else {
const groupID = item.owner_email.substring(0, index);
link = <Link to={`${siteRoot}sys/departments/${groupID}/`}>{item.owner_name}</Link>;
link = <Link to={`${siteRoot}sys/groups/${groupID}/libraries/`}>{item.owner_name}</Link>;
}
return link;
};
@ -92,15 +90,13 @@ class Item extends Component {
const iconUrl = Utils.getLibIconUrl(item);
const iconTitle = Utils.getLibIconTitle(item);
return (
<Fragment>
<tr>
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<td>{this.renderRepoName()}</td>
<td>{this.getOwnerLink()}</td>
<td>{Utils.bytesToSize(item.size)}</td>
<td>{dayjs(item.last_modify).fromNow()}</td>
</tr>
</Fragment>
<tr>
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<td>{this.renderRepoName()}</td>
<td>{this.getOwnerLink()}</td>
<td>{Utils.bytesToSize(item.size)}</td>
<td>{dayjs(item.last_modify).fromNow()}</td>
</tr>
);
}
}

View File

@ -197,6 +197,7 @@ def login(request, template_name='registration/login.html',
getattr(settings, 'ENABLE_KRB5_LOGIN', False) or \
getattr(settings, 'ENABLE_ADFS_LOGIN', False) or \
getattr(settings, 'ENABLE_OAUTH', False) or \
getattr(settings, 'ENABLE_CUSTOM_OAUTH', False) or \
getattr(settings, 'ENABLE_CAS', False) or \
getattr(settings, 'ENABLE_REMOTE_USER_AUTHENTICATION', False)
@ -296,7 +297,7 @@ def logout(request, next_page=None,
# Local logout for ouath user.
via_oauth = request.COOKIES.get('via_oauth', '')
oauth_logout_url = getattr(settings, 'OAUTH_LOGOUT_URL', '')
if getattr(settings, 'ENABLE_OAUTH', False) and via_oauth and oauth_logout_url:
if (getattr(settings, 'ENABLE_OAUTH', False) or getattr(settings, 'ENABLE_CUSTOM_OAUTH', False)) and via_oauth and oauth_logout_url:
response = HttpResponseRedirect(oauth_logout_url)
response.delete_cookie('via_oauth')
response.delete_cookie('seahub_auth')

View File

@ -1,9 +1,20 @@
# Copyright (c) 2012-2016 Seafile Ltd.
from django.urls import path
from seahub.oauth.views import oauth_login, oauth_callback
from seahub.oauth.views import oauth_login, oauth_callback, \
custom_oauth_login_view, custom_oauth_callback_view
import seahub.settings as settings
ENABLE_CUSTOM_OAUTH = getattr(settings, 'ENABLE_CUSTOM_OAUTH', False)
urlpatterns = [
path('login/', oauth_login, name='oauth_login'),
path('callback/', oauth_callback, name='oauth_callback'),
]
if ENABLE_CUSTOM_OAUTH:
urlpatterns = [
path('login/', custom_oauth_login_view, name='oauth_login'),
path('callback/', custom_oauth_callback_view, name='oauth_callback'),
]

View File

@ -58,6 +58,17 @@ if ENABLE_OAUTH:
OAUTH_PROVIDER = getattr(settings, 'OAUTH_PROVIDER_DOMAIN', '')
OAUTH_ATTRIBUTE_MAP = getattr(settings, 'OAUTH_ATTRIBUTE_MAP', {})
ENABLE_CUSTOM_OAUTH = getattr(settings, 'ENABLE_CUSTOM_OAUTH', False)
if ENABLE_CUSTOM_OAUTH:
try:
current_path = os.path.dirname(os.path.abspath(__file__))
conf_dir = os.path.join(current_path, '../../../../conf')
sys.path.append(conf_dir)
from seahub_custom_functions import custom_oauth_login, custom_oauth_callback
ENABLE_CUSTOM_OAUTH = True
except ImportError:
ENABLE_CUSTOM_OAUTH = False
def oauth_check(func):
""" Decorator for check if OAuth valid.
@ -260,3 +271,22 @@ def oauth_callback(request):
response.set_cookie('seahub_auth', email + '@' + api_token.key)
response.set_cookie('via_oauth', 'true')
return response
def custom_oauth_login_view(request):
if not ENABLE_CUSTOM_OAUTH:
return render_error(request, _('Feature is not enabled.'))
if request.user.is_authenticated:
# already authenticated
redirect_url = request.GET.get(auth.REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL)
return HttpResponseRedirect(redirect_url)
return custom_oauth_login(request)
def custom_oauth_callback_view(request):
if not ENABLE_CUSTOM_OAUTH:
return render_error(request, _('Feature is not enabled.'))
return custom_oauth_callback(request)

View File

@ -319,6 +319,8 @@ DISABLE_ADFS_USER_PWD_LOGIN = False
ENABLE_OAUTH = False
ENABLE_WATERMARK = False
ENABLE_CUSTOM_OAUTH = False
ENABLE_SHOW_CONTACT_EMAIL_WHEN_SEARCH_USER = False
ENABLE_SHOW_LOGIN_ID_WHEN_SEARCH_USER = False
@ -1217,7 +1219,7 @@ if ENABLE_REMOTE_USER_AUTHENTICATION:
MIDDLEWARE.append('seahub.auth.middleware.SeafileRemoteUserMiddleware')
AUTHENTICATION_BACKENDS += ('seahub.auth.backends.SeafileRemoteUserBackend',)
if ENABLE_OAUTH or ENABLE_WORK_WEIXIN or ENABLE_WEIXIN or ENABLE_DINGTALK:
if ENABLE_OAUTH or ENABLE_CUSTOM_OAUTH or ENABLE_WORK_WEIXIN or ENABLE_WEIXIN or ENABLE_DINGTALK:
AUTHENTICATION_BACKENDS += ('seahub.oauth.backends.OauthRemoteUserBackend',)
if ENABLE_CAS:

View File

@ -57,6 +57,9 @@ def sso(request):
if getattr(settings, 'ENABLE_OAUTH', False):
return HttpResponseRedirect(reverse('oauth_login') + next_param)
if getattr(settings, 'ENABLE_CUSTOM_OAUTH', False):
return HttpResponseRedirect(reverse('oauth_login') + next_param)
if getattr(settings, 'ENABLE_CAS', False):
return HttpResponseRedirect(reverse('cas_ng_login') + next_param)