1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 07:01:12 +00:00

Add file preview feature

This commit is contained in:
xiez
2012-07-02 22:45:21 +08:00
parent 211893e0b7
commit 340ad516e4
7 changed files with 212 additions and 5 deletions

View File

@@ -582,3 +582,15 @@ table img {
#upload-cancel { #upload-cancel {
margin-top:8px; margin-top:8px;
} }
/* file preview */
#file-content {
border: 1px solid #999;
padding: 5px;
min-height: 200px;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}

View File

@@ -170,6 +170,13 @@ USE_SUBDOMAIN = False
# account type is `personal` or `business` # account type is `personal` or `business`
ACCOUNT_TYPE = 'personal' ACCOUNT_TYPE = 'personal'
FILE_PREVIEW_MAX_SIZE = 10 * 1024 * 1024
PREVIEW_FILEEXT = {
'Document': ('ac', 'am', 'as', 'as3', 'asm', 'bat', 'c', 'cc', 'cmake', 'cpp', 'cs', 'css', 'csv', 'cxx', 'diff', 'erb', 'groovy', 'gsheet', 'h', 'haml', 'hh', 'htm', 'html', 'java', 'js', 'less', 'm', 'make', 'ml', 'mm', 'ods', 'odt', 'php', 'pl', 'properties', 'py', 'rb', 'rtf', 'sass', 'scala', 'scm', 'script', 'sh', 'sml', 'sql', 'txt', 'vi', 'vim', 'wpd', 'xls', 'xlsm', 'xlsx', 'xml', 'xsd', 'xsl', 'xslt', 'yaml'),
'Image': ('ai', 'bmp', 'eps', 'gif', 'ind', 'jpeg', 'jpg', 'png', 'ps', 'psd', 'tif', 'tiff'),
}
try: try:
import local_settings import local_settings
except ImportError: except ImportError:

View File

@@ -104,7 +104,7 @@
{% for dirent in file_list %} {% for dirent in file_list %}
<tr> <tr>
<td class="icon-container"><img src="{{ MEDIA_URL }}img/{{ dirent.obj_name|file_icon_filter }}" alt="文件" /></td> <td class="icon-container"><img src="{{ MEDIA_URL }}img/{{ dirent.obj_name|file_icon_filter }}" alt="文件" /></td>
<td><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=view">{{ dirent.props.obj_name }}</a></td> <td><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/view/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.props.obj_name }}</a></td>
<td>{{ dirent.file_size|filesizeformat }}</td> <td>{{ dirent.file_size|filesizeformat }}</td>
<td> <td>
<img src="{{ MEDIA_URL }}img/more-option.png" alt="更多操作" class="more-op hide" /> <img src="{{ MEDIA_URL }}img/more-option.png" alt="更多操作" class="more-op hide" />

View File

