Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update repo trash #6148

Merged
merged 33 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
432 changes: 432 additions & 0 deletions frontend/src/components/dialog/trash-dialog.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/src/components/dir-view-mode/dir-column-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ class DirColumnNav extends React.Component {
<DirOthers
repoID={this.props.repoID}
userPerm={this.props.userPerm}
currentRepoInfo={this.props.currentRepoInfo}
/>
</>
);
Expand Down
22 changes: 17 additions & 5 deletions frontend/src/components/dir-view-mode/dir-others.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import React from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { gettext, siteRoot } from '../../utils/constants';
import TreeSection from '../tree-section';
import TrashDialog from '../dialog/trash-dialog';

const DirOthers = ({ userPerm, repoID }) => {

const DirOthers = ({ userPerm, repoID, currentRepoInfo }) => {
const [showTrashDialog, setShowTrashDialog] = useState(false);
let trashUrl = null;
const historyUrl = siteRoot + 'repo/history/' + repoID + '/';
if (userPerm === 'rw') {
trashUrl = siteRoot + 'repo/' + repoID + '/trash/';
}

const toggleTrashDialog = () => {
setShowTrashDialog(!showTrashDialog);
};
return (
<TreeSection title={gettext('Others')} className="dir-others">
Michael18811380328 marked this conversation as resolved.
Show resolved Hide resolved
{trashUrl &&
<div className='tree-node-inner text-nowrap' title={gettext('Trash')} onClick={() => location.href = trashUrl}>
<div className='tree-node-inner text-nowrap' title={gettext('Trash')} onClick={toggleTrashDialog}>
<div className="tree-node-text">{gettext('Trash')}</div>
<div className="left-icon">
<div className="tree-node-icon">
Expand All @@ -31,13 +34,22 @@ const DirOthers = ({ userPerm, repoID }) => {
</div>
</div>
</div>
{showTrashDialog && (
<TrashDialog
repoID={repoID}
currentRepoInfo={currentRepoInfo}
showTrashDialog={showTrashDialog}
toggleTrashDialog={toggleTrashDialog}
/>
)}
</TreeSection>
);
};

DirOthers.propTypes = {
userPerm: PropTypes.string,
repoID: PropTypes.string,
currentRepoInfo: PropTypes.object.isRequired,
};

export default DirOthers;
44 changes: 44 additions & 0 deletions frontend/src/css/trash-dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.trash-dialog {
max-width: 1100px;
}

.trash-dialog .modal-header {
align-items: center;
}

.trash-dialog .modal-header .trash-dialog-old-page {
margin-left: auto;
}

.trash-dialog .modal-header .trash-dialog-close-icon {
color: #000;
opacity: 0.5;
font-weight: 700;
cursor: pointer;
}

.trash-dialog .modal-header .trash-dialog-close-icon:hover {
opacity: 0.75;
}

.trash-dialog .modal-header .clean {
height: 30px;
line-height: 28px;
padding: 0 0.5rem;
}

.trash-dialog .modal-body {
height: 500px;
overflow-y: auto;
}

.trash-dialog .modal-body .more {
background: #efefef;
border: 0;
color: #777;
}

.trash-dialog .modal-body .more:hover {
color: #000;
background: #dfdfdf;
}
6 changes: 3 additions & 3 deletions frontend/src/repo-folder-trash.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const {
repoID,
repoFolderName,
path,
enableClean,
enableUserCleanTrash,
isRepoAdmin
} = window.app.pageOptions;

Expand All @@ -35,7 +35,7 @@ class RepoFolderTrash extends React.Component {
items: [],
scanStat: null,
more: false,
isCleanTrashDialogOpen: false
isCleanTrashDialogOpen: false,
};
}

Expand Down Expand Up @@ -204,7 +204,7 @@ class RepoFolderTrash extends React.Component {
</a>
<div className="d-flex justify-content-between align-items-center op-bar">
<p className="m-0 text-truncate d-flex"><span className="mr-1">{gettext('Current path: ')}</span>{showFolder ? this.renderFolderPath() : <span className="text-truncate" title={repoFolderName}>{repoFolderName}</span>}</p>
{(path == '/' && enableClean && !showFolder && isRepoAdmin) &&
{(path == '/' && enableUserCleanTrash && !showFolder && isRepoAdmin) &&
<button className="btn btn-secondary clean flex-shrink-0 ml-4" onClick={this.cleanTrash}>{gettext('Clean')}</button>
}
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const maxFileName = window.app.pageOptions.maxFileName;
export const canPublishRepo = window.app.pageOptions.canPublishRepo;
export const enableEncryptedLibrary = window.app.pageOptions.enableEncryptedLibrary;
export const enableRepoHistorySetting = window.app.pageOptions.enableRepoHistorySetting;
export const enableUserCleanTrash = window.app.pageOptions.enableUserCleanTrash;
export const isSystemStaff = window.app.pageOptions.isSystemStaff;
export const thumbnailSizeForOriginal = window.app.pageOptions.thumbnailSizeForOriginal;
export const repoPasswordMinLength = window.app.pageOptions.repoPasswordMinLength;
Expand Down
51 changes: 51 additions & 0 deletions frontend/src/utils/repo-trash-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import axios from 'axios';
import cookie from 'react-cookies';
import { siteRoot } from './constants';

class RepotrashAPI {

init({ server, username, password, token }) {
this.server = server;
this.username = username;
this.password = password;
this.token = token; //none
if (this.token && this.server) {
this.req = axios.create({
baseURL: this.server,
headers: { 'Authorization': 'Token ' + this.token },
});
}
return this;
}

initForSeahubUsage({ siteRoot, xcsrfHeaders }) {
if (siteRoot && siteRoot.charAt(siteRoot.length - 1) === '/') {
var server = siteRoot.substring(0, siteRoot.length - 1);
this.server = server;
} else {
this.server = siteRoot;
}

this.req = axios.create({
headers: {
'X-CSRFToken': xcsrfHeaders,
}
});
return this;
}

getRepoFolderTrash2(repoID, page, per_page) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/trash2/';
let params = {
page: page || 1,
per_page: per_page
};
return this.req.get(url, {params: params});
}
}

let repotrashAPI = new RepotrashAPI();
let xcsrfHeaders = cookie.load('sfcsrftoken');
repotrashAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });

export { repotrashAPI };
90 changes: 89 additions & 1 deletion seahub/api2/endpoints/repo_trash.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2012-2016 Seafile Ltd.
import stat
import logging
import os
from datetime import datetime

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
Expand All @@ -13,6 +15,7 @@
from seahub.api2.utils import api_error

from seahub.signals import clean_up_repo_trash
from seahub.utils import get_trash_records
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.utils.repo import get_repo_owner, is_repo_admin
from seahub.views import check_folder_permission
Expand All @@ -24,7 +27,7 @@
from constance import config

logger = logging.getLogger(__name__)

SHOW_REPO_TRASH_DAYS = 90

class RepoTrash(APIView):

Expand Down Expand Up @@ -303,3 +306,88 @@ def post(self, request, repo_id):
})

