mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-12 13:24:52 +00:00
add 'list file history' and 'revert file'
This commit is contained in:
@@ -35,8 +35,12 @@ def file_icon_filter(value):
|
||||
@register.filter(name='translate_commit_desc')
|
||||
def translate_commit_desc(value):
|
||||
"""Translate commit description."""
|
||||
if value.startswith('Reverted'):
|
||||
if value.startswith('Reverted repo'):
|
||||
return value.replace('Reverted repo to status at', u'同步目录内容还原到')
|
||||
elif value.startswith('Reverted file'):
|
||||
value = value.replace('Reverted file', u'还原文件')
|
||||
value = value.replace('to status at', u'内容到')
|
||||
return value
|
||||
elif value.startswith('Merged'):
|
||||
return u'合并了其他人的修改'
|
||||
else:
|
||||
|
59
templates/file_revisions.html
Normal file
59
templates/file_revisions.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
{% load seahub_tags %}
|
||||
|
||||
{% block info_bar_message %}
|
||||
{% if request.user.is_authenticated %}
|
||||
{{ block.super }}
|
||||
{% else %}
|
||||
<div id="info-bar">
|
||||
<span class="info">当前链接会在短期内失效,欢迎您 <a href="http://seafile.com/" target="_blank">加入Seafile </a>体验更多功能。</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
<h2>文件修改历史</h2>
|
||||
<h3 class="file-revisions"><span class="file-name">{{ repo.props.name }} {{path}}</span></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th width="30%">修改时间</th>
|
||||
<th width="20%">修改者</th>
|
||||
<th width="20%">大小</th>
|
||||
<th width="30%">操作</th>
|
||||
</tr>
|
||||
{% for commit in commits %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ commit.props.ctime|tsstr_sec }}
|
||||
{% if commit.is_current_version %}
|
||||
(当前版本)
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% if commit.props.creator_name %}
|
||||
<td>{{ commit.props.creator_name }}</td>
|
||||
{% else %}
|
||||
<td>未知</td>
|
||||
{% endif %}
|
||||
|
||||
<td>{{ commit.revision_file_size|filesizeformat }} </td>
|
||||
|
||||
<td>
|
||||
{% if not forloop.first %}
|
||||
{% if is_owner %}
|
||||
<a href="{{ SITE_ROOT }}repo/file_revisions/{{ repo.id }}/?commit={{ commit.id }}&p={{path|urlencode}}&op=revert" class="file-revert op">还原</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a href="{{ SITE_ROOT }}repo/file_revisions/{{ repo.id }}/?commit={{ commit.id }}&p={{path|urlencode}}&op=download" class="file-download op">下载</a>
|
||||
<a href="{{ SITE_ROOT }}repo/file_revisions/{{ repo.id }}/?commit={{ commit.id }}&p={{path|urlencode}}&op=view" class="file-download op">查看</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
@@ -109,7 +109,7 @@
|
||||
</p>
|
||||
{% if not view_history and request.user.is_authenticated %}
|
||||
<div class="repo-op fright">
|
||||
<a href="{{ SITE_ROOT }}repo/upload_file/{{repo.id}}/?p={{ path }}" class="upload-file">上传</a>
|
||||
<a href="{{ SITE_ROOT }}repo/upload_file/{{repo.id}}/?p={{ path|urlencode }}" class="upload-file">上传</a>
|
||||
<a id="add-new-dir" href="#">新建目录</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -169,7 +169,8 @@
|
||||
<li><a class="op file-rename" href="#" data="{{ dirent.obj_name }}">重命名</a></li>
|
||||
<li><a class="op file-mv" href="#" data="{{ dirent.obj_name }}">移动</a></li>
|
||||
<li><a class="op file-cp" href="#" data="{{ dirent.obj_name }}">复制</a></li>
|
||||
<li><a class="op file-update" href="{{ SITE_ROOT }}repo/update_file/{{repo.id}}/?p={{ path }}{{dirent.obj_name}}">更新</a></li>
|
||||
<li><a class="op file-update" href="{{ SITE_ROOT }}repo/update_file/{{repo.id}}/?p={{ path|urlencode }}{{dirent.obj_name|urlencode}}">更新</a></li>
|
||||
<li><a class="op list-revisions" href="{{ SITE_ROOT }}repo/file_revisions/{{repo.id}}/?p={{ path|urlencode}}{{dirent.obj_name|urlencode}}">历史</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -23,6 +23,7 @@
|
||||
<div class="side fright">
|
||||
<h3>操作</h3>
|
||||
<p><a href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=view" target="_blank">查看原始文件</a></p>
|
||||
<p><a href="{{ SITE_ROOT }}repo/file_revisions/{{ repo.id }}/?p={{ path|urlencode }}">查看所有历史版本</a></p>
|
||||
<p><a href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download" target="_blank">下载文件</a></p>
|
||||
</div>
|
||||
|
||||
|
5
urls.py
5
urls.py
@@ -12,7 +12,7 @@ from seahub.views import root, peers, myhome, \
|
||||
seafile_access_check, back_local, repo_history_changes, \
|
||||
repo_upload_file, file_upload_progress, file_upload_progress_page, get_subdir, file_move, \
|
||||
repo_new_dir, repo_rename_file, validate_filename, \
|
||||
repo_create, repo_update_file
|
||||
repo_create, repo_update_file, file_revisions
|
||||
from seahub.notifications.views import notification_list
|
||||
from seahub.share.views import share_admin
|
||||
from seahub.group.views import group_list
|
||||
@@ -51,6 +51,7 @@ urlpatterns = patterns('',
|
||||
(r'^repo/file_rename/$', repo_rename_file),
|
||||
url(r'^repo/upload_file/(?P<repo_id>[^/]+)/$', repo_upload_file, name='repo_upload_file'),
|
||||
url(r'^repo/update_file/(?P<repo_id>[^/]+)/$', repo_update_file, name='repo_update_file'),
|
||||
url(r'^repo/file_revisions/(?P<repo_id>[^/]+)/$', file_revisions, name='file_revisions'),
|
||||
url(r'^repo/(?P<repo_id>[^/]+)/$', repo, name='repo'),
|
||||
(r'^repo/history/(?P<repo_id>[^/]+)/$', repo_history),
|
||||
(r'^repo/history/revert/(?P<repo_id>[^/]+)/$', repo_history_revert),
|
||||
@@ -59,7 +60,7 @@ urlpatterns = patterns('',
|
||||
(r'^repo/remove/(?P<repo_id>[^/]+)/$', remove_repo),
|
||||
# (r'^repo/removefetched/(?P<user_id>[^/]+)/(?P<repo_id>[^/]+)/$', remove_fetched_repo),
|
||||
# (r'^repo/setap/(?P<repo_id>[^/]+)/$', repo_set_access_property),
|
||||
(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file),
|
||||
url(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file, name='repo_access_file'),
|
||||
(r'^repo/(?P<repo_id>[^/]+)/view/(?P<obj_id>[^/]+)/$', repo_view_file),
|
||||
|
||||
(r'^download/repo/$', repo_download),
|
||||
|
21
utils.py
21
utils.py
@@ -142,7 +142,7 @@ class UploadProgressCachedHandler(FileUploadHandler):
|
||||
|
||||
def check_filename_with_rename(repo_id, parent_dir, filename):
|
||||
latest_commit = get_commits(repo_id, 0, 1)[0]
|
||||
dirents = seafserv_rpc.list_dir_by_path(latest_commit.id,
|
||||
dirents = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id,
|
||||
parent_dir.encode('utf-8'))
|
||||
|
||||
def no_duplicate(name):
|
||||
@@ -183,7 +183,7 @@ def get_accessible_repos(request, repo):
|
||||
if latest_commit.root_id == EMPTY_SHA1:
|
||||
return False
|
||||
|
||||
dirs = seafserv_rpc.list_dir_by_path(latest_commit.id, '/')
|
||||
dirs = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, '/')
|
||||
|
||||
for dirent in dirs:
|
||||
if stat.S_ISDIR(dirent.props.mode):
|
||||
@@ -237,3 +237,20 @@ def valid_previewed_file(filename):
|
||||
if fileExt in PREVIEW_FILEEXT.get(filetype):
|
||||
return (True, filetype)
|
||||
return (False, '')
|
||||
|
||||
def get_file_revision_id_size (commit_id, path):
|
||||
"""Given a commit and a file path in that commit, return the seafile id
|
||||
and size of the file blob
|
||||
|
||||
"""
|
||||
dirname = os.path.dirname(path)
|
||||
filename = os.path.basename(path)
|
||||
seafdir = seafserv_threaded_rpc.list_dir_by_path (commit_id, dirname)
|
||||
for dirent in seafdir:
|
||||
if dirent.obj_name == filename:
|
||||
file_size = seafserv_threaded_rpc.get_file_size(dirent.obj_id)
|
||||
return dirent.obj_id, file_size
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
|
130
views.py
130
views.py
@@ -37,7 +37,8 @@ from forms import AddUserForm
|
||||
from utils import go_permission_error, go_error, list_to_string, \
|
||||
get_httpserver_root, get_ccnetapplet_root, gen_token, \
|
||||
calculate_repo_last_modify, valid_previewed_file, \
|
||||
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1
|
||||
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1, \
|
||||
get_file_revision_id_size
|
||||
from seahub.profile.models import Profile
|
||||
from settings import FILE_PREVIEW_MAX_SIZE
|
||||
|
||||
@@ -203,7 +204,7 @@ def render_repo(request, repo_id, error=''):
|
||||
# view newest worktree or history worktree
|
||||
commit_id = request.GET.get('commit_id', '')
|
||||
view_history = True if commit_id else False
|
||||
current_commit = seafserv_rpc.get_commit(commit_id)
|
||||
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
|
||||
if not current_commit:
|
||||
current_commit = get_commits(repo_id, 0, 1)[0]
|
||||
|
||||
@@ -226,7 +227,7 @@ def render_repo(request, repo_id, error=''):
|
||||
dirs = []
|
||||
else:
|
||||
try:
|
||||
dirs = seafserv_rpc.list_dir_by_path(current_commit.id,
|
||||
dirs = seafserv_threaded_rpc.list_dir_by_path(current_commit.id,
|
||||
path.encode('utf-8'))
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
@@ -236,7 +237,7 @@ def render_repo(request, repo_id, error=''):
|
||||
else:
|
||||
file_list.append(dirent)
|
||||
try:
|
||||
dirent.file_size = seafserv_rpc.get_file_size(dirent.obj_id)
|
||||
dirent.file_size = seafserv_threaded_rpc.get_file_size(dirent.obj_id)
|
||||
except:
|
||||
dirent.file_size = 0
|
||||
dir_list.sort(lambda x, y : cmp(x.obj_name.lower(),
|
||||
@@ -431,7 +432,7 @@ def get_subdir(request):
|
||||
|
||||
latest_commit = get_commits(repo_id, 0, 1)[0]
|
||||
try:
|
||||
dirents = seafserv_rpc.list_dir_by_path(latest_commit.id, path.encode('utf-8'))
|
||||
dirents = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, path.encode('utf-8'))
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
@@ -443,7 +444,7 @@ def get_subdir(request):
|
||||
dirent.has_subdir = False
|
||||
path_ = os.path.join (path, dirent.obj_name)
|
||||
try:
|
||||
dirs_ = seafserv_rpc.list_dir_by_path(latest_commit.id, path_.encode('utf-8'))
|
||||
dirs_ = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, path_.encode('utf-8'))
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
@@ -793,7 +794,7 @@ def repo_view_file(request, repo_id, obj_id):
|
||||
filename = urllib2.quote(request.GET.get('file_name', '').encode('utf-8'))
|
||||
commit_id = request.GET.get('commit_id', '')
|
||||
view_history = True if commit_id else False
|
||||
current_commit = seafserv_rpc.get_commit(commit_id)
|
||||
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
|
||||
if not current_commit:
|
||||
current_commit = get_commits(repo_id, 0, 1)[0]
|
||||
|
||||
@@ -864,7 +865,7 @@ def repo_view_file(request, repo_id, obj_id):
|
||||
|
||||
# query commit info
|
||||
commit_id = request.GET.get('commit_id', None)
|
||||
current_commit = seafserv_rpc.get_commit(commit_id)
|
||||
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
|
||||
if not current_commit:
|
||||
current_commit = get_commits(repo.id, 0, 1)[0]
|
||||
|
||||
@@ -885,6 +886,7 @@ def repo_view_file(request, repo_id, obj_id):
|
||||
|
||||
return render_to_response('repo_view_file.html', {
|
||||
'repo': repo,
|
||||
'path': path,
|
||||
'obj_id': obj_id,
|
||||
'file_name': filename,
|
||||
'zipped': zipped,
|
||||
@@ -1547,8 +1549,6 @@ def repo_rename_file(request):
|
||||
newname = request.POST.get("newname")
|
||||
user = request.user.username
|
||||
|
||||
# print repo_id, parent_dir, oldname, newname, user
|
||||
|
||||
if not newname:
|
||||
error_msg = u"新文件名不能为空"
|
||||
return go_error(request, error_msg)
|
||||
@@ -1646,3 +1646,113 @@ def repo_create(request):
|
||||
error_msg = u"创建目录失败"
|
||||
return render_repo_create_error(error_msg)
|
||||
return HttpResponseRedirect(reverse(myhome))
|
||||
|
||||
def render_file_revisions (request, repo_id):
|
||||
"""List all history versions of a file."""
|
||||
target_file = request.GET.get('p')
|
||||
if not target_file:
|
||||
return go_error(request)
|
||||
|
||||
repo = get_repo(repo_id)
|
||||
if not repo:
|
||||
error_msg = u"同步目录不存在"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
try:
|
||||
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, target_file)
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
if not commits:
|
||||
return go_error(request)
|
||||
|
||||
# Check whether use is repo owner
|
||||
if validate_owner(request, repo_id):
|
||||
is_owner = True
|
||||
else:
|
||||
is_owner = False
|
||||
|
||||
try:
|
||||
current_commit = get_commits(repo_id, 0, 1)[0]
|
||||
current_file_id = get_file_revision_id_size (current_commit.id, target_file)[0]
|
||||
for commit in commits:
|
||||
file_id, file_size = get_file_revision_id_size (commit.id, target_file)
|
||||
if not file_id or not file_size:
|
||||
return go_error(request)
|
||||
commit.revision_file_size = file_size
|
||||
if file_id == current_file_id:
|
||||
commit.is_current_version = True
|
||||
else:
|
||||
commit.is_current_version = False
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
|
||||
return render_to_response('file_revisions.html', {
|
||||
'repo': repo,
|
||||
'path': target_file,
|
||||
'commits': commits,
|
||||
'is_owner': is_owner,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def file_revisions(request, repo_id):
|
||||
if request.method != 'GET':
|
||||
return go_error(request)
|
||||
|
||||
op = request.GET.get('op')
|
||||
if not op:
|
||||
return render_file_revisions(request, repo_id)
|
||||
elif op != 'revert' and op != 'download' and op != 'view':
|
||||
return go_error(request)
|
||||
|
||||
commit_id = request.GET.get('commit')
|
||||
path = request.GET.get('p')
|
||||
|
||||
if not (commit_id and path):
|
||||
return go_error(request)
|
||||
|
||||
if op == 'revert':
|
||||
try:
|
||||
seafserv_threaded_rpc.revert_file (repo_id, commit_id,
|
||||
path, request.user.username)
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
else:
|
||||
parent_dir = os.path.dirname(path)
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
elif op == 'download':
|
||||
def handle_download():
|
||||
parent_dir = os.path.dirname(path)
|
||||
file_name = os.path.basename(path)
|
||||
seafdir = seafserv_threaded_rpc.list_dir_by_path (commit_id, \
|
||||
parent_dir.encode('utf-8'))
|
||||
if not seafdir:
|
||||
return go_error(request)
|
||||
|
||||
# for ... else ...
|
||||
for dirent in seafdir:
|
||||
if dirent.obj_name == file_name:
|
||||
break
|
||||
else:
|
||||
return go_error(request)
|
||||
|
||||
url = reverse('repo_access_file', args=[repo_id, dirent.obj_id])
|
||||
url += '?file_name=%s&op=download' % file_name
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
try:
|
||||
return handle_download()
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
elif op == 'view':
|
||||
seafile_id = get_file_revision_id_size (commit_id, path)[0]
|
||||
if not seafile_id:
|
||||
return go_error(request)
|
||||
file_name = os.path.basename(path)
|
||||
url = reverse(repo_view_file, args=[repo_id, seafile_id])
|
||||
url += u'?commit_id=%s&file_name=%s&p=%s' \
|
||||
% (commit_id, file_name, path)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
Reference in New Issue
Block a user