@@ -0,0 +1,71 @@
{% 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 class="subject">
{{repo.props.name}}
</h2>
<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/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download" target="_blank">下载文件</a></p>
</div>
<div class="main fleft">
<p class="path">
当前路径:
{% for name, link in zipped %}
{% if not forloop.last %}
<a href="{{ SITE_ROOT }}repo/{{ repo.id }}/?p={{ link|urlencode }}">{{ name }}</a> /
{% else %}
{{ name }}
{% endif %}
{% endfor %}
</p>
<pre id="file-content">正在读取文件内容...</pre>
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
$(window).load(function() {
var can_preview = "{{ can_preview }}";
var filetype = "{{ filetype }}";
if (can_preview == 'True' && filetype == 'Document') {
$.ajax({
url: '{{ SITE_ROOT }}repo/{{ repo.id }}/view/{{ obj_id }}/?file_name={{ file_name }}&t={{ token }}',
dataType: 'json',
cache: false,
contentType: 'application/json; charset=utf-8',
success: function(data) {
if (data.length > 0) {
$('#file-content').html(data[0]['content']);
}
},
error: function(xhr, ajaxOptions, thrownError) {
var jsonVal = jQuery.parseJSON(xhr.responseText);
$('#file-content').html(jsonVal[0]['error']);
}
});
return false;
} else if (can_preview == 'True' && filetype == 'Image') {
$('#file-content').replaceWith('<img src="{{ raw_path }}" width="550" height="450"></img>');
} else {
$('#file-content').html('无法识别该文件格式,<a class="op" href="">下载文件</a>。');
}
});
</script>
{% endblock %}

View File

@@ -8,11 +8,10 @@ from seahub.views import root, peers, myhome, \
activate_user, user_add, user_remove, sys_group_admin, sys_org_admin, \ activate_user, user_add, user_remove, sys_group_admin, sys_org_admin, \
ownerhome, repo_history_dir, repo_history_revert, \ ownerhome, repo_history_dir, repo_history_revert, \
user_info, repo_set_access_property, repo_access_file, \ user_info, repo_set_access_property, repo_access_file, \
repo_remove_share, repo_download, org_info, \ repo_remove_share, repo_download, org_info, repo_view_file, \
seafile_access_check, back_local, repo_history_changes, \ seafile_access_check, back_local, repo_history_changes, \
repo_upload_file, file_upload_progress, file_upload_progress_page, get_subdir, file_move, \ repo_upload_file, file_upload_progress, file_upload_progress_page, get_subdir, file_move, \
repo_new_dir, repo_rename_file, validate_filename repo_new_dir, repo_rename_file, validate_filename
from seahub.notifications.views import notification_list from seahub.notifications.views import notification_list
from seahub.share.views import share_admin from seahub.share.views import share_admin
from seahub.group.views import group_list from seahub.group.views import group_list
@@ -59,6 +58,8 @@ urlpatterns = patterns('',
# (r'^repo/removefetched/(?P<user_id>[^/]+)/(?P<repo_id>[^/]+)/$', remove_fetched_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/setap/(?P<repo_id>[^/]+)/$', repo_set_access_property),
(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file), (r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file),
(r'^repo/(?P<repo_id>[^/]+)/view/(?P<obj_id>[^/]+)/$', repo_view_file),
(r'^download/repo/$', repo_download), (r'^download/repo/$', repo_download),
(r'^file/move/get_subdir/$', get_subdir), (r'^file/move/get_subdir/$', get_subdir),
(r'^file/move/$', file_move), (r'^file/move/$', file_move),

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
import settings import settings
import os
import re import re
import time import time
import os import os
@@ -17,6 +18,9 @@ from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \
EMPTY_SHA1 = '0000000000000000000000000000000000000000' EMPTY_SHA1 = '0000000000000000000000000000000000000000'
import settings
from settings import PREVIEW_FILEEXT
def go_permission_error(request, msg=None): def go_permission_error(request, msg=None):
""" """
Return permisson error page. Return permisson error page.
@@ -225,3 +229,14 @@ def get_accessible_repos(request, repo):
repo.props.has_subdir = check_has_subdir(repo) repo.props.has_subdir = check_has_subdir(repo)
return accessible_repos return accessible_repos
def valid_previewed_file(filename):
"""
Check whether file can preview on web
"""
fileExt = os.path.splitext(filename)[1][1:]
for filetype in PREVIEW_FILEEXT.keys():
if fileExt in PREVIEW_FILEEXT.get(filetype):
return (True, filetype)
return (False, '')

105
views.py
View File

@@ -3,7 +3,9 @@ import settings
import os import os
import stat import stat
import simplejson as json import simplejson as json
import re
import sys import sys
import urllib2
from urllib import quote from urllib import quote
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.mail import send_mail from django.core.mail import send_mail
@@ -34,9 +36,10 @@ from seahub.notifications.models import UserNotification
from forms import AddUserForm from forms import AddUserForm
from utils import go_permission_error, go_error, list_to_string, \ from utils import go_permission_error, go_error, list_to_string, \
get_httpserver_root, get_ccnetapplet_root, gen_token, \ get_httpserver_root, get_ccnetapplet_root, gen_token, \
calculate_repo_last_modify, \ 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
from seahub.profile.models import Profile from seahub.profile.models import Profile
from settings import FILE_PREVIEW_MAX_SIZE
@login_required @login_required
def root(request): def root(request):
@@ -751,6 +754,104 @@ def repo_del_file(request, repo_id):
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir) url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@login_required
def repo_view_file(request, repo_id, obj_id):
http_server_root = get_httpserver_root()
filename = urllib2.quote(request.GET.get('file_name', '').encode('utf-8'))
if request.is_ajax():
content_type = 'application/json; charset=utf-8'
token = request.GET.get('t')
tmp_str = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s'
redirect_url = tmp_str % (http_server_root,
repo_id, obj_id,
filename, 'view',
token,
request.user.username)
try:
proxied_request = urllib2.urlopen(redirect_url)
if long(proxied_request.headers['Content-Length']) > FILE_PREVIEW_MAX_SIZE:
data = json.dumps([{'error': '文件超过10M无法在线查看。'}])
return HttpResponse(data, status=400, content_type=content_type)
else:
content = proxied_request.read()
except urllib2.HTTPError, e:
err = 'HTTPError: 无法在线打开该文件'
data = json.dumps([{'error': err}])
return HttpResponse(data, status=400, content_type=content_type)
except urllib2.URLError as e:
err = 'URLError: 无法在线打开该文件'
data = json.dumps([{'error': err}])
return HttpResponse(data, status=400, content_type=content_type)
else:
l, d = [], {}
try:
# XXX: file in windows is encoded in gbk
u_content = content.decode('gbk')
except:
u_content = content.decode('utf-8')
from django.utils.html import escape
d['content'] = re.sub("\r\n|\n", "<br />", escape(u_content))
l.append(d)
data = json.dumps(l)
return HttpResponse(data, status=200, content_type=content_type)
repo = get_repo(repo_id)
if not repo:
raise Http404
# if a repo doesn't have access property in db, then assume it's 'own'
repo_ap = seafserv_threaded_rpc.repo_query_access_property(repo_id)
if not repo_ap:
repo_ap = 'own'
# if a repo is shared to me, then I can view and download file no mater whether
# repo's access property is 'own' or 'public'
if check_shared_repo(request, repo_id):
share_to_me = True
else:
share_to_me = False
token = ''
if repo_ap == 'own':
# people who is owner or this repo is shared to him, can visit the repo;
# others, just go to 404 page
if validate_owner(request, repo_id) or share_to_me:
# owner should get a token to visit repo
token = gen_token()
# put token into memory in seaf-server
seafserv_rpc.web_save_access_token(token, obj_id)
else:
raise Http404
# generate path and link
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
zipped = gen_path_link(path, repo.name)
# filename
can_preview, filetype = valid_previewed_file(filename)
# raw path
tmp_str = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s'
raw_path = tmp_str % (http_server_root,
repo_id, obj_id,
filename, 'view',
token,
request.user.username)
return render_to_response('repo_view_file.html', {
'repo': repo,
'obj_id': obj_id,
'file_name': filename,
'zipped': zipped,
'token': token,
'can_preview': can_preview,
'filetype': filetype,
'raw_path': raw_path,
}, context_instance=RequestContext(request))
def repo_access_file(request, repo_id, obj_id): def repo_access_file(request, repo_id, obj_id):
if repo_id: if repo_id:
repo = get_repo(repo_id) repo = get_repo(repo_id)
@@ -807,7 +908,7 @@ def repo_access_file(request, repo_id, obj_id):
token, token,
request.user.username) request.user.username)
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)
@login_required @login_required
def repo_download(request): def repo_download(request):
repo_id = request.GET.get('repo_id', '') repo_id = request.GET.get('repo_id', '')