return Response(result)


class RepoTrash2(APIView):

authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )

def get_item_info(self, trash_item):

item_info = {
'parent_dir': '/' if trash_item.path == '/' else trash_item.path,
'obj_name': trash_item.obj_name,
'deleted_time': timestamp_to_isoformat_timestr(int(trash_item.delete_time.timestamp())),
'commit_id': trash_item.commit_id,
}

if trash_item.obj_type == 'dir':
is_dir = True
else:
is_dir = False

item_info['is_dir'] = is_dir
item_info['size'] = trash_item.size if not is_dir else ''
item_info['obj_id'] = trash_item.obj_id if not is_dir else ''

return item_info

def get(self, request, repo_id):
""" Return deleted files/dirs of a repo/folder

Permission checking:
1. all authenticated user can perform this action.
"""

path = '/'
# resource check
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)

try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '100'))
except ValueError:
current_page = 1
per_page = 100
start = (current_page - 1) * per_page
limit = per_page
try:
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)

if not dir_id:
error_msg = 'Folder %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

# permission check
if check_folder_permission(request, repo_id, path) is None:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

try:
deleted_entries, total_count = get_trash_records(repo_id, SHOW_REPO_TRASH_DAYS, start, limit)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)

items = []
if len(deleted_entries) >= 1:
for item in deleted_entries:
item_info = self.get_item_info(item)
items.append(item_info)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在seafevents层面直接排序后返回, 不需要直接在这里进行排序

result = {
'items': items,
'total_count': total_count
}

return Response(result)
41 changes: 41 additions & 0 deletions seahub/base/management/commands/clean_repo_trash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import logging
from datetime import datetime
from seahub.utils import SeafEventsSession
from seafevents import seafevents_api
from django.core.management.base import BaseCommand


