1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-05 00:43:53 +00:00

Add file shared link, and modify url viewing file

This commit is contained in:
xiez
2012-07-11 22:39:36 +08:00
parent c3296df1c1
commit d66938cce0
10 changed files with 547 additions and 85 deletions

View File

@@ -32,3 +32,13 @@ class AddUserForm(forms.Form):
if self.cleaned_data['password1'] != self.cleaned_data['password2']: if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError(_("The two password fields didn't match.")) raise forms.ValidationError(_("The two password fields didn't match."))
return self.cleaned_data return self.cleaned_data
class FileLinkShareForm(forms.Form):
"""
Form for sharing file shared link to emails.
"""
email = forms.CharField(max_length=512)
file_shared_link = forms.CharField(max_length=40)

View File

@@ -466,6 +466,7 @@ table img {
margin-top:8px; margin-top:8px;
} }
/*repo-share-form*/ /*repo-share-form*/
#email,
#email_or_group, #email_or_group,
#share-link, #share-link,
#added-member-name { #added-member-name {
@@ -653,3 +654,4 @@ table img {
max-width: 550px; max-width: 550px;
max-height: 550px; max-height: 550px;
} }

View File

@@ -1,8 +1,21 @@
import datetime
from django.db import models from django.db import models
class AnonymousShare(models.Model): class AnonymousShare(models.Model):
"""
Model used for sharing repo to unregistered email.
"""
repo_owner = models.EmailField(max_length=255) repo_owner = models.EmailField(max_length=255)
repo_id = models.CharField(max_length=36) repo_id = models.CharField(max_length=36)
anonymous_email = models.EmailField(max_length=255) anonymous_email = models.EmailField(max_length=255)
token = models.CharField(max_length=25, unique=True) token = models.CharField(max_length=25, unique=True)
class FileShare(models.Model):
"""
Model used for file share link.
"""
username = models.EmailField(max_length=255)
repo_id = models.CharField(max_length=36, db_index=True)
path = models.TextField()
token = models.CharField(max_length=10, unique=True)
ctime = models.DateTimeField(default=datetime.datetime.now)

View File

@@ -153,9 +153,9 @@
<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>
{% if view_history %} {% if view_history %}
<td><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/view/{{ dirent.props.obj_id }}/?commit_id={{ current_commit.id }}&file_name={{ dirent.props.obj_name }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.props.obj_name }}</a></td> <td><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/files/?obj_id={{ dirent.props.obj_id }}&commit_id={{ current_commit.id }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.props.obj_name }}</a></td>
{% else %} {% else %}
<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><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/files/?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.props.obj_name }}</a></td>
{% endif %} {% endif %}
<td>{{ dirent.file_size|filesizeformat }}</td> <td>{{ dirent.file_size|filesizeformat }}</td>

View File

