diff --git a/media/css/seahub.css b/media/css/seahub.css index 2df5768d74..66e373ff3a 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -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+ */ +} diff --git a/settings.py b/settings.py index 34e3222aa9..10f65a1ba0 100644 --- a/settings.py +++ b/settings.py @@ -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: diff --git a/templates/repo.html b/templates/repo.html index 0f512c3dc1..fecd5af62a 100644 --- a/templates/repo.html +++ b/templates/repo.html @@ -104,7 +104,7 @@ {% for dirent in file_list %} 文件 - {{ dirent.props.obj_name }} + {{ dirent.props.obj_name }} {{ dirent.file_size|filesizeformat }} 更多操作 diff --git a/templates/repo_view_file.html b/templates/repo_view_file.html new file mode 100644 index 0000000000..f9640015d1 --- /dev/null +++ b/templates/repo_view_file.html @@ -0,0 +1,71 @@ +{% extends "myhome_base.html" %} +{% load seahub_tags %} + +{% block info_bar_message %} +{% if request.user.is_authenticated %} + {{ block.super }} +{% else %} +
+ 当前链接会在短期内失效,欢迎您 加入Seafile 体验更多功能。 +
+{% endif %} +{% endblock %} + +{% block main_panel %} +

+ {{repo.props.name}} +

+ +
+

操作

+

查看原始文件

+

下载文件

+
+ +
+

+ 当前路径: + {% for name, link in zipped %} + {% if not forloop.last %} + {{ name }} / + {% else %} + {{ name }} + {% endif %} + {% endfor %} +

+
正在读取文件内容...
+
+{% endblock %} + +{% block extra_script %} + +{% endblock %} diff --git a/urls.py b/urls.py index 168db59d8d..bce26d81b6 100644 --- a/urls.py +++ b/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[^/]+)/(?P[^/]+)/$', remove_fetched_repo), # (r'^repo/setap/(?P[^/]+)/$', repo_set_access_property), (r'^repo/(?P[^/]+)/(?P[^/]+)/$', repo_access_file), + (r'^repo/(?P[^/]+)/view/(?P[^/]+)/$', repo_view_file), + (r'^download/repo/$', repo_download), (r'^file/move/get_subdir/$', get_subdir), (r'^file/move/$', file_move), diff --git a/utils.py b/utils.py index a761a1c2a7..924f75cc2d 100644 --- a/utils.py +++ b/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, '') diff --git a/views.py b/views.py index 83460d0edf..92f732faaa 100644 --- a/views.py +++ b/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", "
", 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', '')