mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 07:27:04 +00:00
receive share from other service
This commit is contained in:
@@ -20,6 +20,7 @@ import ShareAdminShareLinks from './pages/share-admin/share-links';
|
||||
import ShareAdminUploadLinks from './pages/share-admin/upload-links';
|
||||
import SharedLibraries from './pages/shared-libs/shared-libs';
|
||||
import ShareWithOCM from './pages/share-with-ocm/shared-with-ocm';
|
||||
import OCMViaWebdav from './pages/ocm-via-webdav/ocm-via-webdav';
|
||||
import OCMRepoDir from './pages/share-with-ocm/remote-dir-view';
|
||||
import MyLibraries from './pages/my-libs/my-libs';
|
||||
import MyLibDeleted from './pages/my-libs/my-libs-deleted';
|
||||
@@ -41,6 +42,7 @@ const StarredWrapper = MainContentWrapper(Starred);
|
||||
const LinkedDevicesWrapper = MainContentWrapper(LinkedDevices);
|
||||
const SharedLibrariesWrapper = MainContentWrapper(SharedLibraries);
|
||||
const SharedWithOCMWrapper = MainContentWrapper(ShareWithOCM);
|
||||
const OCMViaWebdavWrapper = MainContentWrapper(OCMViaWebdav);
|
||||
const ShareAdminLibrariesWrapper = MainContentWrapper(ShareAdminLibraries);
|
||||
const ShareAdminFoldersWrapper = MainContentWrapper(ShareAdminFolders);
|
||||
const ShareAdminShareLinksWrapper = MainContentWrapper(ShareAdminShareLinks);
|
||||
@@ -261,6 +263,7 @@ class App extends Component {
|
||||
<ShareAdminUploadLinksWrapper path={siteRoot + 'share-admin-upload-links'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
|
||||
<SharedLibrariesWrapper path={siteRoot + 'shared-libs'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
|
||||
<SharedWithOCMWrapper path={siteRoot + 'shared-with-ocm'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
|
||||
<OCMViaWebdavWrapper path={siteRoot + 'ocm-via-webdav'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
|
||||
<MyLibraries path={siteRoot + 'my-libs'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick} />
|
||||
<MyLibDeleted path={siteRoot + 'my-libs/deleted/'} onSearchedClick={this.onSearchedClick} />
|
||||
<LibContentView path={siteRoot + 'library/:repoID/*'} pathPrefix={this.state.pathPrefix} onMenuClick={this.onShowSidePanel} onTabNavClick={this.tabItemClick}/>
|
||||
|
@@ -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, canPublishRepo, canAddRepo, canGenerateShareLink, canGenerateUploadLink, canInvitePeople, dtableWebServer, enableOCM } from '../utils/constants';
|
||||
import { gettext, siteRoot, canPublishRepo, canAddRepo, canGenerateShareLink, canGenerateUploadLink, canInvitePeople, dtableWebServer, enableOCM, enableOCMViaWebdav } from '../utils/constants';
|
||||
import { seafileAPI } from '../utils/seafile-api';
|
||||
import { Utils } from '../utils/utils';
|
||||
import toaster from './toast';
|
||||
@@ -224,6 +224,14 @@ class MainSideNav extends React.Component {
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{enableOCMViaWebdav &&
|
||||
<li className="nav-item">
|
||||
<Link to={siteRoot + 'ocm-via-webdav/'} className={`nav-link ellipsis ${this.getActiveClass('ocm-via-webdav')}`} title={gettext('Shared from other servers')} onClick={(e) => this.tabItemClick(e, 'ocm-via-webdav')}>
|
||||
<span className="sf3-font-share-from-other-servers sf3-font" aria-hidden="true"></span>
|
||||
<span className="nav-text">{gettext('Shared from other servers')}</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
|
||||
|
184
frontend/src/pages/ocm-via-webdav/ocm-via-webdav.js
Normal file
184
frontend/src/pages/ocm-via-webdav/ocm-via-webdav.js
Normal file
@@ -0,0 +1,184 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import { Link } from '@reach/router';
|
||||
import { gettext, siteRoot } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import Loading from '../../components/loading';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
render() {
|
||||
const { loading, errorMsg, items } = this.props;
|
||||
|
||||
const emptyTip = (
|
||||
<EmptyTip>
|
||||
<h2>{gettext('No libraries have been shared with you')}</h2>
|
||||
<p>{gettext('No libraries have been shared with you from other servers.')}</p>
|
||||
</EmptyTip>
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
} else if (errorMsg) {
|
||||
return <p className="error text-center">{errorMsg}</p>;
|
||||
} else {
|
||||
const table = (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%"></th>
|
||||
<th width="30%">{gettext('Name')}</th>
|
||||
<th width="35%">{gettext('Shared by')}</th>
|
||||
<th width="20%">{gettext('Time')}</th>
|
||||
<th width="5%">{/* operations */}</th>
|
||||
<th width="5%">{/* operations */}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item, index) => {
|
||||
return <Item
|
||||
key={index}
|
||||
item={item}
|
||||
leaveShare={this.props.leaveShare}
|
||||
/>;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
return items.length ? table : emptyTip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Content.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
errorMsg: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpIconShown: false
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseOver = () => {
|
||||
this.setState({
|
||||
isOpIconShown: true
|
||||
});
|
||||
}
|
||||
|
||||
handleMouseOut = () => {
|
||||
this.setState({
|
||||
isOpIconShown: false
|
||||
});
|
||||
}
|
||||
|
||||
downloadFile = () => {
|
||||
let downloadUrl = siteRoot + 'ocm-via-webdav/download-received-file/?share_id=' + this.props.item.id;
|
||||
window.location.href = downloadUrl;
|
||||
}
|
||||
|
||||
leaveShare = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.leaveShare(this.props.item);
|
||||
}
|
||||
|
||||
render() {
|
||||
const item = this.props.item;
|
||||
const { isOpIconShown } = this.state;
|
||||
|
||||
item.icon_url = Utils.getFileIconUrl(item.name);
|
||||
return (
|
||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||
<td><img src={item.icon_url} width="24" /></td>
|
||||
<td>
|
||||
{item.name}
|
||||
</td>
|
||||
<td>{item.shared_by}</td>
|
||||
<td title={moment(item.last_modified).format('llll')}>{moment(item.ctime).fromNow()}</td>
|
||||
<td>
|
||||
<a href="#" className={`action-icon sf2-icon-download ${isOpIconShown ? '' : 'invisible'}`} title={gettext('Download')} onClick={this.downloadFile}></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" className={`action-icon sf2-icon-x3 ${isOpIconShown ? '' : 'invisible'}`} title={gettext('Leave Share')} onClick={this.leaveShare}></a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Item.propTypes = {
|
||||
item: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
class OCMViaWebdav extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
errorMsg: '',
|
||||
items: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const url = seafileAPI.server + '/ocm-via-webdav/received-shares/';
|
||||
seafileAPI.req.get(url).then((res) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: res.data.received_share_list
|
||||
});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
leaveShare = (item) => {
|
||||
const { id, name } = item;
|
||||
const url = seafileAPI.server + '/ocm-via-webdav/received-shares/' + id + '/';
|
||||
seafileAPI.req.delete(url).then((res) => {
|
||||
let items = this.state.items.filter(item => {
|
||||
return item.id != id;
|
||||
});
|
||||
this.setState({items: items});
|
||||
toaster.success(gettext('Successfully unshared {name}').replace('{name}', name));
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="main-panel-center">
|
||||
<div className="cur-view-container">
|
||||
<div className="cur-view-path">
|
||||
<h3 className="sf-heading m-0">{gettext('Shared from other servers')}</h3>
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
<Content
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={this.state.items}
|
||||
leaveShare={this.leaveShare}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OCMViaWebdav;
|
@@ -74,6 +74,7 @@ export const maxUploadFileSize = window.app.pageOptions.maxUploadFileSize;
|
||||
export const maxNumberOfFilesForFileupload = window.app.pageOptions.maxNumberOfFilesForFileupload;
|
||||
export const enableOCM = window.app.pageOptions.enableOCM;
|
||||
export const ocmRemoteServers = window.app.pageOptions.ocmRemoteServers;
|
||||
export const enableOCMViaWebdav = window.app.pageOptions.enableOCMViaWebdav;
|
||||
|
||||
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
|
||||
export const curNoteID = window.app.pageOptions.curNoteID;
|
||||
|
0
seahub/ocm_via_webdav/__init__.py
Normal file
0
seahub/ocm_via_webdav/__init__.py
Normal file
38
seahub/ocm_via_webdav/migrations/0001_initial.py
Normal file
38
seahub/ocm_via_webdav/migrations/0001_initial.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 2.2.14 on 2021-09-16 16:55
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ShareReceived',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('description', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('name', models.CharField(db_index=True, max_length=255)),
|
||||
('owner', models.CharField(db_index=True, max_length=255)),
|
||||
('owner_display_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('protocol_name', models.CharField(db_index=True, max_length=255)),
|
||||
('shared_secret', models.CharField(db_index=True, max_length=255)),
|
||||
('permissions', models.CharField(db_index=True, max_length=255)),
|
||||
('provider_id', models.CharField(db_index=True, max_length=255)),
|
||||
('resource_type', models.CharField(db_index=True, max_length=255)),
|
||||
('share_type', models.CharField(db_index=True, max_length=255)),
|
||||
('share_with', models.CharField(db_index=True, max_length=255)),
|
||||
('shared_by', models.CharField(db_index=True, max_length=255)),
|
||||
('shared_by_display_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('ctime', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'ocm_via_webdav_share_received',
|
||||
},
|
||||
),
|
||||
]
|
17
seahub/ocm_via_webdav/migrations/0002_auto_20210926_1503.py
Normal file
17
seahub/ocm_via_webdav/migrations/0002_auto_20210926_1503.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 2.2.14 on 2021-09-26 15:03
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ocm_via_webdav', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='ShareReceived',
|
||||
new_name='ReceivedShares',
|
||||
),
|
||||
]
|
32
seahub/ocm_via_webdav/migrations/0003_auto_20210926_1505.py
Normal file
32
seahub/ocm_via_webdav/migrations/0003_auto_20210926_1505.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 2.2.14 on 2021-09-26 15:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ocm_via_webdav', '0002_auto_20210926_1503'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='receivedshares',
|
||||
name='name',
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='receivedshares',
|
||||
name='permissions',
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='receivedshares',
|
||||
name='protocol_name',
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterModelTable(
|
||||
name='receivedshares',
|
||||
table='ocm_via_webdav_received_shares',
|
||||
),
|
||||
]
|
0
seahub/ocm_via_webdav/migrations/__init__.py
Normal file
0
seahub/ocm_via_webdav/migrations/__init__.py
Normal file
25
seahub/ocm_via_webdav/models.py
Normal file
25
seahub/ocm_via_webdav/models.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class ReceivedShares(models.Model):
|
||||
|
||||
# https://cs3org.github.io/OCM-API/docs.html
|
||||
|
||||
class Meta:
|
||||
db_table = 'ocm_via_webdav_received_shares'
|
||||
|
||||
description = models.CharField(max_length=255, blank=True, null=True)
|
||||
name = models.CharField(max_length=255)
|
||||
owner = models.CharField(max_length=255, db_index=True)
|
||||
owner_display_name = models.CharField(max_length=255, blank=True, null=True)
|
||||
protocol_name = models.CharField(max_length=255)
|
||||
shared_secret = models.CharField(max_length=255, db_index=True)
|
||||
permissions = models.CharField(max_length=255)
|
||||
provider_id = models.CharField(max_length=255, db_index=True)
|
||||
resource_type = models.CharField(max_length=255, db_index=True)
|
||||
share_type = models.CharField(max_length=255, db_index=True)
|
||||
share_with = models.CharField(max_length=255, db_index=True)
|
||||
shared_by = models.CharField(max_length=255, db_index=True)
|
||||
shared_by_display_name = models.CharField(max_length=255, blank=True, null=True)
|
||||
ctime = models.DateTimeField(default=timezone.now)
|
373
seahub/ocm_via_webdav/ocm_api.py
Normal file
373
seahub/ocm_via_webdav/ocm_api.py
Normal file
@@ -0,0 +1,373 @@
|
||||
import base64
|
||||
import logging
|
||||
import requests
|
||||
from constance import config
|
||||
|
||||
from django.http import HttpResponse
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.utils import api_error
|
||||
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
|
||||
from seahub.ocm_via_webdav.settings import ENABLE_OCM_VIA_WEBDAV, OCM_VIA_WEBDAV_ENDPOINT
|
||||
from seahub.ocm_via_webdav.models import ReceivedShares
|
||||
|
||||
from seahub.ocm.settings import ENABLE_OCM, OCM_SEAFILE_PROTOCOL, \
|
||||
OCM_RESOURCE_TYPE_LIBRARY, OCM_API_VERSION, OCM_SHARE_TYPES, \
|
||||
OCM_ENDPOINT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OCMProviderView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
Return ocm protocol info to remote server
|
||||
"""
|
||||
|
||||
result = {}
|
||||
|
||||
if ENABLE_OCM:
|
||||
|
||||
result = {
|
||||
'enabled': True,
|
||||
'apiVersion': OCM_API_VERSION,
|
||||
'endPoint': config.SERVICE_URL + '/' + OCM_ENDPOINT,
|
||||
'resourceTypes': {
|
||||
'name': OCM_RESOURCE_TYPE_LIBRARY,
|
||||
'shareTypes': OCM_SHARE_TYPES,
|
||||
'protocols': {
|
||||
OCM_SEAFILE_PROTOCOL: OCM_SEAFILE_PROTOCOL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ENABLE_OCM_VIA_WEBDAV:
|
||||
|
||||
result = {
|
||||
'apiVersion': '1.0-proposal1',
|
||||
'enabled': True,
|
||||
'endPoint': config.SERVICE_URL + '/' + OCM_VIA_WEBDAV_ENDPOINT,
|
||||
'resourceTypes': {
|
||||
'name': 'file',
|
||||
'protocols': {'webdav': 'TODO'},
|
||||
'shareTypes': ['user'],
|
||||
}
|
||||
}
|
||||
return Response(result)
|
||||
|
||||
|
||||
class SharesView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Receive share from other service
|
||||
"""
|
||||
|
||||
if not ENABLE_OCM_VIA_WEBDAV:
|
||||
error_msg = 'OCM via webdav feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
# {'description': '',
|
||||
# 'name': 'file-3-in-nextcloud-folder.md',
|
||||
# 'owner': 'lian@https://nextcloud.seafile.top/',
|
||||
# 'ownerDisplayName': 'lian',
|
||||
# 'protocol': {'name': 'webdav',
|
||||
# 'options': {'permissions': '{http://open-cloud-mesh.org/ns}share-permissions',
|
||||
# 'sharedSecret': 'HdjKpI4o6lamWwN'}},
|
||||
# 'providerId': 9,
|
||||
# 'resourceType': 'file',
|
||||
# 'shareType': 'user',
|
||||
# 'shareWith': 'lian@lian.com@https://demo.seafile.top', # or 'lian@https://demo.seafile.top',
|
||||
# 'sharedBy': 'lian@https://nextcloud.seafile.top/',
|
||||
# 'sharedByDisplayName': 'lian'}
|
||||
|
||||
protocol_dict = request.data.get('protocol', {})
|
||||
protocol_name = protocol_dict.get('name')
|
||||
shared_secret = protocol_dict.get('options').get('sharedSecret')
|
||||
permissions = protocol_dict.get('options').get('permissions')
|
||||
|
||||
owner = request.data.get('owner')
|
||||
owner_display_name = request.data.get('owner_display_name')
|
||||
|
||||
name = request.data.get('name')
|
||||
description = request.data.get('description')
|
||||
provider_id = request.data.get('providerId')
|
||||
resource_type = request.data.get('resourceType')
|
||||
share_type = request.data.get('shareType')
|
||||
share_with = request.data.get('shareWith').split('http')[0].rstrip('@')
|
||||
shared_by = request.data.get('sharedBy')
|
||||
shared_by_display_name = request.data.get('sharedByDisplayName')
|
||||
|
||||
share = ReceivedShares(description=description,
|
||||
name=name,
|
||||
owner=owner,
|
||||
owner_display_name=owner_display_name,
|
||||
protocol_name=protocol_name,
|
||||
shared_secret=shared_secret,
|
||||
permissions=permissions,
|
||||
provider_id=provider_id,
|
||||
resource_type=resource_type,
|
||||
share_type=share_type,
|
||||
share_with=share_with,
|
||||
shared_by=shared_by,
|
||||
shared_by_display_name=shared_by_display_name)
|
||||
share.save()
|
||||
|
||||
result = {
|
||||
"recipientDisplayName": email2nickname(share_with)
|
||||
}
|
||||
return Response(result, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class ReceivedSharesView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
Get items shared from other service.
|
||||
"""
|
||||
|
||||
if not ENABLE_OCM_VIA_WEBDAV:
|
||||
error_msg = 'OCM via webdav feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
username = request.user.username
|
||||
|
||||
info_list = []
|
||||
for share in ReceivedShares.objects.filter(share_with=username):
|
||||
|
||||
info = {}
|
||||
info['id'] = share.id
|
||||
info['name'] = share.name
|
||||
info['ctime'] = share.ctime
|
||||
info['shared_by'] = share.shared_by
|
||||
|
||||
info_list.append(info)
|
||||
|
||||
result = {
|
||||
'received_share_list': info_list
|
||||
}
|
||||
return Response(result)
|
||||
|
||||
|
||||
class ReceivedShareView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def delete(self, request, share_id):
|
||||
"""
|
||||
Delete item shared from other service.
|
||||
"""
|
||||
|
||||
if not ENABLE_OCM_VIA_WEBDAV:
|
||||
error_msg = 'OCM via webdav feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
try:
|
||||
share = ReceivedShares.objects.get(id=share_id)
|
||||
except ReceivedShares.DoesNotExist:
|
||||
error_msg = "OCM share {} not found.".format(share_id)
|
||||
return api_error(404, error_msg)
|
||||
|
||||
username = request.user.username
|
||||
if share.share_with != username:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(403, error_msg)
|
||||
|
||||
# get remote server endpoint
|
||||
shared_by = share.shared_by
|
||||
remote_domain = shared_by.split('@')[-1]
|
||||
remote_domain = remote_domain.rstrip('/')
|
||||
|
||||
ocm_provider_url = remote_domain + '/ocm-provider/'
|
||||
resp = requests.get(ocm_provider_url)
|
||||
end_point = resp.json().get('endPoint')
|
||||
|
||||
if not end_point:
|
||||
logger.error('Can not get endPoint from {}'.format(ocm_provider_url))
|
||||
logger.error(resp.content)
|
||||
|
||||
end_point = end_point.rstrip('/')
|
||||
|
||||
# send SHARE_DECLINED notification
|
||||
data = {
|
||||
"notification": {
|
||||
"message": "Recipient declined the share",
|
||||
"sharedSecret": ""
|
||||
},
|
||||
"notificationType": "SHARE_DECLINED",
|
||||
"providerId": "",
|
||||
"resourceType": ""
|
||||
}
|
||||
|
||||
data['notification']['sharedSecret'] = share.shared_secret
|
||||
data['providerId'] = share.provider_id
|
||||
data['resourceType'] = share.resource_type
|
||||
|
||||
notifications_url = end_point + '/notifications'
|
||||
resp = requests.post(notifications_url, json=data)
|
||||
if resp.status_code != 201:
|
||||
logger.error('Error occurred when send notification to {}'.format(notifications_url))
|
||||
logger.error(resp.content)
|
||||
|
||||
share.delete()
|
||||
|
||||
result = {
|
||||
'success': True
|
||||
}
|
||||
return Response(result)
|
||||
|
||||
|
||||
class DownloadReceivedFileView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
Download received file.
|
||||
"""
|
||||
|
||||
if not ENABLE_OCM_VIA_WEBDAV:
|
||||
error_msg = 'OCM via webdav feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
share_id = request.GET.get('share_id')
|
||||
if not share_id:
|
||||
error_msg = 'share_id invalid.'
|
||||
return api_error(400, error_msg)
|
||||
|
||||
try:
|
||||
share_id = int(share_id)
|
||||
except ValueError as e:
|
||||
logger.error(e)
|
||||
error_msg = 'share_id invalid.'
|
||||
return api_error(400, error_msg)
|
||||
|
||||
try:
|
||||
share = ReceivedShares.objects.get(id=share_id)
|
||||
except ReceivedShares.DoesNotExist:
|
||||
error_msg = "OCM share {} not found.".format(share_id)
|
||||
return api_error(404, error_msg)
|
||||
|
||||
# get remote server endpoint
|
||||
shared_by = share.shared_by
|
||||
remote_domain = shared_by.split('@')[-1]
|
||||
remote_domain = remote_domain.rstrip('/')
|
||||
|
||||
ocm_provider_url = remote_domain + '/ocm-provider/'
|
||||
resp = requests.get(ocm_provider_url)
|
||||
|
||||
# {
|
||||
# 'apiVersion': '1.0-proposal1',
|
||||
# 'enabled': True,
|
||||
# 'endPoint': 'https://nextcloud.seafile.top/index.php/ocm',
|
||||
# 'resourceTypes': [
|
||||
# {
|
||||
# 'name': 'file',
|
||||
# 'protocols': {'webdav': '/public.php/webdav/'},
|
||||
# 'shareTypes': ['user', 'group']
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
|
||||
resource_types = resp.json().get('resourceTypes', [])
|
||||
if not resource_types:
|
||||
logger.error('Can not get resource_types from {}'.format(ocm_provider_url))
|
||||
logger.error(resp.content)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
protocols = resource_types[0].get('protocols')
|
||||
if not protocols:
|
||||
logger.error('Can not get protocols from {}'.format(ocm_provider_url))
|
||||
logger.error(resp.content)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
webdav_url = protocols.get('webdav')
|
||||
if not webdav_url:
|
||||
logger.error('Can not get webdav url from {}'.format(ocm_provider_url))
|
||||
logger.error(resp.content)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
# download file via webdav
|
||||
full_webdav_url = remote_domain + webdav_url
|
||||
|
||||
def format_string(string):
|
||||
return string + (4 - len(string) % 4) * ':'
|
||||
|
||||
shared_secret = share.shared_secret
|
||||
token = base64.b64encode('{}'.format(format_string(shared_secret)).encode('utf-8'))
|
||||
headers = {"Authorization": "Basic {}".format(token.decode('utf-8'))}
|
||||
download_file_resp = requests.get(full_webdav_url, headers=headers)
|
||||
|
||||
response = HttpResponse(download_file_resp.content, content_type="application/octet-stream")
|
||||
response['Content-Disposition'] = 'attachment; filename={}'.format(share.name)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class NotificationsView(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Receive notification from remote server.
|
||||
"""
|
||||
|
||||
if not ENABLE_OCM_VIA_WEBDAV:
|
||||
error_msg = 'OCM via webdav feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
# {'notification': {'messgage': 'file is no longer shared with you',
|
||||
# 'sharedSecret': 'QoVQuBhqphvVYvz'},
|
||||
# 'notificationType': 'SHARE_UNSHARED',
|
||||
# 'providerId': '13',
|
||||
# 'resourceType': 'file'}
|
||||
|
||||
notification_type = request.data.get('notificationType')
|
||||
notification_dict = request.data.get('notification')
|
||||
shared_secret = notification_dict.get('sharedSecret')
|
||||
provider_id = notification_dict.get('providerId')
|
||||
|
||||
error_result_not_found = {
|
||||
"message": "RESOURCE_NOT_FOUND",
|
||||
"validationErrors": [
|
||||
{
|
||||
"name": "",
|
||||
"message": "NOT_FOUND"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if notification_type == 'SHARE_UNSHARED':
|
||||
|
||||
try:
|
||||
share = ReceivedShares.objects.get(shared_secret=shared_secret)
|
||||
except ReceivedShares.DoesNotExist:
|
||||
error_msg = "OCM share with secret {} not found.".format(shared_secret)
|
||||
error_result_not_found['validationErrors']['name'] = 'sharedSecret'
|
||||
return Response(error_result_not_found, status=400)
|
||||
|
||||
if share.provider_id != provider_id:
|
||||
error_msg = "OCM share with provider id {} not found.".format(provider_id)
|
||||
error_result_not_found['validationErrors']['name'] = 'providerID'
|
||||
return Response(error_result_not_found, status=400)
|
||||
|
||||
share.delete()
|
||||
|
||||
return Response({}, status=status.HTTP_201_CREATED)
|
4
seahub/ocm_via_webdav/settings.py
Normal file
4
seahub/ocm_via_webdav/settings.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from django.conf import settings
|
||||
|
||||
ENABLE_OCM_VIA_WEBDAV = getattr(settings, 'ENABLE_OCM_VIA_WEBDAV', False)
|
||||
OCM_VIA_WEBDAV_ENDPOINT = getattr(settings, 'OCM_VIA_WEBDAV_ENDPOINT', 'ocm-via-webdav')
|
17
seahub/ocm_via_webdav/urls.py
Normal file
17
seahub/ocm_via_webdav/urls.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.urls import path
|
||||
|
||||
from seahub.views import react_fake_view
|
||||
from seahub.ocm_via_webdav.ocm_api import SharesView, ReceivedSharesView, \
|
||||
ReceivedShareView, DownloadReceivedFileView, NotificationsView
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path(r'', react_fake_view, name="ocm_via_webdav"),
|
||||
|
||||
path('shares', SharesView.as_view(), name='ocm-via-webdav-shares'),
|
||||
path('notifications', NotificationsView.as_view(), name='ocm-via-webdav-notifications'),
|
||||
|
||||
path('received-shares/', ReceivedSharesView.as_view(), name='ocm-via-webdav-received-shares'),
|
||||
path('received-shares/<int:share_id>/', ReceivedShareView.as_view(), name='ocm-via-webdav-received-share'),
|
||||
path('download-received-file/', DownloadReceivedFileView.as_view(), name='ocm-via-webdav-download-received-file'),
|
||||
]
|
@@ -263,7 +263,7 @@ INSTALLED_APPS = [
|
||||
'seahub.abuse_reports',
|
||||
'seahub.repo_auto_delete',
|
||||
'seahub.ocm',
|
||||
|
||||
'seahub.ocm_via_webdav',
|
||||
'seahub.search',
|
||||
'seahub.sysadmin_extra',
|
||||
'seahub.organizations',
|
||||
|
@@ -106,6 +106,7 @@
|
||||
thumbnailSizeForOriginal: {{ thumbnail_size_for_original }},
|
||||
repoPasswordMinLength: {{repo_password_min_length}},
|
||||
canAddPublicRepo: {% if can_add_public_repo %} true {% else %} false {% endif %},
|
||||
enableOCMViaWebdav: {% if enable_ocm_via_webdav %} true {% else %} false {% endif %},
|
||||
enableOCM: {% if enable_ocm %} true {% else %} false {% endif %},
|
||||
ocmRemoteServers: (function () {
|
||||
var servers = [];
|
||||
|
@@ -106,6 +106,8 @@ from seahub.api2.endpoints.ocm_repos import OCMReposDirView, OCMReposDownloadLin
|
||||
OCMReposUploadLinkView
|
||||
from seahub.api2.endpoints.custom_share_permissions import CustomSharePermissionsView, CustomSharePermissionView
|
||||
|
||||
from seahub.ocm_via_webdav.ocm_api import OCMProviderView
|
||||
|
||||
from seahub.api2.endpoints.repo_share_links import RepoShareLinks, RepoShareLink
|
||||
from seahub.api2.endpoints.repo_upload_links import RepoUploadLinks, RepoUploadLink
|
||||
|
||||
@@ -194,6 +196,7 @@ urlpatterns = [
|
||||
url(r'^shib-login/', shib_login, name="shib_login"),
|
||||
url(r'^oauth/', include('seahub.oauth.urls')),
|
||||
url(r'^thirdparty-editor/', include('seahub.thirdparty_editor.urls')),
|
||||
url(r'^ocm-via-webdav/', include('seahub.ocm_via_webdav.urls')),
|
||||
|
||||
url(r'^$', react_fake_view, name='libraries'),
|
||||
url(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', content_type='text/plain')),
|
||||
@@ -474,7 +477,7 @@ urlpatterns = [
|
||||
|
||||
## user::ocm
|
||||
# ocm inter-server api, interact with other server
|
||||
url(r'ocm-provider/$', OCMProtocolView.as_view(), name='api-v2.1-ocm-protocol'),
|
||||
url(r'ocm-provider/$', OCMProviderView.as_view(), name='api-v2.1-ocm-protocol'),
|
||||
url(r'' + OCM_ENDPOINT + 'shares/$', OCMSharesView.as_view(), name='api-v2.1-ocm-shares'),
|
||||
url(r'' + OCM_ENDPOINT + 'notifications/$', OCMNotificationsView.as_view(), name='api-v2.1-ocm-notifications'),
|
||||
|
||||
|
@@ -61,6 +61,7 @@ from seahub.settings import AVATAR_FILE_STORAGE, \
|
||||
from seahub.wopi.settings import ENABLE_OFFICE_WEB_APP
|
||||
from seahub.onlyoffice.settings import ONLYOFFICE_DESKTOP_EDITORS_PORTAL_LOGIN
|
||||
from seahub.ocm.settings import ENABLE_OCM, OCM_REMOTE_SERVERS
|
||||
from seahub.ocm_via_webdav.settings import ENABLE_OCM_VIA_WEBDAV
|
||||
from seahub.constants import HASH_URLS, PERMISSION_READ
|
||||
from seahub.group.settings import GROUP_IMPORT_MEMBERS_EXTRA_MSG
|
||||
|
||||
@@ -1201,6 +1202,7 @@ def react_fake_view(request, **kwargs):
|
||||
'additional_share_dialog_note': ADDITIONAL_SHARE_DIALOG_NOTE,
|
||||
'additional_app_bottom_links': ADDITIONAL_APP_BOTTOM_LINKS,
|
||||
'additional_about_dialog_links': ADDITIONAL_ABOUT_DIALOG_LINKS,
|
||||
'enable_ocm_via_webdav': ENABLE_OCM_VIA_WEBDAV,
|
||||
'enable_ocm': ENABLE_OCM,
|
||||
'ocm_remote_servers': OCM_REMOTE_SERVERS,
|
||||
'enable_share_to_department': settings.ENABLE_SHARE_TO_DEPARTMENT,
|
||||
|
Reference in New Issue
Block a user