logger = logging.getLogger(__name__)

class Command(BaseCommand):
help = 'Clear repo trash within the specified time'
label = 'clean_repo_trash'

def print_msg(self, msg):
self.stdout.write('[%s] %s\n' % (datetime.now(), msg))

def add_arguments(self, parser):
parser.add_argument('--keep-days', help='keep days', type=int, default=90)

def handle(self, *args, **options):
days = options.get('keep_days')
if days < 0:
self.print_msg('keep-days cannot be set to nagative number')
return
logger.info('Start clean repo trash...')
self.print_msg('Start clean repo trash...')
self.do_action(days)
self.print_msg('Finish clean repo trash.\n')
logger.info('Finish clean repo trash.\n')

def do_action(self, days):
try:
session = SeafEventsSession()
seafevents_api.clean_up_all_repo_trash(session, days)
except Exception as e:
logger.debug('Clean up repo trash error: %s' % e)
self.print_msg('Clean up repo trash error: %s' % e)
return

logger.info('Successfully cleared repo trash older than %s days' % days)
self.print_msg('Successfully cleared repo trash older than %s days' % days)
7 changes: 6 additions & 1 deletion seahub/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ def clean_up_repo_trash_cb(sender, **kwargs):

from .utils import SeafEventsSession
session = SeafEventsSession()
seafevents_api.save_user_activity(session, record)
try:
seafevents_api.save_user_activity(session, record)
seafevents_api.clean_up_repo_trash(session, repo_id, days)
except Exception as e:
logger.error(e)

session.close()

def repo_restored_cb(sender, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions seahub/templates/base_for_react.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
canPublishRepo: {% if user.permissions.can_publish_repo %} true {% else %} false {% endif %},
enableEncryptedLibrary: {% if enable_encrypted_library %} true {% else %} false {% endif %},
enableRepoHistorySetting: {% if enable_repo_history_setting %} true {% else %} false {% endif %},
enableUserCleanTrash: {% if enable_user_clean_trash %} true {% else %} false {% endif %},
isSystemStaff: {% if request.user.is_staff %} true {% else %} false {% endif %},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enableClean-->enableUserCleanTrash

thumbnailSizeForOriginal: {{ thumbnail_size_for_original }},
repoPasswordMinLength: {{repo_password_min_length}},
Expand Down
2 changes: 1 addition & 1 deletion seahub/templates/repo_folder_trash_react.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
repoID: '{{repo.id}}',
repoFolderName: '{{repo_folder_name|escapejs}}',
path: '{{path|escapejs}}',
enableClean: {% if enable_clean %} true {% else %} false {% endif %},
enableUserCleanTrash: {% if enable_user_clean_trash %} true {% else %} false {% endif %},
isRepoAdmin: {% if is_repo_admin %} true {% else %} false {% endif %}
};
</script>
Expand Down
3 changes: 2 additions & 1 deletion seahub/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
from seahub.api2.endpoints.dir import DirView, DirDetailView
from seahub.api2.endpoints.file_tag import FileTagView
from seahub.api2.endpoints.file_tag import FileTagsView
from seahub.api2.endpoints.repo_trash import RepoTrash, RepoTrashRevertDirents
from seahub.api2.endpoints.repo_trash import RepoTrash, RepoTrashRevertDirents, RepoTrash2
from seahub.api2.endpoints.repo_commit import RepoCommitView
from seahub.api2.endpoints.repo_commit_dir import RepoCommitDirView
from seahub.api2.endpoints.repo_commit_revert import RepoCommitRevertView
Expand Down Expand Up @@ -430,6 +430,7 @@
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/commits/(?P<commit_id>[0-9a-f]{40})/revert/$', RepoCommitRevertView.as_view(), name='api-v2.1-repo-commit-revert'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/dir/detail/$', DirDetailView.as_view(), name='api-v2.1-dir-detail-view'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/trash/$', RepoTrash.as_view(), name='api-v2.1-repo-trash'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/trash2/$', RepoTrash2.as_view(), name='api-v2.1-repo-trash2'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/trash/revert-dirents/$', RepoTrashRevertDirents.as_view(), name='api-v2.1-repo-trash-revert-dirents'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/history/$', RepoHistory.as_view(), name='api-v2.1-repo-history'),
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/set-password/$', RepoSetPassword.as_view(), name="api-v2.1-repo-set-password"),
Expand Down
Loading
Loading