mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-31 22:54:11 +00:00
Add file preview feature
This commit is contained in:
@@ -582,3 +582,15 @@ table img {
|
||||
#upload-cancel {
|
||||
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+ */
|
||||
}
|
||||
|
@@ -170,6 +170,13 @@ USE_SUBDOMAIN = False
|
||||
# account type is `personal` or `business`
|
||||
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:
|
||||
import local_settings
|
||||
except ImportError:
|
||||
|
@@ -104,7 +104,7 @@
|
||||
{% for dirent in file_list %}
|
||||
<tr>
|
||||
<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>
|
||||
<img src="{{ MEDIA_URL }}img/more-option.png" alt="更多操作" class="more-op hide" />
|
||||
|
71
templates/repo_view_file.html
Normal file
71
templates/repo_view_file.html
Normal 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 %}
|
5
urls.py
5
urls.py
@@ -8,11 +8,10 @@ from seahub.views import root, peers, myhome, \
|
||||
activate_user, user_add, user_remove, sys_group_admin, sys_org_admin, \
|
||||
ownerhome, repo_history_dir, repo_history_revert, \
|
||||
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, \
|
||||
repo_upload_file, file_upload_progress, file_upload_progress_page, get_subdir, file_move, \
|
||||
repo_new_dir, repo_rename_file, validate_filename
|
||||
|
||||
from seahub.notifications.views import notification_list
|
||||
from seahub.share.views import share_admin
|
||||
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/setap/(?P<repo_id>[^/]+)/$', repo_set_access_property),
|
||||
(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'^file/move/get_subdir/$', get_subdir),
|
||||
(r'^file/move/$', file_move),
|
||||
|
15
utils.py
15
utils.py
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
import settings
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
@@ -17,6 +18,9 @@ from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \
|
||||
|
||||
EMPTY_SHA1 = '0000000000000000000000000000000000000000'
|
||||
|
||||
import settings
|
||||
from settings import PREVIEW_FILEEXT
|
||||
|
||||
def go_permission_error(request, msg=None):
|
||||
"""
|
||||
Return permisson error page.
|
||||
@@ -225,3 +229,14 @@ def get_accessible_repos(request, repo):
|
||||
repo.props.has_subdir = check_has_subdir(repo)
|
||||
|
||||
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
105
views.py
@@ -3,7 +3,9 @@ import settings
|
||||
import os
|
||||
import stat
|
||||
import simplejson as json
|
||||
import re
|
||||
import sys
|
||||
import urllib2
|
||||
from urllib import quote
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.mail import send_mail
|
||||
@@ -34,9 +36,10 @@ from seahub.notifications.models import UserNotification
|
||||
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, \
|
||||
calculate_repo_last_modify, valid_previewed_file, \
|
||||
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1
|
||||
from seahub.profile.models import Profile
|
||||
from settings import FILE_PREVIEW_MAX_SIZE
|
||||
|
||||
@login_required
|
||||
def root(request):
|
||||
@@ -751,6 +754,104 @@ def repo_del_file(request, repo_id):
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
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):
|
||||
if repo_id:
|
||||
repo = get_repo(repo_id)
|
||||
@@ -807,7 +908,7 @@ def repo_access_file(request, repo_id, obj_id):
|
||||
token,
|
||||
request.user.username)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
@login_required
|
||||
def repo_download(request):
|
||||
repo_id = request.GET.get('repo_id', '')
|
||||
|
Reference in New Issue
Block a user