@@ -25,6 +25,17 @@
<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=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/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> <p><a href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download" target="_blank">下载文件</a></p>
{% if not view_history %}
<h3>共享</h3>
<p class="shared-link">
<span class="hide">链接:{{ file_shared_link }}</span>
<a href="#" data="{{ SITE_ROOT }}sharedlink/get/?repo_id={{ repo.id }}&p={{ path|urlencode }}&file_name={{ file_name }}" class="get-shared-link">获取分享链接</a></p>
<div class="link-op hide">
<a href="#" class="send-shared-link">发送</a>
<a href="#" data="{{ SITE_ROOT }}sharedlink/remove/?t={{ fileshare.token }}" class="remove-shared-link">删除</a>
</div>
{% endif %}
</div> </div>
<div class="main fleft"> <div class="main fleft">
@@ -44,17 +55,53 @@
</p> </p>
<pre id="file-content">正在读取文件内容...</pre> <pre id="file-content">正在读取文件内容...</pre>
</div> </div>
<form id="link-send-form" action="" method="post" name="link-send-form" class="hide">
<label>邮箱:</label><br />
<textarea id="email" name="email"></textarea><br />
<input id="file_shared_link" type="hidden" name="file_shared_link" value="{{ file_shared_link }}" />
<p class="error hide">输入不能为空。</p>
<p class="success hide"></p>
<p class="sending hide">发送中...</p>
<input type="submit" value="提交" id="share-submit-btn" />
</form>
{% endblock %} {% endblock %}
{% block extra_script %} {% block extra_script %}
<script type="text/javascript"> <script type="text/javascript">
$(window).load(function() { function showLink() {
$('.shared-link a').addClass('hide');
$('.shared-link span').removeClass('hide');
$('.link-op').removeClass('hide');
}
function hideLink() {
$('.shared-link span').addClass('hide');
$('.link-op').addClass('hide');
$('.shared-link a').removeClass('hide');
}
$(window).load(function() {
var can_preview = "{{ can_preview }}"; var can_preview = "{{ can_preview }}";
var view_history = "{{ view_history }}";
var url = "";
if (view_history == 'True') {
url = "{{ SITE_ROOT }}repo/{{ repo.id }}/file/get/?obj_id={{ obj_id }}&p={{ path }}&t={{ token }}&u={{ request.user.username }}";
} else {
url = "{{ SITE_ROOT }}repo/{{ repo.id }}/file/get/?p={{ path }}&t={{ token }}&u={{ request.user.username }}";
}
var filetype = "{{ filetype }}"; var filetype = "{{ filetype }}";
var t = "{{ fileshare.token }}";
if (t) {
showLink();
} else {
hideLink();
}
if (can_preview == 'True' && filetype == 'Document') { if (can_preview == 'True' && filetype == 'Document') {
$.ajax({ $.ajax({
url: '{{ SITE_ROOT }}repo/{{ repo.id }}/view/{{ obj_id }}/?file_name={{ file_name }}&t={{ token }}', url: url,
dataType: 'json', dataType: 'json',
cache: false, cache: false,
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
@@ -69,12 +116,119 @@
} }
}); });
return false; return false;
} else if (can_preview == 'True' && filetype == 'Image') { } else if (can_preview == 'True' && filetype == 'Image') {
$('#file-content').replaceWith('<img class="img-preview" src="{{ raw_path }}"></img>'); $('#file-content').replaceWith('<img class="img-preview" src="{{ raw_path }}"></img>');
} else { } else {
$('#file-content').html('无法识别该文件格式,<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download">下载文件</a>。'); $('#file-content').html('无法识别该文件格式,<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download">下载文件</a>。');
} }
}); });
$('.get-shared-link').click(function() {
var url = $(this).attr('data');
$.ajax({
url: url,
dataType: 'json',
cache: false,
contentType: 'application/json; charset=utf-8',
success: function(data) {
if (data.length > 0) {
var t = data[0]['token'];
var shared_link = '{{ protocol }}://{{ domain }}{{ SITE_ROOT }}f/' + t + '/';
$('.shared-link span').html('链接:' + shared_link);
$('.remove-shared-link').attr('data', '{{ SITE_ROOT }}sharedlink/remove/?t='+t);
showLink();
}
},
error: function(xhr, ajaxOptions, thrownError) {
var jsonVal = jQuery.parseJSON(xhr.responseText);
$('.get-shared-link').html(jsonVal[0]['error']);
}
});
return false;
});
$('.remove-shared-link').click(function() {
var url = $(this).attr('data');
$.ajax({
url: url,
dataType: 'json',
cache: false,
contentType: 'application/json; charset=utf-8',
success: function(data) {
hideLink();
}
});
return false;
});
$('.send-shared-link').click(function() {
$("#link-send-form").modal({appendTo: "#main"});
});
$("#link-send-form").submit(function(event) {
// clear error and sucess msg
$('.error').html("").addClass('hide');
$('.success').html("").addClass('hide');
$('.sending').removeClass('hide');
$('#simplemodal-container').css('height', $('#link-send-form').height());
var form = $(this),
file_shared_link = form.children('input[name="file_shared_link"]').val(),
email = form.children('textarea[name="email"]').val();
if (email && email.length <= 512) {
// prepare django csrf token
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
$.ajax({
type: "POST",
url: "{{ SITE_ROOT }}sharedlink/send/",
dataType: 'json',
cache: false,
contentType: 'application/json; charset=utf-8',
data: "file_shared_link="+file_shared_link+"&email="+email,
success: function(data) {
form.children('.error').addClass('hide');
form.children('.sending').addClass('hide');
form.children('.success').html(data[0]['msg']).removeClass('hide');
$('#simplemodal-container').css('height', $('#link-send-form').height());
},
error: function(xhr, ajaxOptions, thrownError) {
var jsonVal = jQuery.parseJSON(xhr.responseText);
form.children('.success').addClass('hide');
form.children('.sending').addClass('hide');
form.children('.error').html(jsonVal[0]['error']).removeClass('hide');
$('#simplemodal-container').css('height', $('#link-send-form').height());
}
});
} else {
form.children('.success').html("").addClass('hide');
form.children('.sending').addClass('hide');
form.children('.error').removeClass('hide');
}
return false;
});
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,12 @@
{% autoescape off %}
亲爱的 {{ to_email }}
{{ email }} 在SeaCloud上共享了一个文件给你请点击以下链接查看
{{ file_shared_link }}
感谢使用我们的网站!
Seafile团队
{% endautoescape %}

