mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-04 16:31:13 +00:00
show tag info on folder share link page
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
import MD5 from 'MD5';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Button, Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Dropdown, DropdownToggle, DropdownItem, UncontrolledTooltip} from 'reactstrap';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Account from './components/common/account';
|
import Account from './components/common/account';
|
||||||
import { isPro, useGoFileserver, fileServerRoot, gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle, thumbnailSizeForOriginal } from './utils/constants';
|
import { isPro, useGoFileserver, fileServerRoot, gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle, thumbnailSizeForOriginal } from './utils/constants';
|
||||||
@@ -15,8 +16,13 @@ import FileUploader from './components/shared-link-file-uploader/file-uploader';
|
|||||||
import SaveSharedDirDialog from './components/dialog/save-shared-dir-dialog';
|
import SaveSharedDirDialog from './components/dialog/save-shared-dir-dialog';
|
||||||
import CopyMoveDirentProgressDialog from './components/dialog/copy-move-dirent-progress-dialog';
|
import CopyMoveDirentProgressDialog from './components/dialog/copy-move-dirent-progress-dialog';
|
||||||
|
|
||||||
|
import RepoTag from './models/repo-tag';
|
||||||
|
import FileTag from './models/file-tag';
|
||||||
|
|
||||||
|
|
||||||
import './css/shared-dir-view.css';
|
import './css/shared-dir-view.css';
|
||||||
import './css/grid-view.css';
|
import './css/grid-view.css';
|
||||||
|
import './css/repo-info-bar.css';
|
||||||
|
|
||||||
moment.locale(window.app.config.lang);
|
moment.locale(window.app.config.lang);
|
||||||
|
|
||||||
@@ -49,6 +55,9 @@ class SharedDirView extends React.Component {
|
|||||||
isZipDialogOpen: false,
|
isZipDialogOpen: false,
|
||||||
zipFolderPath: '',
|
zipFolderPath: '',
|
||||||
|
|
||||||
|
usedRepoTags: [],
|
||||||
|
isRepoInfoBarShow: false,
|
||||||
|
|
||||||
isSaveSharedDirDialogShow: false,
|
isSaveSharedDirDialogShow: false,
|
||||||
itemsForSave: [],
|
itemsForSave: [],
|
||||||
|
|
||||||
@@ -88,6 +97,8 @@ class SharedDirView extends React.Component {
|
|||||||
errorMsg: errorMsg
|
errorMsg: errorMsg
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.getShareLinkRepoTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
sortItems = (sortBy, sortOrder) => {
|
sortItems = (sortBy, sortOrder) => {
|
||||||
@@ -400,6 +411,25 @@ class SharedDirView extends React.Component {
|
|||||||
seafileAPI.shareLinksUploadDone(token, Utils.joinPath(dirPath, name));
|
seafileAPI.shareLinksUploadDone(token, Utils.joinPath(dirPath, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getShareLinkRepoTags = () => {
|
||||||
|
seafileAPI.getShareLinkRepoTags(token).then(res => {
|
||||||
|
let usedRepoTags = [];
|
||||||
|
res.data.repo_tags.forEach(item => {
|
||||||
|
let usedRepoTag = new RepoTag(item);
|
||||||
|
if (usedRepoTag.fileCount > 0) {
|
||||||
|
usedRepoTags.push(usedRepoTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({usedRepoTags: usedRepoTags});
|
||||||
|
if (usedRepoTags.length != 0 && relativePath == '/') {
|
||||||
|
this.setState({isRepoInfoBarShow: true});
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const isDesktop = Utils.isDesktop();
|
const isDesktop = Utils.isDesktop();
|
||||||
const modeBaseClass = 'btn btn-secondary btn-icon sf-view-mode-btn';
|
const modeBaseClass = 'btn btn-secondary btn-icon sf-view-mode-btn';
|
||||||
@@ -462,6 +492,16 @@ class SharedDirView extends React.Component {
|
|||||||
onFileUploadSuccess={this.onFileUploadSuccess}
|
onFileUploadSuccess={this.onFileUploadSuccess}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{this.state.isRepoInfoBarShow && (
|
||||||
|
<RepoInfoBar
|
||||||
|
repoID={repoID}
|
||||||
|
currentPath={'/'}
|
||||||
|
usedRepoTags={this.state.usedRepoTags}
|
||||||
|
updateUsedRepoTags={this.getShareLinkRepoTags}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Content
|
<Content
|
||||||
isDesktop={isDesktop}
|
isDesktop={isDesktop}
|
||||||
isLoading={this.state.isLoading}
|
isLoading={this.state.isLoading}
|
||||||
@@ -607,6 +647,7 @@ class Content extends React.Component {
|
|||||||
}
|
}
|
||||||
<th width="5%"></th>
|
<th width="5%"></th>
|
||||||
<th width={showDownloadIcon ? '52%' : '55%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortBy == 'name' && sortIcon}</a></th>
|
<th width={showDownloadIcon ? '52%' : '55%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortBy == 'name' && sortIcon}</a></th>
|
||||||
|
<th width="10%"></th>
|
||||||
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' && sortIcon}</a></th>
|
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' && sortIcon}</a></th>
|
||||||
<th width="16%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortBy == 'time' && sortIcon}</a></th>
|
<th width="16%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortBy == 'time' && sortIcon}</a></th>
|
||||||
<th width="10%"></th>
|
<th width="10%"></th>
|
||||||
@@ -674,6 +715,13 @@ class Item extends React.Component {
|
|||||||
const { item, isDesktop } = this.props;
|
const { item, isDesktop } = this.props;
|
||||||
const { isIconShown } = this.state;
|
const { isIconShown } = this.state;
|
||||||
|
|
||||||
|
let toolTipID = '';
|
||||||
|
let tagTitle = '';
|
||||||
|
if (item.file_tags && item.file_tags.length > 0) {
|
||||||
|
toolTipID = MD5(item.file_name).slice(0, 7);
|
||||||
|
tagTitle = item.file_tags.map(item => item.tag_name).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
if (item.is_dir) {
|
if (item.is_dir) {
|
||||||
return isDesktop ? (
|
return isDesktop ? (
|
||||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
||||||
@@ -687,6 +735,7 @@ class Item extends React.Component {
|
|||||||
<a href={`?p=${encodeURIComponent(item.folder_path.substr(0, item.folder_path.length - 1))}&mode=${mode}`}>{item.folder_name}</a>
|
<a href={`?p=${encodeURIComponent(item.folder_path.substr(0, item.folder_path.length - 1))}&mode=${mode}`}>{item.folder_name}</a>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td></td>
|
||||||
<td title={moment(item.last_modified).format('llll')}>{moment(item.last_modified).fromNow()}</td>
|
<td title={moment(item.last_modified).format('llll')}>{moment(item.last_modified).fromNow()}</td>
|
||||||
<td>
|
<td>
|
||||||
{showDownloadIcon &&
|
{showDownloadIcon &&
|
||||||
@@ -743,6 +792,23 @@ class Item extends React.Component {
|
|||||||
<td>
|
<td>
|
||||||
<a href={fileURL} onClick={this.handleFileClick}>{item.file_name}</a>
|
<a href={fileURL} onClick={this.handleFileClick}>{item.file_name}</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td className="tag-list-title">
|
||||||
|
{(item.file_tags && item.file_tags.length > 0) && (
|
||||||
|
<Fragment>
|
||||||
|
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked">
|
||||||
|
{item.file_tags.map((fileTag, index) => {
|
||||||
|
let length = item.file_tags.length;
|
||||||
|
return (
|
||||||
|
<span className="file-tag" key={fileTag.file_tag_id} style={{zIndex:length - index, backgroundColor:fileTag.tag_color}}></span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<UncontrolledTooltip target={`tag-list-title-${toolTipID}`} placement="bottom">
|
||||||
|
{tagTitle}
|
||||||
|
</UncontrolledTooltip>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td>{Utils.bytesToSize(item.size)}</td>
|
<td>{Utils.bytesToSize(item.size)}</td>
|
||||||
<td title={moment(item.last_modified).format('llll')}>{moment(item.last_modified).fromNow()}</td>
|
<td title={moment(item.last_modified).format('llll')}>{moment(item.last_modified).fromNow()}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -862,6 +928,176 @@ class GridItem extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RepoInfoBar extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
currentTag: null,
|
||||||
|
isListTaggedFileShow: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onListTaggedFiles = (currentTag) => {
|
||||||
|
this.setState({
|
||||||
|
currentTag: currentTag,
|
||||||
|
isListTaggedFileShow: !this.state.isListTaggedFileShow,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
isListTaggedFileShow: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {repoID, currentPath, usedRepoTags} = this.props;
|
||||||
|
// let href = readmeMarkdown !== null ? siteRoot + 'lib/' + repoID + '/file' + Utils.joinPath(currentPath, readmeMarkdown.name) + '?mode=edit' : '';
|
||||||
|
// let filePath = readmeMarkdown !== null ? currentPath + readmeMarkdown.name : '';
|
||||||
|
return (
|
||||||
|
<div className="repo-info-bar">
|
||||||
|
{usedRepoTags.length > 0 && (
|
||||||
|
<ul className="used-tag-list">
|
||||||
|
{usedRepoTags.map((usedRepoTag) => {
|
||||||
|
return (
|
||||||
|
<li key={usedRepoTag.id} className="used-tag-item">
|
||||||
|
<span className="used-tag" style={{backgroundColor:usedRepoTag.color}}></span>
|
||||||
|
<span className="used-tag-name" title={usedRepoTag.name}>{usedRepoTag.name}</span>
|
||||||
|
<button type="button" className="used-tag-files border-0 bg-transparent" onClick={this.onListTaggedFiles.bind(this, usedRepoTag)}>
|
||||||
|
{usedRepoTag.fileCount > 1 ? usedRepoTag.fileCount + ' files' : usedRepoTag.fileCount + ' file'}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{this.state.isListTaggedFileShow && (
|
||||||
|
<ModalPortal>
|
||||||
|
<ListTaggedFilesDialog
|
||||||
|
repoID={repoID}
|
||||||
|
currentTag={this.state.currentTag}
|
||||||
|
onClose={this.onCloseDialog}
|
||||||
|
toggleCancel={this.onListTaggedFiles}
|
||||||
|
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ListTaggedFilesDialog extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
taggedFileList: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getTaggedFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
getTaggedFiles = () => {
|
||||||
|
let { repoID, currentTag } = this.props;
|
||||||
|
seafileAPI.getShareLinkTaggedFiles(token, currentTag.id).then(res => {
|
||||||
|
let taggedFileList = [];
|
||||||
|
res.data.tagged_files !== undefined &&
|
||||||
|
res.data.tagged_files.forEach(file => {
|
||||||
|
let taggedFile = file;
|
||||||
|
taggedFileList.push(taggedFile);
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
taggedFileList: taggedFileList,
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let taggedFileList = this.state.taggedFileList;
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true}>
|
||||||
|
<ModalHeader toggle={this.props.onClose}>{gettext('Tagged Files')}</ModalHeader>
|
||||||
|
<ModalBody className="dialog-list-container">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width='45%' className="ellipsis">{gettext('Name')}</th>
|
||||||
|
<th width='27%'>{gettext('Size')}</th>
|
||||||
|
<th width='28%'>{gettext('Last Update')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{taggedFileList.map((taggedFile, index) => {
|
||||||
|
return (
|
||||||
|
<TaggedFile
|
||||||
|
key={index}
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
taggedFile={taggedFile}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggleCancel}>{gettext('Close')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaggedFile extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = ({
|
||||||
|
active: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseEnter = () => {
|
||||||
|
this.setState({
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseLeave = () => {
|
||||||
|
this.setState({
|
||||||
|
active: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const taggedFile = this.props.taggedFile;
|
||||||
|
let className = this.state.active ? 'action-icon sf2-icon-x3' : 'action-icon vh sf2-icon-x3';
|
||||||
|
let path = taggedFile.parent_path ? Utils.joinPath(taggedFile.parent_path, taggedFile.filename) : '';
|
||||||
|
let href = siteRoot + 'd/' + token + '/files/?p=' + Utils.encodePath(path);
|
||||||
|
return ( taggedFile.file_deleted ?
|
||||||
|
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter}>
|
||||||
|
<td colSpan='3' className="name">{taggedFile.filename}{' '}
|
||||||
|
<span style={{color:'red'}}>{gettext('deleted')}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
:
|
||||||
|
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter}>
|
||||||
|
<td className="name"><a href={href} target='_blank'>{taggedFile.filename}</a></td>
|
||||||
|
<td>{Utils.bytesToSize(taggedFile.size)}</td>
|
||||||
|
<td>{moment.unix(taggedFile.mtime).fromNow()}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<SharedDirView />,
|
<SharedDirView />,
|
||||||
document.getElementById('wrapper')
|
document.getElementById('wrapper')
|
||||||
|
@@ -8,6 +8,7 @@ import posixpath
|
|||||||
from constance import config
|
from constance import config
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
@@ -35,6 +36,7 @@ from seahub.utils import gen_shared_link, is_org_context, normalize_file_path, \
|
|||||||
check_filename_with_rename, gen_file_upload_url, get_password_strength_level
|
check_filename_with_rename, gen_file_upload_url, get_password_strength_level
|
||||||
from seahub.utils.file_op import if_locked_by_online_office
|
from seahub.utils.file_op import if_locked_by_online_office
|
||||||
from seahub.utils.file_types import IMAGE, VIDEO, XMIND
|
from seahub.utils.file_types import IMAGE, VIDEO, XMIND
|
||||||
|
from seahub.utils.file_tags import get_tagged_files, get_files_tags_in_dir
|
||||||
from seahub.utils.timeutils import datetime_to_isoformat_timestr, \
|
from seahub.utils.timeutils import datetime_to_isoformat_timestr, \
|
||||||
timestamp_to_isoformat_timestr
|
timestamp_to_isoformat_timestr
|
||||||
from seahub.utils.repo import parse_repo_perm
|
from seahub.utils.repo import parse_repo_perm
|
||||||
@@ -48,6 +50,7 @@ from seahub.wiki.models import Wiki
|
|||||||
from seahub.views.file import can_edit_file
|
from seahub.views.file import can_edit_file
|
||||||
from seahub.views import check_folder_permission
|
from seahub.views import check_folder_permission
|
||||||
from seahub.signals import upload_file_successful
|
from seahub.signals import upload_file_successful
|
||||||
|
from seahub.repo_tags.models import RepoTags
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -789,6 +792,12 @@ class ShareLinkDirents(APIView):
|
|||||||
error_msg = 'Internal Server Error'
|
error_msg = 'Internal Server Error'
|
||||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
files_tags_in_dir = get_files_tags_in_dir(repo_id, path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
files_tags_in_dir = {}
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for dirent in dirent_list:
|
for dirent in dirent_list:
|
||||||
|
|
||||||
@@ -818,6 +827,13 @@ class ShareLinkDirents(APIView):
|
|||||||
src = get_share_link_thumbnail_src(token, thumbnail_size, req_image_path)
|
src = get_share_link_thumbnail_src(token, thumbnail_size, req_image_path)
|
||||||
dirent_info['encoded_thumbnail_src'] = urlquote(src)
|
dirent_info['encoded_thumbnail_src'] = urlquote(src)
|
||||||
|
|
||||||
|
# get tag info
|
||||||
|
file_tags = files_tags_in_dir.get(dirent.obj_name, [])
|
||||||
|
if file_tags:
|
||||||
|
dirent_info['file_tags'] = []
|
||||||
|
for file_tag in file_tags:
|
||||||
|
dirent_info['file_tags'].append(file_tag)
|
||||||
|
|
||||||
result.append(dirent_info)
|
result.append(dirent_info)
|
||||||
|
|
||||||
return Response({'dirent_list': result})
|
return Response({'dirent_list': result})
|
||||||
@@ -1217,3 +1233,93 @@ class ShareLinkSaveItemsToRepo(APIView):
|
|||||||
result['task_id'] = res.task_id
|
result['task_id'] = res.task_id
|
||||||
|
|
||||||
return Response(result)
|
return Response(result)
|
||||||
|
|
||||||
|
|
||||||
|
class ShareLinkRepoTags(APIView):
|
||||||
|
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def get(self, request, token):
|
||||||
|
"""get all repo_tags by share link token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
share_link = FileShare.objects.get(token=token)
|
||||||
|
except FileShare.DoesNotExist:
|
||||||
|
error_msg = 'Token %s not found.' % token
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_id = share_link.repo_id
|
||||||
|
repo = seafile_api.get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# get all tags in repo
|
||||||
|
repo_tags = RepoTags.objects.filter(repo_id=repo_id)
|
||||||
|
|
||||||
|
# get tagged files by tag id
|
||||||
|
tag_id_file_list_dict = defaultdict(list)
|
||||||
|
for repo_tag in repo_tags:
|
||||||
|
tagged_files = get_tagged_files(repo, repo_tag.pk)['tagged_files']
|
||||||
|
tagged_files = [item for item in tagged_files if item.get('parent_path') and item.get('parent_path').startswith(share_link.path.rstrip('/'))]
|
||||||
|
tag_id_file_list_dict[repo_tag.pk] = tagged_files
|
||||||
|
|
||||||
|
# generate response
|
||||||
|
result = {
|
||||||
|
"repo_tags": []
|
||||||
|
}
|
||||||
|
|
||||||
|
for repo_tag in repo_tags:
|
||||||
|
|
||||||
|
repo_tag_info = repo_tag.to_dict()
|
||||||
|
repo_tag_id = repo_tag_info["repo_tag_id"]
|
||||||
|
repo_tag_info["files_count"] = len(tag_id_file_list_dict.get(repo_tag_id, []))
|
||||||
|
|
||||||
|
result['repo_tags'].append(repo_tag_info)
|
||||||
|
|
||||||
|
return Response(result)
|
||||||
|
|
||||||
|
|
||||||
|
class ShareLinkRepoTagsTaggedFiles(APIView):
|
||||||
|
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def get(self, request, token, tag_id):
|
||||||
|
"""get tagged files by share link token and tag id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
share_link = FileShare.objects.get(token=token)
|
||||||
|
except FileShare.DoesNotExist:
|
||||||
|
error_msg = 'Token %s not found.' % token
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_id = share_link.repo_id
|
||||||
|
repo = seafile_api.get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_tag = RepoTags.objects.get_repo_tag_by_id(tag_id)
|
||||||
|
if not repo_tag:
|
||||||
|
error_msg = 'repo_tag %s not found.' % tag_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
share_link_path = share_link.path.rstrip('/') if share_link.path != '/' else '/'
|
||||||
|
|
||||||
|
filtered_tagged_files = []
|
||||||
|
tagged_files = get_tagged_files(repo, tag_id)
|
||||||
|
for tagged_file in tagged_files.get('tagged_files', []):
|
||||||
|
|
||||||
|
if tagged_file.get('file_deleted', False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
tagged_file_parent_path = tagged_file.get('parent_path', '')
|
||||||
|
if share_link_path == '/':
|
||||||
|
filtered_tagged_files.append(tagged_file)
|
||||||
|
elif tagged_file_parent_path.startswith(share_link_path):
|
||||||
|
tagged_file['parent_path'] = '/' + tagged_file_parent_path.lstrip(share_link_path)
|
||||||
|
filtered_tagged_files.append(tagged_file)
|
||||||
|
|
||||||
|
return Response({'tagged_files': filtered_tagged_files})
|
||||||
|
@@ -39,7 +39,8 @@ from seahub.api2.endpoints.group_members import GroupMembers, GroupSearchMember,
|
|||||||
from seahub.api2.endpoints.search_group import SearchGroup
|
from seahub.api2.endpoints.search_group import SearchGroup
|
||||||
from seahub.api2.endpoints.share_links import ShareLinks, ShareLink, \
|
from seahub.api2.endpoints.share_links import ShareLinks, ShareLink, \
|
||||||
ShareLinkOnlineOfficeLock, ShareLinkDirents, ShareLinkSaveFileToRepo, \
|
ShareLinkOnlineOfficeLock, ShareLinkDirents, ShareLinkSaveFileToRepo, \
|
||||||
ShareLinkUpload, ShareLinkUploadDone, ShareLinkSaveItemsToRepo
|
ShareLinkUpload, ShareLinkUploadDone, ShareLinkSaveItemsToRepo, \
|
||||||
|
ShareLinkRepoTags, ShareLinkRepoTagsTaggedFiles
|
||||||
from seahub.api2.endpoints.shared_folders import SharedFolders
|
from seahub.api2.endpoints.shared_folders import SharedFolders
|
||||||
from seahub.api2.endpoints.shared_repos import SharedRepos, SharedRepo
|
from seahub.api2.endpoints.shared_repos import SharedRepos, SharedRepo
|
||||||
from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink, \
|
from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink, \
|
||||||
@@ -348,6 +349,9 @@ urlpatterns = [
|
|||||||
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/upload/$', ShareLinkUpload.as_view(), name='api-v2.1-share-link-upload'),
|
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/upload/$', ShareLinkUpload.as_view(), name='api-v2.1-share-link-upload'),
|
||||||
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/upload/done/$', ShareLinkUploadDone.as_view(), name='api-v2.1-share-link-upload-done'),
|
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/upload/done/$', ShareLinkUploadDone.as_view(), name='api-v2.1-share-link-upload-done'),
|
||||||
|
|
||||||
|
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/repo-tags/$', ShareLinkRepoTags.as_view(), name='api-v2.1-share-link-repo-tags'),
|
||||||
|
url(r'^api/v2.1/share-links/(?P<token>[a-f0-9]+)/repo-tags/(?P<tag_id>\d+)/$', ShareLinkRepoTagsTaggedFiles.as_view(), name='api-v2.1-share-link-repo-tags-tagged-files'),
|
||||||
|
|
||||||
## user::shared-upload-links
|
## user::shared-upload-links
|
||||||
url(r'^api/v2.1/upload-links/$', UploadLinks.as_view(), name='api-v2.1-upload-links'),
|
url(r'^api/v2.1/upload-links/$', UploadLinks.as_view(), name='api-v2.1-upload-links'),
|
||||||
url(r'^api/v2.1/upload-links/(?P<token>[a-f0-9]+)/$', UploadLink.as_view(), name='api-v2.1-upload-link'),
|
url(r'^api/v2.1/upload-links/(?P<token>[a-f0-9]+)/$', UploadLink.as_view(), name='api-v2.1-upload-link'),
|
||||||
|
Reference in New Issue
Block a user