View File

@@ -0,0 +1,54 @@
{% extends "myhome_base.html" %}
{% load seahub_tags %}
{% block main_panel %}
<h2 class="subject">
{{ file_name }}<br />
<span class="latest-commit-time-author">共享者:{{ username }}</span>
</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">
<pre id="file-content">正在读取文件内容...</pre>
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
$(window).load(function() {
var can_preview = "{{ can_preview }}";
var url = "{{ SITE_ROOT }}repo/{{ repo.id }}/file/get/?t={{ access_token }}&p={{ path }}&u={{ username }}";
var filetype = "{{ filetype }}";
if (can_preview == 'True' && filetype == 'Document') {
$.ajax({
url: url,
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 class="img-preview" src="{{ raw_path }}"></img>');
} else {
$('#file-content').html('无法识别该文件格式,<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=download">下载文件</a>。');
}
});
</script>
{% endblock %}

18
urls.py
View File

@@ -6,13 +6,14 @@ from seahub.views import root, peers, myhome, \
repo, repo_history, modify_token, remove_repo, sys_seafadmin, sys_useradmin, \ repo, repo_history, modify_token, remove_repo, sys_seafadmin, sys_useradmin, \
org_seafadmin, org_useradmin, org_group_admin, org_remove, \ org_seafadmin, org_useradmin, org_group_admin, org_remove, \
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_revert, \ ownerhome, repo_history_revert, repo_file_get, \
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_view_file, \ 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, \
repo_new_dir, repo_rename_file, validate_filename, \ get_subdir, file_move, repo_new_dir, repo_rename_file, validate_filename, \
repo_create, repo_update_file, file_revisions repo_create, repo_update_file, file_revisions, \
get_shared_link, view_shared_file, remove_shared_link, send_shared_link
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
@@ -42,7 +43,11 @@ urlpatterns = patterns('',
(r'^share/', include('share.urls')), (r'^share/', include('share.urls')),
url(r'^shareadmin/$', share_admin, name='share_admin'), url(r'^shareadmin/$', share_admin, name='share_admin'),
(r'^shareadmin/removeshare/$', repo_remove_share), (r'^shareadmin/removeshare/$', repo_remove_share),
(r'^sharedlink/get/$', get_shared_link),
(r'^sharedlink/remove/$', remove_shared_link),
(r'^sharedlink/send/$', send_shared_link),
(r'^f/(?P<token>[^/]+)/$', view_shared_file),
(r'^file_upload_progress/$', file_upload_progress), (r'^file_upload_progress/$', file_upload_progress),
(r'^file_upload_progress_page/$', file_upload_progress_page), (r'^file_upload_progress_page/$', file_upload_progress_page),
(r'^repo/new_dir/$', repo_new_dir), (r'^repo/new_dir/$', repo_new_dir),
@@ -61,7 +66,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),
url(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file, name='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'^repo/(?P<repo_id>[^/]+)/files/$', repo_view_file),
(r'^repo/(?P<repo_id>[^/]+)/file/get/$', repo_file_get),
(r'^download/repo/$', repo_download), (r'^download/repo/$', repo_download),
(r'^file/move/get_subdir/$', get_subdir), (r'^file/move/get_subdir/$', get_subdir),

View File

@@ -68,13 +68,13 @@ def get_ccnetapplet_root():
ccnet_applet_root = settings.CCNET_APPLET_ROOT ccnet_applet_root = settings.CCNET_APPLET_ROOT
return ccnet_applet_root return ccnet_applet_root
def gen_token(): def gen_token(max_length=5):
""" """
Generate short token used for owner to access repo file. Generate a random token.
""" """
token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[:5] token = sha_constructor(settings.SECRET_KEY + unicode(time.time())).hexdigest()[:max_length]
return token return token
def validate_group_name(group_name): def validate_group_name(group_name):

343
views.py
View File

@@ -24,6 +24,7 @@ from auth.decorators import login_required
from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, \ from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, \
PasswordChangeForm PasswordChangeForm
from auth.tokens import default_token_generator from auth.tokens import default_token_generator
from share.models import FileShare
from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_groups, get_users, get_repos, \ from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_groups, get_users, get_repos, \
get_repo, get_commits, get_branches, \ get_repo, get_commits, get_branches, \
seafserv_threaded_rpc, seafserv_rpc, get_binding_peerids, get_ccnetuser, \ seafserv_threaded_rpc, seafserv_rpc, get_binding_peerids, get_ccnetuser, \
@@ -33,7 +34,7 @@ from pysearpc import SearpcError
from seahub.base.accounts import CcnetUser from seahub.base.accounts import CcnetUser
from seahub.contacts.models import Contact from seahub.contacts.models import Contact
from seahub.notifications.models import UserNotification from seahub.notifications.models import UserNotification
from forms import AddUserForm from forms import AddUserForm, FileLinkShareForm
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, valid_previewed_file, \ calculate_repo_last_modify, valid_previewed_file, \
@@ -147,8 +148,9 @@ def gen_path_link(path, repo_name):
Generate navigate paths and links in repo page. Generate navigate paths and links in repo page.
""" """
if path[-1:] != '/': if path and path[-1] != '/':
path += '/' path += '/'
paths = [] paths = []
links = [] links = []
if path and path != '/': if path and path != '/':
@@ -210,7 +212,6 @@ def render_repo(request, repo_id, error=''):
# query repo infomation # query repo infomation
repo_size = seafserv_threaded_rpc.server_repo_size(repo_id) repo_size = seafserv_threaded_rpc.server_repo_size(repo_id)
# latest_commit = get_commits(repo_id, 0, 1)[0]
# get repo dirents # get repo dirents
dirs = [] dirs = []
@@ -789,61 +790,37 @@ 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)
def repo_view_file(request, repo_id, obj_id): def repo_view_file(request, repo_id):
"""
Preview file on web, including files in current worktree and history.
"""
http_server_root = get_httpserver_root() http_server_root = get_httpserver_root()
filename = urllib2.quote(request.GET.get('file_name', '').encode('utf-8')) path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
filename = urllib2.quote(os.path.basename(path[:-1]).encode('utf-8'))
commit_id = request.GET.get('commit_id', '') commit_id = request.GET.get('commit_id', '')
view_history = True if commit_id else False view_history = True if commit_id else False
current_commit = seafserv_threaded_rpc.get_commit(commit_id) current_commit = seafserv_threaded_rpc.get_commit(commit_id)
if not current_commit: if not current_commit:
current_commit = get_commits(repo_id, 0, 1)[0] current_commit = get_commits(repo_id, 0, 1)[0]
if request.is_ajax(): if view_history:
content_type = 'application/json; charset=utf-8' obj_id = request.GET.get('obj_id', '')
token = request.GET.get('t') else:
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: try:
proxied_request = urllib2.urlopen(redirect_url) obj_id = seafserv_rpc.get_file_by_path(repo_id, path[:-1])
if long(proxied_request.headers['Content-Length']) > FILE_PREVIEW_MAX_SIZE: except:
data = json.dumps([{'error': '文件超过10M无法在线查看。'}]) obj_id = None
return HttpResponse(data, status=400, content_type=content_type)
else: if not obj_id:
content = proxied_request.read() return go_error(request, '文件不存在')
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) repo = get_repo(repo_id)
if not repo: if not repo:
raise Http404 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 # 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' # repo's access property is 'own' or 'public'
if check_shared_repo(request, repo_id): if check_shared_repo(request, repo_id):
@@ -852,30 +829,22 @@ def repo_view_file(request, repo_id, obj_id):
share_to_me = False share_to_me = False
token = '' token = ''
if repo_ap == 'own': # people who is owner or this repo is shared to him, can visit the repo;
# people who is owner or this repo is shared to him, can visit the repo; # others, just go to 404 page
# others, just go to 404 page if validate_owner(request, repo_id) or share_to_me:
if validate_owner(request, repo_id) or share_to_me: # owner should get a token to visit repo
# owner should get a token to visit repo token = gen_token()
token = gen_token() # put token into memory in seaf-server
# put token into memory in seaf-server seafserv_rpc.web_save_access_token(token, obj_id)
seafserv_rpc.web_save_access_token(token, obj_id) else:
else: raise Http404
raise Http404
# query commit info
commit_id = request.GET.get('commit_id', None)
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
if not current_commit:
current_commit = get_commits(repo.id, 0, 1)[0]
# generate path and link # generate path and link
path = request.GET.get('p', '/')
zipped = gen_path_link(path, repo.name) zipped = gen_path_link(path, repo.name)
# filename # determin whether file can preview on web
can_preview, filetype = valid_previewed_file(filename) can_preview, filetype = valid_previewed_file(filename)
# raw path # raw path
tmp_str = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s' tmp_str = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s'
raw_path = tmp_str % (http_server_root, raw_path = tmp_str % (http_server_root,
@@ -883,12 +852,26 @@ def repo_view_file(request, repo_id, obj_id):
filename, 'view', filename, 'view',
token, token,
request.user.username) request.user.username)
# file share link
l = FileShare.objects.filter(repo_id=repo_id).filter(path=path[:-1])
fileshare = l[0] if len(l) > 0 else None
http_or_https = request.is_secure() and 'https' or 'http'
domain = RequestSite(request).domain
if fileshare:
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain,
settings.SITE_ROOT,
fileshare.token)
else:
file_shared_link = ''
return render_to_response('repo_view_file.html', { return render_to_response('repo_view_file.html', {
'repo': repo, 'repo': repo,
'path': path, 'path': path,
'obj_id': obj_id, 'obj_id': obj_id,
'file_name': filename, 'file_name': filename,
'path': path,
'zipped': zipped, 'zipped': zipped,
'view_history': view_history, 'view_history': view_history,
'current_commit': current_commit, 'current_commit': current_commit,
@@ -896,8 +879,76 @@ def repo_view_file(request, repo_id, obj_id):
'can_preview': can_preview, 'can_preview': can_preview,
'filetype': filetype, 'filetype': filetype,
'raw_path': raw_path, 'raw_path': raw_path,
'fileshare': fileshare,
'protocol': http_or_https,
'domain': domain,
'file_shared_link': file_shared_link,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def repo_file_get(request, repo_id):
"""
Handle ajax request to get file content from httpserver.
If get current worktree file, need access_token, path and username from
url params.
If get history file, need access_token, path username and obj_id from
url params.
"""
if not request.is_ajax():
return Http404
http_server_root = get_httpserver_root()
content_type = 'application/json; charset=utf-8'
access_token = request.GET.get('t')
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
filename = urllib2.quote(os.path.basename(path).encode('utf-8'))
obj_id = request.GET.get('obj_id', '')
if not obj_id:
try:
obj_id = seafserv_rpc.get_file_by_path(repo_id, path)
except:
obj_id = None
if not obj_id:
data = json.dumps([{'error': '获取文件数据失败'}])
return HttpResponse(data, status=400, content_type=content_type)
username = request.GET.get('u', '')
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',
access_token,
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)
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)
@@ -1756,3 +1807,163 @@ def file_revisions(request, repo_id):
% (commit_id, file_name, path) % (commit_id, file_name, path)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@login_required
def get_shared_link(request):
"""
Handle ajax request to generate file shared link.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
repo_id = request.GET.get('repo_id')
obj_id = request.GET.get('obj_id')
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
l = FileShare.objects.filter(repo_id=repo_id).filter(path=path)
if len(l) > 0:
fileshare = l[0]
token = fileshare.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
try:
fs.save()
except IntegrityError, e:
err = '获取分享链接失败,请重新获取'
data = json.dumps([{'error': err}])
return HttpResponse(data, status=500, content_type=content_type)
data = json.dumps([{'token': token}])
return HttpResponse(data, status=200, content_type=content_type)
def view_shared_file(request, token):
"""
Preview file via shared link.
"""
assert token is not None # Checked by URLconf
try:
fileshare = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
raise Http404
username = fileshare.username
repo_id = fileshare.repo_id
path = fileshare.path
http_server_root = get_httpserver_root()
if path[-1] == '/':
path = path[:-1]
filename = os.path.basename(path)
quote_filename = urllib2.quote(filename.encode('utf-8'))
try:
obj_id = seafserv_rpc.get_file_by_path(repo_id, path)
except:
obj_id = None
if not obj_id:
return go_error(request, '文件不存在')
repo = get_repo(repo_id)
if not repo:
raise Http404
access_token = gen_token()
seafserv_rpc.web_save_access_token(access_token, obj_id)
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,
quote_filename, 'view',
access_token,
username)
return render_to_response('view_shared_file.html', {
'repo': repo,
'obj_id': obj_id,
'path': path,
'file_name': filename,
'token': token,
'access_token': access_token,
'can_preview': can_preview,
'filetype': filetype,
'raw_path': raw_path,
'username': username,
}, context_instance=RequestContext(request))
@login_required
def remove_shared_link(request):
"""
Handle ajax request to remove file shared link.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
token = request.GET.get('t', '')
FileShare.objects.filter(token=token).delete()
msg = '删除成功'
data = json.dumps([{'msg': msg}])
return HttpResponse(data, status=200, content_type=content_type)
@login_required
def send_shared_link(request):
"""
Handle ajax post request to share file shared link.
"""
if not request.is_ajax() and not request.method == 'POST':
raise Http404
content_type = 'application/json; charset=utf-8'
form = FileLinkShareForm(request.POST)
if not form.is_valid():
err = '发送失败'
data = json.dumps([{'error':err}])
return HttpResponse(data, status=400, content_type=content_type)
email = form.cleaned_data['email']
file_shared_link = form.cleaned_data['file_shared_link']
# Handle the diffent separator
to_email_str = email.replace(';',',')
to_email_str = to_email_str.replace('\n',',')
to_email_str = to_email_str.replace('\r',',')
to_email_list = to_email_str.split(',')
t = loader.get_template('shared_link_email.html')
for to_email in to_email_list:
c = {
'email': request.user.username,
'to_email': to_email,
'file_shared_link': file_shared_link,
}
try:
send_mail('您的好友通过SeaCloud分享了一个文件给您',
t.render(Context(c)), None, [to_email],
fail_silently=False)
except:
err = '发送失败'
data = json.dumps([{'error':err}])
return HttpResponse(data, status=400, content_type=content_type)
msg = '发送成功。'
data = json.dumps([{'msg': msg}])
return HttpResponse(data, status=200, content_type=content_type)