mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-12 04:12:16 +00:00
Merge branch 'feature/share_dir'
Conflicts: utils/__init__.py views.py
This commit is contained in:
commit
37eacd22c3
@ -896,10 +896,14 @@ textarea:-moz-placeholder {/* for FF */
|
|||||||
.repo-op .op-btn {
|
.repo-op .op-btn {
|
||||||
*margin-left:5px;/* for ie 7*/
|
*margin-left:5px;/* for ie 7*/
|
||||||
}
|
}
|
||||||
#upload-file {
|
#upload-file{
|
||||||
padding-left:19px;
|
padding-left:19px;
|
||||||
background-image:url('../img/upload.png?v=1');
|
background-image:url('../img/upload.png?v=1');
|
||||||
}
|
}
|
||||||
|
#download-dir {
|
||||||
|
padding-left:19px;
|
||||||
|
background-image:url('../img/download-blue.png?t=1352500800');
|
||||||
|
}
|
||||||
#add-new-dir {
|
#add-new-dir {
|
||||||
padding-left:23px;
|
padding-left:23px;
|
||||||
background-image:url('../img/folder-add.png');
|
background-image:url('../img/folder-add.png');
|
||||||
@ -1364,7 +1368,8 @@ textarea:-moz-placeholder {/* for FF */
|
|||||||
color:#444;
|
color:#444;
|
||||||
text-align:right;
|
text-align:right;
|
||||||
}
|
}
|
||||||
#shared-link {
|
#shared-link,
|
||||||
|
#shared-link-text {
|
||||||
border:0;
|
border:0;
|
||||||
}
|
}
|
||||||
.file-op {
|
.file-op {
|
||||||
|
BIN
media/img/download-blue.png
Normal file
BIN
media/img/download-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 3.1 KiB |
@ -12,7 +12,7 @@ class AnonymousShare(models.Model):
|
|||||||
|
|
||||||
class FileShare(models.Model):
|
class FileShare(models.Model):
|
||||||
"""
|
"""
|
||||||
Model used for file share link.
|
Model used for file or dir shared link.
|
||||||
"""
|
"""
|
||||||
username = models.EmailField(max_length=255, db_index=True)
|
username = models.EmailField(max_length=255, db_index=True)
|
||||||
repo_id = models.CharField(max_length=36, db_index=True)
|
repo_id = models.CharField(max_length=36, db_index=True)
|
||||||
@ -20,3 +20,4 @@ class FileShare(models.Model):
|
|||||||
token = models.CharField(max_length=10, unique=True)
|
token = models.CharField(max_length=10, unique=True)
|
||||||
ctime = models.DateTimeField(default=datetime.datetime.now)
|
ctime = models.DateTimeField(default=datetime.datetime.now)
|
||||||
view_cnt = models.IntegerField(default=0)
|
view_cnt = models.IntegerField(default=0)
|
||||||
|
s_type = models.CharField(max_length=2, db_index=True, default='f') # `f` or `d`
|
||||||
|
@ -80,18 +80,22 @@
|
|||||||
{% if fileshares %}
|
{% if fileshares %}
|
||||||
<table class="sharelink-list">
|
<table class="sharelink-list">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="45%">{% trans "File"%}</th>
|
<th width="45%">{% trans "File or Folder"%}</th>
|
||||||
<th width="25%">{% trans "Library"%}</th>
|
<th width="25%">{% trans "Library"%}</th>
|
||||||
<th width="15%">{% trans "Visits"%}</th>
|
<th width="15%">{% trans "Visits"%}</th>
|
||||||
<th width="15%">{% trans "Operations"%}</th>
|
<th width="15%">{% trans "Operations"%}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for fs in fileshares %}
|
{% for fs in fileshares %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ SITE_ROOT }}repo/{{ fs.repo.id }}/files/?p={{ fs.path|urlencode }}">{{ fs.filename }}</a></td>
|
{% if fs.s_type == 'f' %}
|
||||||
|
<td><a href="{% url 'repo_view_file' fs.repo.id %}?p={{ fs.path|urlencode }}">{{ fs.filename }}</a></td>
|
||||||
|
{% else %}
|
||||||
|
<td><a href="{% url 'repo' fs.repo.id %}?p={{ fs.path|urlencode }}">{{ fs.filename }}</a></td>
|
||||||
|
{% endif %}
|
||||||
<td><a href="{{ SITE_ROOT }}repo/{{ fs.repo.id }}/">{{ fs.repo.name }}</a></td>
|
<td><a href="{{ SITE_ROOT }}repo/{{ fs.repo.id }}/">{{ fs.repo.name }}</a></td>
|
||||||
<td>{{ fs.view_cnt }}</td>
|
<td>{{ fs.view_cnt }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" class="op view-link" data="{{ fs.token }}">{% trans "View" %}</a>
|
<a href="#" class="op view-link" data="{{ fs.shared_link }}">{% trans "View" %}</a>
|
||||||
<a class="op" href="{% url 'remove_shared_link' %}?t={{ fs.token }}">{% trans "Remove"%}</a>
|
<a class="op" href="{% url 'remove_shared_link' %}?t={{ fs.token }}">{% trans "Remove"%}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -173,8 +177,7 @@ $('.cancel-share').click(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(".view-link").click(function() {
|
$(".view-link").click(function() {
|
||||||
var token = $(this).attr('data'),
|
var link = $(this).attr('data');
|
||||||
link = '{{ protocol }}://' + '{{ domain }}{{ SITE_ROOT }}f/' + token + '/';
|
|
||||||
$('#link').before('<p class="hide">' + link + '</p>');
|
$('#link').before('<p class="hide">' + link + '</p>');
|
||||||
$('#shared-link').val(link).css('width', $('#link').prev().width() + 2);
|
$('#shared-link').val(link).css('width', $('#link').prev().width() + 2);
|
||||||
$("#link").modal({appendTo:'#main'});
|
$("#link").modal({appendTo:'#main'});
|
||||||
|
@ -3,7 +3,8 @@ import os
|
|||||||
import simplejson as json
|
import simplejson as json
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
from django.http import HttpResponse, HttpResponseRedirect, Http404, \
|
||||||
|
HttpResponseBadRequest
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import Context, loader, RequestContext
|
from django.template import Context, loader, RequestContext
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
@ -25,12 +26,13 @@ from seahub.contacts.signals import mail_sended
|
|||||||
from seahub.share.models import FileShare
|
from seahub.share.models import FileShare
|
||||||
from seahub.views import validate_owner, is_registered_user
|
from seahub.views import validate_owner, is_registered_user
|
||||||
from seahub.utils import render_permission_error, string2list, render_error, \
|
from seahub.utils import render_permission_error, string2list, render_error, \
|
||||||
gen_token
|
gen_token, gen_shared_link
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import CLOUD_MODE
|
from seahub.settings import CLOUD_MODE
|
||||||
except ImportError:
|
except ImportError:
|
||||||
CLOUD_MODE = False
|
CLOUD_MODE = False
|
||||||
|
from seahub.settings import SITE_ROOT
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def share_repo(request):
|
def share_repo(request):
|
||||||
@ -176,7 +178,6 @@ def repo_remove_share(request):
|
|||||||
|
|
||||||
next = request.META.get('HTTP_REFERER', None)
|
next = request.META.get('HTTP_REFERER', None)
|
||||||
if not next:
|
if not next:
|
||||||
from seahub.settings import SITE_ROOT
|
|
||||||
next = SITE_ROOT
|
next = SITE_ROOT
|
||||||
|
|
||||||
return HttpResponseRedirect(next)
|
return HttpResponseRedirect(next)
|
||||||
@ -233,13 +234,17 @@ def share_admin(request):
|
|||||||
# link.repo_name = repo.name
|
# link.repo_name = repo.name
|
||||||
# link.remain_time = anon_share_token_generator.get_remain_time(link.token)
|
# link.remain_time = anon_share_token_generator.get_remain_time(link.token)
|
||||||
|
|
||||||
# File shared links
|
# Shared links
|
||||||
fileshares = FileShare.objects.filter(username=username)
|
fileshares = FileShare.objects.filter(username=username)
|
||||||
p_fileshares = [] # personal file share
|
p_fileshares = [] # personal file share
|
||||||
for fs in fileshares:
|
for fs in fileshares:
|
||||||
if is_personal_repo(fs.repo_id):
|
if is_personal_repo(fs.repo_id): # only list files in personal repos
|
||||||
# only list files in personal repos
|
if fs.s_type == 'f':
|
||||||
fs.filename = os.path.basename(fs.path)
|
fs.filename = os.path.basename(fs.path)
|
||||||
|
fs.shared_link = gen_shared_link(request, fs.token, 'f')
|
||||||
|
else:
|
||||||
|
fs.filename = os.path.basename(fs.path[:-1])
|
||||||
|
fs.shared_link = gen_shared_link(request, fs.token, 'd')
|
||||||
fs.repo = get_repo(fs.repo_id)
|
fs.repo = get_repo(fs.repo_id)
|
||||||
p_fileshares.append(fs)
|
p_fileshares.append(fs)
|
||||||
|
|
||||||
@ -248,8 +253,6 @@ def share_admin(request):
|
|||||||
"shared_repos": shared_repos,
|
"shared_repos": shared_repos,
|
||||||
# "out_links": out_links,
|
# "out_links": out_links,
|
||||||
"fileshares": p_fileshares,
|
"fileshares": p_fileshares,
|
||||||
"protocol": request.is_secure() and 'https' or 'http',
|
|
||||||
"domain": RequestSite(request).domain,
|
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -371,17 +374,32 @@ def remove_anonymous_share(request, token):
|
|||||||
@login_required
|
@login_required
|
||||||
def get_shared_link(request):
|
def get_shared_link(request):
|
||||||
"""
|
"""
|
||||||
Handle ajax request to generate file shared link.
|
Handle ajax request to generate file or dir shared link.
|
||||||
"""
|
"""
|
||||||
if not request.is_ajax():
|
if not request.is_ajax():
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
content_type = 'application/json; charset=utf-8'
|
content_type = 'application/json; charset=utf-8'
|
||||||
|
|
||||||
repo_id = request.GET.get('repo_id')
|
repo_id = request.GET.get('repo_id', '')
|
||||||
path = request.GET.get('p', '/')
|
share_type = request.GET.get('type', 'f') # `f` or `d`
|
||||||
if path[-1] == '/':
|
path = request.GET.get('p', '')
|
||||||
path = path[:-1]
|
if not (repo_id and path):
|
||||||
|
err = _('Invalid arguments')
|
||||||
|
data = json.dumps([{'error': err}])
|
||||||
|
return HttpResponse(data, status=400, content_type=content_type)
|
||||||
|
|
||||||
|
if share_type == 'f':
|
||||||
|
if path[-1] == '/': # cut out last '/' at end of path
|
||||||
|
path = path[:-1]
|
||||||
|
else:
|
||||||
|
if path == '/': # can not share root dir
|
||||||
|
err = _('Can not share root dir.')
|
||||||
|
data = json.dumps([{'error': err}])
|
||||||
|
return HttpResponse(data, status=400, content_type=content_type)
|
||||||
|
else:
|
||||||
|
if path[-1] != '/': # append '/' at end of path
|
||||||
|
path += '/'
|
||||||
|
|
||||||
l = FileShare.objects.filter(repo_id=repo_id).filter(
|
l = FileShare.objects.filter(repo_id=repo_id).filter(
|
||||||
username=request.user.username).filter(path=path)
|
username=request.user.username).filter(path=path)
|
||||||
@ -396,6 +414,7 @@ def get_shared_link(request):
|
|||||||
fs.repo_id = repo_id
|
fs.repo_id = repo_id
|
||||||
fs.path = path
|
fs.path = path
|
||||||
fs.token = token
|
fs.token = token
|
||||||
|
fs.s_type = 'f' if share_type == 'f' else 'd'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fs.save()
|
fs.save()
|
||||||
@ -403,8 +422,10 @@ def get_shared_link(request):
|
|||||||
err = _('Failed to get the link, please retry it.')
|
err = _('Failed to get the link, please retry it.')
|
||||||
data = json.dumps([{'error': err}])
|
data = json.dumps([{'error': err}])
|
||||||
return HttpResponse(data, status=500, content_type=content_type)
|
return HttpResponse(data, status=500, content_type=content_type)
|
||||||
|
|
||||||
data = json.dumps([{'token': token}])
|
shared_link = gen_shared_link(request, token, fs.s_type)
|
||||||
|
|
||||||
|
data = json.dumps([{'token': token, 'shared_link': shared_link}])
|
||||||
return HttpResponse(data, status=200, content_type=content_type)
|
return HttpResponse(data, status=200, content_type=content_type)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -156,122 +156,9 @@ $('#view-original, #download').click(function() {
|
|||||||
$('#edit').click(function() {
|
$('#edit').click(function() {
|
||||||
location.href = $(this).attr('data');
|
location.href = $(this).attr('data');
|
||||||
});
|
});
|
||||||
function showLink() {
|
|
||||||
$('#get-shared-link').addClass('hide');
|
|
||||||
$('#shared-link, #send-shared-link, #rm-shared-link').removeClass('hide');
|
|
||||||
}
|
|
||||||
function hideLink() {
|
|
||||||
$('#shared-link, #send-shared-link, #rm-shared-link').addClass('hide');
|
|
||||||
$('#get-shared-link').removeClass('hide');
|
|
||||||
}
|
|
||||||
function setLinkWidth() {
|
|
||||||
var link = $('#shared-link');
|
|
||||||
link.before('<p class="hide">' + link.val() + '</p>');
|
|
||||||
link.css('width', link.prev().width() + 2);
|
|
||||||
link.prev().remove();
|
|
||||||
}
|
|
||||||
if ($.trim($('#shared-link').val())) {
|
|
||||||
setLinkWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
{% if fileshare.token %}
|
//share link
|
||||||
showLink();
|
{% include "snippets/shared_link_js.html" %}
|
||||||
{% else %}
|
|
||||||
hideLink();
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
$('#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'];
|
|
||||||
$('#rm-shared-link').attr('data', '{% url 'remove_shared_link' %}?t=' + t);
|
|
||||||
$('#shared-link, input[name="file_shared_link"]').val('{{ protocol }}://{{ domain }}{{ SITE_ROOT }}f/' + t + '/');
|
|
||||||
setLinkWidth();
|
|
||||||
showLink();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(xhr, ajaxOptions, thrownError) {
|
|
||||||
var jsonVal = jQuery.parseJSON(xhr.responseText);
|
|
||||||
feedback(jsonVal[0]['error'], 'error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#rm-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();
|
|
||||||
$('#shared-link').val('');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var share_list = [];
|
|
||||||
{% for contact in contacts %}
|
|
||||||
share_list.push({value:'{{ contact.contact_email }}', label:'{{ contact.contact_email }}'});
|
|
||||||
{% endfor %}
|
|
||||||
$('#send-shared-link').click(function() {
|
|
||||||
$("#link-send-form").modal({appendTo: "#main", focus: false});
|
|
||||||
$('#simplemodal-container').css('height', 'auto');
|
|
||||||
addAutocomplete('#link-send-input', '#link-send-form', share_list);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#link-send-form").submit(function(event) {
|
|
||||||
var form = $(this),
|
|
||||||
file_shared_link = form.children('input[name="file_shared_link"]').val(),
|
|
||||||
email = $.trim(form.children('textarea[name="email"]').val()),
|
|
||||||
submit_btn = form.children('input[type="submit"]');
|
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
apply_form_error('link-send-form', '{% trans "Please input at least an email." %}');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
disable(submit_btn);
|
|
||||||
$('#link-send-form .error').addClass('hide');
|
|
||||||
$('#sending').removeClass('hide');
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: "{% url 'send_shared_link' %}",
|
|
||||||
dataType: 'json',
|
|
||||||
cache: false,
|
|
||||||
contentType: 'application/json; charset=utf-8',
|
|
||||||
beforeSend: prepareCSRFToken,
|
|
||||||
data: {file_shared_link: file_shared_link, email: email},
|
|
||||||
success: function(data) {
|
|
||||||
$.modal.close();
|
|
||||||
feedback('{% trans "Successfully sent." %}', "success");
|
|
||||||
},
|
|
||||||
error: function(data, textStatus, jqXHR) {
|
|
||||||
$('#sending').addClass('hide');
|
|
||||||
enable(submit_btn);
|
|
||||||
var errors = $.parseJSON(data.responseText);
|
|
||||||
$.each(errors, function(index, value) {
|
|
||||||
if (index == 'error') {
|
|
||||||
apply_form_error('link-send-form', value);
|
|
||||||
} else {
|
|
||||||
apply_form_error('link-send-form', value[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
$('#shared-link').click(function() {
|
|
||||||
$(this).select();
|
|
||||||
});
|
|
||||||
|
|
||||||
//star
|
//star
|
||||||
$('#star').click(function() {
|
$('#star').click(function() {
|
||||||
|
@ -17,10 +17,18 @@
|
|||||||
<div class="w100 ovhd">
|
<div class="w100 ovhd">
|
||||||
<h2 class="fleft">{{repo.props.name}}</h2>
|
<h2 class="fleft">{{repo.props.name}}</h2>
|
||||||
<div class="fright">
|
<div class="fright">
|
||||||
|
{% if path != '/' %}
|
||||||
|
<input id="shared-link" class="hide" type="text" readonly="readonly" value="{{ dir_shared_link }}" />
|
||||||
|
<button data="{% url 'get_shared_link' %}?repo_id={{ repo.id }}&p={{ path|urlencode }}&type=d" id="get-shared-link">{% trans "Share link"%}</button>
|
||||||
|
<button id="send-shared-link" class="hide">{% trans "Send"%}</button>
|
||||||
|
<button data="{% url 'remove_shared_link' %}?t={{ fileshare.token }}" id="rm-shared-link" class="hide">{% trans "Delete"%}</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if path == '/' %}
|
||||||
<button id="repo-download-btn">{% trans "Download"%}</button>
|
<button id="repo-download-btn">{% trans "Download"%}</button>
|
||||||
{% if user_perm == 'rw' %}
|
{% if user_perm == 'rw' %}
|
||||||
<button id="recycle-btn" data="{% url 'repo_recycle_view' repo.id %}">{% trans "Trash"%}</button>
|
<button id="recycle-btn" data="{% url 'repo_recycle_view' repo.id %}">{% trans "Trash"%}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if user_perm == 'r' %}
|
{% if user_perm == 'r' %}
|
||||||
@ -80,13 +88,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% if user_perm == 'rw' %}
|
|
||||||
<div class="repo-op fright">
|
<div class="repo-op fright">
|
||||||
|
{% if user_perm and path != '/' %}
|
||||||
|
<button data="{% url 'repo_download_dir' repo.id %}?p={{ path|urlencode }}" id="download-dir" class="op-btn">{% trans "Download"%}</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if user_perm == 'rw' %}
|
||||||
<button data="{{ SITE_ROOT }}repo/upload_file/{{repo.id}}/?p={{ path|urlencode }}" id="upload-file" class="op-btn">{% trans "Upload"%}</button>
|
<button data="{{ SITE_ROOT }}repo/upload_file/{{repo.id}}/?p={{ path|urlencode }}" id="upload-file" class="op-btn">{% trans "Upload"%}</button>
|
||||||
<button id="add-new-dir" class="op-btn">{% trans "New Directory"%}</button>
|
<button id="add-new-dir" class="op-btn">{% trans "New Directory"%}</button>
|
||||||
<button id="add-new-file" class="op-btn">{% trans "New File"%}</button>
|
<button id="add-new-file" class="op-btn">{% trans "New File"%}</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.repo-file-list-topbar -->
|
<!-- /.repo-file-list-topbar -->
|
||||||
<table class="repo-file-list">
|
<table class="repo-file-list">
|
||||||
@ -119,7 +130,8 @@
|
|||||||
{% if user_perm %}
|
{% if user_perm %}
|
||||||
<div class="repo-file-op vh">
|
<div class="repo-file-op vh">
|
||||||
<div class="displayed-op">
|
<div class="displayed-op">
|
||||||
<a class="op" href="{{ SITE_ROOT }}repo/download_dir/{{ repo.id }}/?parent={{ path|urlencode }}&dirname={{ dirent.obj_name|urlencode }}">{% trans 'Download' %}</a>
|
<a class="op" href="{% url 'repo_download_dir' repo.id %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{% trans 'Download' %}</a>
|
||||||
|
<a class="op file-share" href="#" data-name="{{ dirent.obj_name }}" data-link="{{ dirent.sharelink }}" data-token="{{ dirent.sharetoken }}" data-type="d">{% trans "Share" %}</a>
|
||||||
</div>
|
</div>
|
||||||
{% if user_perm == 'rw' %}
|
{% if user_perm == 'rw' %}
|
||||||
<img src="{{ MEDIA_URL }}img/dropdown-arrow.png" title="{% trans 'More operations'%}" alt="{% trans 'More operations'%}" class="more-op-icon" data="no-popup" />
|
<img src="{{ MEDIA_URL }}img/dropdown-arrow.png" title="{% trans 'More operations'%}" alt="{% trans 'More operations'%}" class="more-op-icon" data="no-popup" />
|
||||||
@ -298,15 +310,16 @@
|
|||||||
|
|
||||||
<div id="file-share" class="hide">
|
<div id="file-share" class="hide">
|
||||||
<h3>{% trans 'Share' %} <span class="op-target"></span></h3>
|
<h3>{% trans 'Share' %} <span class="op-target"></span></h3>
|
||||||
<p>{% trans 'Link: ' %}<input type="text" readonly="readonly" id="shared-link" /></p>
|
<p>{% trans 'Link: ' %}<input type="text" readonly="readonly" id="shared-link-text" /></p>
|
||||||
<button id="send-link">{% trans 'Send' %}</button>
|
<button id="send-link">{% trans 'Send' %}</button>
|
||||||
<form id="link-send-form" class="hide">
|
<form id="link-send-form" action="" method="post" class="hide">
|
||||||
<label>{% trans "Send to:"%}</label><br />
|
<h3>Send Link</h3>
|
||||||
<textarea id="link-send-input" name="email" placeholder="{% trans "Emails, Seperated by ','"%}"></textarea><br />
|
<label>{% trans "Send to:"%}</label><br />
|
||||||
<input type="hidden" name="file_shared_link" value="" />
|
<textarea id="link-send-input" name="email" placeholder="{% trans "Emails, Seperated by ','"%}"></textarea><br />
|
||||||
<input type="submit" value="{% trans "Submit"%}" class="submit" />
|
<input type="hidden" name="file_shared_link" value="{{ dir_shared_link }}" />
|
||||||
<p class="error hide"></p>
|
<input type="submit" value="{% trans "Submit"%}" class="submit" />
|
||||||
<p id="sending" class="hide">{% trans "Sending..."%}</p>
|
<p class="error hide"></p>
|
||||||
|
<p id="sending" class="hide">{% trans "Sending..."%}</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -602,7 +615,7 @@ $('#add-new-file-form').submit(function() {
|
|||||||
$('#add-new-dir-form').submit(function() {
|
$('#add-new-dir-form').submit(function() {
|
||||||
var new_dir = $(this).find('input[name="new_dir_name"]').val();
|
var new_dir = $(this).find('input[name="new_dir_name"]').val();
|
||||||
if (!$.trim(new_dir)) {
|
if (!$.trim(new_dir)) {
|
||||||
apply_form_error('add-new-dir-form', "{% trans "Directory name can't be empty" %}");
|
apply_form_error('add-new-dir-form', "{% trans "Directory name can not be empty" %}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,24 +768,31 @@ $('.file-share').click(function() {
|
|||||||
var filename = $(this).data('name');
|
var filename = $(this).data('name');
|
||||||
function showPopup(link) {
|
function showPopup(link) {
|
||||||
$('#file-share .op-target').html(trimFilename(filename, 30));
|
$('#file-share .op-target').html(trimFilename(filename, 30));
|
||||||
$('#shared-link, #link-send-form input[name="file_shared_link"]').val(link);
|
$('#shared-link-text, #link-send-form input[name="file_shared_link"]').val(link);
|
||||||
$('#main').append('<p id="linkwidth" class="hide">' + link + '</p>');
|
$('#main').append('<p id="linkwidth" class="hide">' + link + '</p>');
|
||||||
$('#shared-link').css({'width':$('#linkwidth').width() + 2});
|
$('#shared-link-text').css({'width':$('#linkwidth').width() + 2});
|
||||||
$('#file-share').modal({'focus':false}); // in ff: if 'focus' is true, 'shared-link' gets the focus
|
$('#file-share').modal({'focus':false}); // in ff: if 'focus' is true, 'shared-link-text' gets the focus
|
||||||
$('#linkwidth').remove();
|
$('#linkwidth').remove();
|
||||||
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
|
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
|
||||||
}
|
}
|
||||||
if ($(this).data('link')) {
|
if ($(this).data('link')) {
|
||||||
showPopup($(this).data('link'));
|
showPopup($(this).data('link'));
|
||||||
} else {
|
} else {
|
||||||
|
var aj_url = '';
|
||||||
|
if ($(this).data('type') == 'd') {
|
||||||
|
aj_url = '{% url 'get_shared_link' %}?repo_id={{ repo.id }}&p={{ path|urlencode }}' + e(filename) + '&type=d';
|
||||||
|
} else {
|
||||||
|
aj_url = '{% url 'get_shared_link' %}?repo_id={{ repo.id }}&p={{ path|urlencode }}' + e(filename);
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '{% url 'get_shared_link' %}?repo_id={{ repo.id }}&p={{ path|urlencode }}' + e(filename),
|
url: aj_url,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
cache: false,
|
cache: false,
|
||||||
contentType: 'application/json; charset=utf-8',
|
contentType: 'application/json; charset=utf-8',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
showPopup('{{ protocol }}://{{ domain }}{{ SITE_ROOT }}f/' + data[0]['token'] + '/');
|
showPopup(data[0]['shared_link']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, ajaxOptions, thrownError) {
|
error: function(xhr, ajaxOptions, thrownError) {
|
||||||
@ -839,9 +859,15 @@ $("#link-send-form").submit(function() {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
$('#shared-link').click(function() { $(this).select(); });
|
$('#shared-link-text').click(function() { $(this).select(); });
|
||||||
|
|
||||||
|
$('#download-dir').click(function() {
|
||||||
|
location.href = $(this).attr('data');
|
||||||
|
});
|
||||||
|
{% include "snippets/shared_link_js.html" %}
|
||||||
{% include "snippets/list_commit_detail.html" %}
|
{% include "snippets/list_commit_detail.html" %}
|
||||||
{% include "snippets/bottom_bar.html" %}
|
{% include "snippets/bottom_bar.html" %}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% include 'snippets/file_upload_progress_js.html' %}
|
{% include 'snippets/file_upload_progress_js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -13,7 +13,20 @@
|
|||||||
<h2 id="view-hd">{{ file_name }}</h2>
|
<h2 id="view-hd">{{ file_name }}</h2>
|
||||||
<div id="file">
|
<div id="file">
|
||||||
<div id="file-op">
|
<div id="file-op">
|
||||||
|
{% if zipped %}
|
||||||
|
<p class="path fleft">
|
||||||
|
{% trans "Current path: "%}
|
||||||
|
{% for name, link in zipped %}
|
||||||
|
{% if not forloop.last %}
|
||||||
|
<a href="{{ SITE_ROOT }}d/{{ token }}/?p={{ link|urlencode }}">{{ name }}</a> /
|
||||||
|
{% else %}
|
||||||
|
{{ name }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
<p class="fleft">{% trans "Shared by: " %}{{ username|email2nickname }}</p>
|
<p class="fleft">{% trans "Shared by: " %}{{ username|email2nickname }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if filetype == 'Text' or filetype == 'Image' or filetype == 'SVG' or filetype == 'Markdown' %}
|
{% if filetype == 'Text' or filetype == 'Image' or filetype == 'SVG' or filetype == 'Markdown' %}
|
||||||
<button data="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=view&t={{ shared_token }}" id="view-original">{% trans "Raw" %}</button>
|
<button data="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name }}&op=view&t={{ shared_token }}" id="view-original">{% trans "Raw" %}</button>
|
||||||
|
117
templates/snippets/shared_link_js.html
Normal file
117
templates/snippets/shared_link_js.html
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load url from future %}
|
||||||
|
function showLink() {
|
||||||
|
$('#get-shared-link').addClass('hide');
|
||||||
|
$('#shared-link, #send-shared-link, #rm-shared-link').removeClass('hide');
|
||||||
|
}
|
||||||
|
function hideLink() {
|
||||||
|
$('#shared-link, #send-shared-link, #rm-shared-link').addClass('hide');
|
||||||
|
$('#get-shared-link').removeClass('hide');
|
||||||
|
}
|
||||||
|
function setLinkWidth() {
|
||||||
|
var link = $('#shared-link');
|
||||||
|
link.before('<p class="hide">' + link.val() + '</p>');
|
||||||
|
link.css('width', link.prev().width() + 2);
|
||||||
|
link.prev().remove();
|
||||||
|
}
|
||||||
|
if ($.trim($('#shared-link').val())) {
|
||||||
|
setLinkWidth();
|
||||||
|
}
|
||||||
|
{% if fileshare.token %}
|
||||||
|
showLink();
|
||||||
|
{% else %}
|
||||||
|
hideLink();
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
$('#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'];
|
||||||
|
$('#rm-shared-link').attr('data', '{% url 'remove_shared_link' %}?t=' + t);
|
||||||
|
$('#shared-link, input[name="file_shared_link"]').val(data[0]['shared_link']);
|
||||||
|
setLinkWidth();
|
||||||
|
showLink();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, ajaxOptions, thrownError) {
|
||||||
|
var jsonVal = jQuery.parseJSON(xhr.responseText);
|
||||||
|
feedback(jsonVal[0]['error'], 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#rm-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();
|
||||||
|
$('#shared-link').val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var share_list = [];
|
||||||
|
{% for contact in contacts %}
|
||||||
|
share_list.push({value:'{{ contact.contact_email }}', label:'{{ contact.contact_email }}'});
|
||||||
|
{% endfor %}
|
||||||
|
$('#send-shared-link').click(function() {
|
||||||
|
$("#link-send-form").modal({appendTo: "#main", focus: false});
|
||||||
|
$('#simplemodal-container').css('height', 'auto');
|
||||||
|
addAutocomplete('#link-send-input', '#link-send-form', share_list);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#link-send-form").submit(function(event) {
|
||||||
|
var form = $(this),
|
||||||
|
file_shared_link = form.children('input[name="file_shared_link"]').val(),
|
||||||
|
email = $.trim(form.children('textarea[name="email"]').val()),
|
||||||
|
submit_btn = form.children('input[type="submit"]');
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
apply_form_error('link-send-form', '{% trans "Please input at least an email." %}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable(submit_btn);
|
||||||
|
$('#link-send-form .error').addClass('hide');
|
||||||
|
$('#sending').removeClass('hide');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "{% url 'send_shared_link' %}",
|
||||||
|
dataType: 'json',
|
||||||
|
cache: false,
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
beforeSend: prepareCSRFToken,
|
||||||
|
data: {file_shared_link: file_shared_link, email: email},
|
||||||
|
success: function(data) {
|
||||||
|
$.modal.close();
|
||||||
|
feedback('{% trans "Successfully sent." %}', "success");
|
||||||
|
},
|
||||||
|
error: function(data, textStatus, jqXHR) {
|
||||||
|
$('#sending').addClass('hide');
|
||||||
|
enable(submit_btn);
|
||||||
|
var errors = $.parseJSON(data.responseText);
|
||||||
|
$.each(errors, function(index, value) {
|
||||||
|
if (index == 'error') {
|
||||||
|
apply_form_error('link-send-form', value);
|
||||||
|
} else {
|
||||||
|
apply_form_error('link-send-form', value[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$('#shared-link').click(function() {
|
||||||
|
$(this).select();
|
||||||
|
});
|
157
templates/view_shared_dir.html
Normal file
157
templates/view_shared_dir.html
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
{% extends base_template %}
|
||||||
|
|
||||||
|
{% load seahub_tags i18n %}
|
||||||
|
{% load url from future %}
|
||||||
|
|
||||||
|
{% block info_bar_message %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main_panel %}
|
||||||
|
<h2 id="view-hd">{{ dir_name }}</h2>
|
||||||
|
<div>
|
||||||
|
<p>{% trans "Shared by: " %}{{ username|email2nickname }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="repo-file-list-outer-container">
|
||||||
|
<div class="repo-file-list-inner-container">
|
||||||
|
<div class="repo-file-list-topbar ovhd">
|
||||||
|
<p class="path fleft">
|
||||||
|
{% trans "Current path: "%}
|
||||||
|
{% for name, link in zipped %}
|
||||||
|
{% if not forloop.last %}
|
||||||
|
<a href="{{ SITE_ROOT }}d/{{ token }}/?p={{ link|urlencode }}">{{ name }}</a> /
|
||||||
|
{% else %}
|
||||||
|
{{ name }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
<div class="repo-op fright">
|
||||||
|
<button data="{% url 'repo_download_dir' repo.id %}?p={{ path|urlencode }}&t={{ token }}" id="download-dir" class="op-btn">{% trans "Download"%}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.repo-file-list-topbar -->
|
||||||
|
<table class="repo-file-list">
|
||||||
|
<tr>
|
||||||
|
<th width="3%"></th>
|
||||||
|
<th width="5%"></th>
|
||||||
|
<th width="53%">{% trans "Name"%}</th>
|
||||||
|
<th width="10%">{% trans "Size"%}</th>
|
||||||
|
<th width="29%">{% trans "Operations"%}</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for dirent in dir_list %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="icon-container"><img src="{{ MEDIA_URL }}img/folder-icon-24.png" alt="{% trans "Directory icon"%}" /></td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ SITE_ROOT }}d/{{ token }}/?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.obj_name }}</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<div class="repo-file-op vh">
|
||||||
|
<div class="displayed-op">
|
||||||
|
<a class="op" href="{% url 'repo_download_dir' repo.id %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&t={{token}}">{% trans 'Download' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for dirent in file_list %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="icon-container"><img src="{{ MEDIA_URL }}img/file/{{ dirent.obj_name|file_icon_filter }}" alt="{% trans "File"%}" /></td>
|
||||||
|
<td>
|
||||||
|
<a class="op" href="{{ SITE_ROOT }}d/{{ token }}/files/?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.props.obj_name }}</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{{ dirent.file_size|filesizeformat }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="repo-file-op vh">
|
||||||
|
<div class="displayed-op">
|
||||||
|
<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=download">{% trans "Download"%}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
<!-- /.repo-file-list -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_script %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var popup_tr = ''; // the tr which the shown popup belongs to
|
||||||
|
var no_file_op_popup = true;
|
||||||
|
$("tr:gt(0)").unbind().hover( // remove previously binded hover handler at first
|
||||||
|
function() {
|
||||||
|
if (no_file_op_popup) {
|
||||||
|
$(this).addClass('hl').find('.repo-file-op').removeClass('vh');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
if (no_file_op_popup) {
|
||||||
|
$(this).removeClass('hl').find('.repo-file-op').addClass('vh');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$('#main-panel').removeClass('ovhd');
|
||||||
|
$('.more-op-icon').click(function(e) {
|
||||||
|
var hidden_op = $(this).next();
|
||||||
|
if ($(this).attr('data')) { // no popup
|
||||||
|
hidden_op.css({'left': $(this).position().left + $(this).width() + 5});
|
||||||
|
if ($(this).offset().top + hidden_op.height() <= $('#main').offset().top + $('#main').height()) {
|
||||||
|
hidden_op.css('top', 6);
|
||||||
|
} else {
|
||||||
|
hidden_op.css('bottom', 2);
|
||||||
|
}
|
||||||
|
hidden_op.removeClass('hide');
|
||||||
|
$(this).attr('data','');
|
||||||
|
no_file_op_popup = false;
|
||||||
|
popup_tr = $(this).parents('tr');
|
||||||
|
} else {
|
||||||
|
hidden_op.addClass('hide');
|
||||||
|
$(this).attr('data','no-popup');
|
||||||
|
no_file_op_popup = true;
|
||||||
|
popup_tr = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$(document).click(function(e) {
|
||||||
|
var target = e.target || event.srcElement;
|
||||||
|
if (!no_file_op_popup &&
|
||||||
|
!$('.more-op-icon, .hidden-op').is(target) &&
|
||||||
|
!$('.hidden-op').find('*').is(target)) {
|
||||||
|
$('.hidden-op').addClass('hide');
|
||||||
|
$('.more-op-icon').attr('data', 'no-popup');
|
||||||
|
no_file_op_popup = true;
|
||||||
|
|
||||||
|
if (!popup_tr.find('*').is(target)) {
|
||||||
|
popup_tr.removeClass('hl').find('.repo-file-op').addClass('vh'); // clicked place: the first tr, place out of the table
|
||||||
|
$('tr:gt(0)').each(function() { // when other tr is clicked
|
||||||
|
if ($(this).find('*').is(target)) {
|
||||||
|
$(this).addClass('hl').find('.repo-file-op').removeClass('vh');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.hidden-op li').hover(
|
||||||
|
function() {
|
||||||
|
$(this).css('background', '#eee');
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
$(this).css('background', '#fff');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$('#download-dir').click(function() {
|
||||||
|
location.href = $(this).attr('data');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -16,7 +16,7 @@ from service import get_repos, get_repo, get_commits, get_branches, remove_repo,
|
|||||||
list_personal_shared_repos, is_personal_repo, list_inner_pub_repos, \
|
list_personal_shared_repos, is_personal_repo, list_inner_pub_repos, \
|
||||||
is_org_repo_owner, get_org_repo_owner, is_org_repo, get_file_size,\
|
is_org_repo_owner, get_org_repo_owner, is_org_repo, get_file_size,\
|
||||||
list_personal_repos_by_owner, get_repo_token_nonnull, get_repo_owner, \
|
list_personal_repos_by_owner, get_repo_token_nonnull, get_repo_owner, \
|
||||||
server_repo_size
|
server_repo_size, get_file_id_by_path
|
||||||
|
|
||||||
from service import get_binding_peerids, is_valid_filename, check_permission,\
|
from service import get_binding_peerids, is_valid_filename, check_permission,\
|
||||||
is_passwd_set
|
is_passwd_set
|
||||||
|
@ -669,6 +669,13 @@ def get_file_size(file_id):
|
|||||||
fs = 0
|
fs = 0
|
||||||
return fs
|
return fs
|
||||||
|
|
||||||
|
def get_file_id_by_path(repo_id, path):
|
||||||
|
try:
|
||||||
|
ret = seafserv_threaded_rpc.get_file_id_by_path(repo_id, path)
|
||||||
|
except SearpcError, e:
|
||||||
|
ret = ''
|
||||||
|
return ret
|
||||||
|
|
||||||
def get_related_users_by_repo(repo_id):
|
def get_related_users_by_repo(repo_id):
|
||||||
"""Give a repo id, returns a list of users of:
|
"""Give a repo id, returns a list of users of:
|
||||||
- the repo owner
|
- the repo owner
|
||||||
|
6
urls.py
6
urls.py
@ -57,7 +57,9 @@ urlpatterns = patterns('',
|
|||||||
(r'^repo/(?P<repo_id>[-0-9a-f]{36})/file/edit/$', file_edit),
|
(r'^repo/(?P<repo_id>[-0-9a-f]{36})/file/edit/$', file_edit),
|
||||||
url(r'^repo/(?P<repo_id>[-0-9a-f]{36})/(?P<obj_id>[^/]+)/$', repo_access_file, name='repo_access_file'),
|
url(r'^repo/(?P<repo_id>[-0-9a-f]{36})/(?P<obj_id>[^/]+)/$', repo_access_file, name='repo_access_file'),
|
||||||
|
|
||||||
url(r'^f/(?P<token>[^/]+)/$', view_shared_file, name='view_shared_file'),
|
url(r'^f/(?P<token>[a-f0-9]{10})/$', view_shared_file, name='view_shared_file'),
|
||||||
|
url(r'^d/(?P<token>[a-f0-9]{10})/$', view_shared_dir, name='view_shared_dir'),
|
||||||
|
url(r'^d/(?P<token>[a-f0-9]{10})/files/$', view_file_via_shared_dir, name='view_file_via_shared_dir'),
|
||||||
(r'^file_upload_progress_page/$', file_upload_progress_page),
|
(r'^file_upload_progress_page/$', file_upload_progress_page),
|
||||||
(r'^publicrepo/create/$', public_repo_create),
|
(r'^publicrepo/create/$', public_repo_create),
|
||||||
(r'^events/$', events),
|
(r'^events/$', events),
|
||||||
@ -77,7 +79,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^useradmin/password/reset/(?P<user_id>[^/]+)/$', user_reset, name='user_reset'),
|
url(r'^useradmin/password/reset/(?P<user_id>[^/]+)/$', user_reset, name='user_reset'),
|
||||||
|
|
||||||
### Apps ###
|
### Apps ###
|
||||||
(r'^api/', include('api.urls')),
|
# (r'^api/', include('api.urls')),
|
||||||
(r'^api2/', include('api2.urls')),
|
(r'^api2/', include('api2.urls')),
|
||||||
(r'^avatar/', include('avatar.urls')),
|
(r'^avatar/', include('avatar.urls')),
|
||||||
(r'^notification/', include('notifications.urls')),
|
(r'^notification/', include('notifications.urls')),
|
||||||
|
@ -7,6 +7,7 @@ import stat
|
|||||||
import urllib2
|
import urllib2
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from django.contrib.sites.models import RequestSite
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.utils.hashcompat import sha_constructor
|
from django.utils.hashcompat import sha_constructor
|
||||||
@ -701,3 +702,13 @@ def calc_file_path_hash(path, bits=12):
|
|||||||
path_hash = md5_constructor(urllib2.quote(path)).hexdigest()[:bits]
|
path_hash = md5_constructor(urllib2.quote(path)).hexdigest()[:bits]
|
||||||
|
|
||||||
return path_hash
|
return path_hash
|
||||||
|
|
||||||
|
def gen_shared_link(request, token, s_type):
|
||||||
|
http_or_https = request.is_secure() and 'https' or 'http'
|
||||||
|
domain = RequestSite(request).domain
|
||||||
|
|
||||||
|
if s_type == 'f':
|
||||||
|
return '%s://%s%sf/%s/' % (http_or_https, domain, settings.SITE_ROOT, token)
|
||||||
|
else:
|
||||||
|
return '%s://%s%sd/%s/' % (http_or_https, domain, settings.SITE_ROOT, token)
|
||||||
|
|
||||||
|
192
views.py
192
views.py
@ -44,7 +44,7 @@ from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_repos, get_emailusers, \
|
|||||||
list_inner_pub_repos, get_org_groups_by_repo, is_org_repo_owner, \
|
list_inner_pub_repos, get_org_groups_by_repo, is_org_repo_owner, \
|
||||||
get_org_repo_owner, is_passwd_set, get_file_size, check_quota, \
|
get_org_repo_owner, is_passwd_set, get_file_size, check_quota, \
|
||||||
get_related_users_by_repo, get_related_users_by_org_repo, HtmlDiff, \
|
get_related_users_by_repo, get_related_users_by_org_repo, HtmlDiff, \
|
||||||
get_session_info, get_group_repoids, get_repo_owner
|
get_session_info, get_group_repoids, get_repo_owner, get_file_id_by_path
|
||||||
from pysearpc import SearpcError
|
from pysearpc import SearpcError
|
||||||
|
|
||||||
from signals import repo_created, repo_deleted
|
from signals import repo_created, repo_deleted
|
||||||
@ -64,7 +64,7 @@ from forms import AddUserForm, RepoCreateForm, RepoNewDirForm, RepoNewFileForm,\
|
|||||||
FileCommentForm, RepoRenameFileForm, RepoPassowrdForm, SharedRepoCreateForm,\
|
FileCommentForm, RepoRenameFileForm, RepoPassowrdForm, SharedRepoCreateForm,\
|
||||||
SetUserQuotaForm
|
SetUserQuotaForm
|
||||||
from utils import render_permission_error, render_error, list_to_string, \
|
from utils import render_permission_error, render_error, list_to_string, \
|
||||||
get_httpserver_root, get_ccnetapplet_root, \
|
get_httpserver_root, get_ccnetapplet_root, gen_shared_link, \
|
||||||
calculate_repo_last_modify, valid_previewed_file, \
|
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, get_ccnet_server_addr_port, \
|
get_file_revision_id_size, get_ccnet_server_addr_port, \
|
||||||
@ -165,7 +165,7 @@ def get_repo_dirents(request, repo_id, commit, path):
|
|||||||
# return render_error(self.request, e.msg)
|
# return render_error(self.request, e.msg)
|
||||||
|
|
||||||
org_id = -1
|
org_id = -1
|
||||||
if request.user.org:
|
if hasattr(request.user, 'org') and request.user.org:
|
||||||
org_id = request.user.org['org_id']
|
org_id = request.user.org['org_id']
|
||||||
starred_files = get_dir_starred_files(request.user.username, repo_id, path, org_id)
|
starred_files = get_dir_starred_files(request.user.username, repo_id, path, org_id)
|
||||||
|
|
||||||
@ -177,20 +177,27 @@ def get_repo_dirents(request, repo_id, commit, path):
|
|||||||
|
|
||||||
for dirent in dirs:
|
for dirent in dirs:
|
||||||
dirent.last_modified = last_modified_info.get(dirent.obj_name, 0)
|
dirent.last_modified = last_modified_info.get(dirent.obj_name, 0)
|
||||||
|
dirent.sharelink = ''
|
||||||
if stat.S_ISDIR(dirent.props.mode):
|
if stat.S_ISDIR(dirent.props.mode):
|
||||||
|
dpath = os.path.join(path, dirent.obj_name)
|
||||||
|
if dpath[-1] != '/':
|
||||||
|
dpath += '/'
|
||||||
|
for share in fileshares:
|
||||||
|
if dpath == share.path:
|
||||||
|
dirent.sharelink = gen_shared_link(request, share.token, 'd')
|
||||||
|
dirent.sharetoken = share.token
|
||||||
|
break
|
||||||
dir_list.append(dirent)
|
dir_list.append(dirent)
|
||||||
else:
|
else:
|
||||||
file_list.append(dirent)
|
file_list.append(dirent)
|
||||||
dirent.file_size = get_file_size(dirent.obj_id)
|
dirent.file_size = get_file_size(dirent.obj_id)
|
||||||
dirent.starred = False
|
dirent.starred = False
|
||||||
dirent.sharelink = ''
|
|
||||||
fpath = os.path.join(path, dirent.obj_name)
|
fpath = os.path.join(path, dirent.obj_name)
|
||||||
if fpath in starred_files:
|
if fpath in starred_files:
|
||||||
dirent.starred = True
|
dirent.starred = True
|
||||||
for share in fileshares:
|
for share in fileshares:
|
||||||
if fpath == share.path:
|
if fpath == share.path:
|
||||||
dirent.sharelink = '%s://%s%sf/%s/' % (http_or_https, domain, settings.SITE_ROOT, share.token)
|
dirent.sharelink = gen_shared_link(request, share.token, 'f')
|
||||||
dirent.sharetoken = share.token
|
dirent.sharetoken = share.token
|
||||||
break
|
break
|
||||||
dir_list.sort(lambda x, y : cmp(x.obj_name.lower(),
|
dir_list.sort(lambda x, y : cmp(x.obj_name.lower(),
|
||||||
@ -346,6 +353,26 @@ class RepoView(LoginRequiredMixin, CtxSwitchRequiredMixin, RepoMixin,
|
|||||||
return gen_file_upload_url(token, 'update')
|
return gen_file_upload_url(token, 'update')
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def get_fileshare(self, repo_id, user, path):
|
||||||
|
if path == '/': # no shared link for root dir
|
||||||
|
return None
|
||||||
|
|
||||||
|
l = FileShare.objects.filter(repo_id=repo_id).filter(\
|
||||||
|
username=user).filter(path=path)
|
||||||
|
fileshare = l[0] if len(l) > 0 else None
|
||||||
|
return fileshare
|
||||||
|
|
||||||
|
def get_shared_link(self, fileshare):
|
||||||
|
# dir shared link
|
||||||
|
http_or_https = self.request.is_secure() and 'https' or 'http'
|
||||||
|
domain = RequestSite(self.request).domain
|
||||||
|
|
||||||
|
if fileshare:
|
||||||
|
dir_shared_link = gen_shared_link(self.request, fileshare.token, 'd')
|
||||||
|
else:
|
||||||
|
dir_shared_link = ''
|
||||||
|
return dir_shared_link
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['repo'] = self.repo
|
kwargs['repo'] = self.repo
|
||||||
@ -379,6 +406,9 @@ class RepoView(LoginRequiredMixin, CtxSwitchRequiredMixin, RepoMixin,
|
|||||||
kwargs['protocol'] = self.protocol
|
kwargs['protocol'] = self.protocol
|
||||||
kwargs['domain'] = self.domain
|
kwargs['domain'] = self.domain
|
||||||
kwargs['contacts'] = self.contacts
|
kwargs['contacts'] = self.contacts
|
||||||
|
kwargs['fileshare'] = self.get_fileshare(\
|
||||||
|
self.repo_id, self.request.user.username, self.path)
|
||||||
|
kwargs['dir_shared_link'] = self.get_shared_link(kwargs['fileshare'])
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@ -1297,13 +1327,12 @@ def repo_view_file(request, repo_id):
|
|||||||
http_or_https = request.is_secure() and 'https' or 'http'
|
http_or_https = request.is_secure() and 'https' or 'http'
|
||||||
domain = RequestSite(request).domain
|
domain = RequestSite(request).domain
|
||||||
if fileshare:
|
if fileshare:
|
||||||
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain, settings.SITE_ROOT, fileshare.token)
|
file_shared_link = gen_shared_link(request, fileshare.token, 'f')
|
||||||
else:
|
else:
|
||||||
file_shared_link = ''
|
file_shared_link = ''
|
||||||
|
|
||||||
# my constacts
|
# my constacts
|
||||||
contacts = Contact.objects.filter(user_email=request.user.username)
|
contacts = Contact.objects.filter(user_email=request.user.username)
|
||||||
|
|
||||||
|
|
||||||
# Get groups this repo is shared.
|
# Get groups this repo is shared.
|
||||||
if request.user.org:
|
if request.user.org:
|
||||||
@ -2416,7 +2445,7 @@ def view_shared_file(request, token):
|
|||||||
path = fileshare.path
|
path = fileshare.path
|
||||||
|
|
||||||
http_server_root = get_httpserver_root()
|
http_server_root = get_httpserver_root()
|
||||||
if path[-1] == '/':
|
if path[-1] == '/': # Normalize file path
|
||||||
path = path[:-1]
|
path = path[:-1]
|
||||||
filename = os.path.basename(path)
|
filename = os.path.basename(path)
|
||||||
quote_filename = urllib2.quote(filename.encode('utf-8'))
|
quote_filename = urllib2.quote(filename.encode('utf-8'))
|
||||||
@ -2472,7 +2501,113 @@ def view_shared_file(request, token):
|
|||||||
'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT,
|
'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
def view_shared_dir(request, token):
|
||||||
|
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 = request.GET.get('p', '')
|
||||||
|
path = fileshare.path if not path else path
|
||||||
|
if path[-1] != '/': # Normalize dir path
|
||||||
|
path += '/'
|
||||||
|
|
||||||
|
if not path.startswith(fileshare.path):
|
||||||
|
path = fileshare.path # Can not view upper dir of shared dir
|
||||||
|
|
||||||
|
repo = get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
dir_name = os.path.basename(path[:-1])
|
||||||
|
current_commit = get_commits(repo_id, 0, 1)[0]
|
||||||
|
file_list, dir_list = get_repo_dirents(request, repo_id, current_commit,
|
||||||
|
path)
|
||||||
|
zipped = gen_path_link(path, '')
|
||||||
|
|
||||||
|
if path == fileshare.path: # When user view the shared dir..
|
||||||
|
# increase shared link view_cnt,
|
||||||
|
fileshare = FileShare.objects.get(token=token)
|
||||||
|
fileshare.view_cnt = F('view_cnt') + 1
|
||||||
|
fileshare.save()
|
||||||
|
|
||||||
|
return render_to_response('view_shared_dir.html', {
|
||||||
|
'repo': repo,
|
||||||
|
'token': token,
|
||||||
|
'path': path,
|
||||||
|
'username': username,
|
||||||
|
'dir_name': dir_name,
|
||||||
|
'file_list': file_list,
|
||||||
|
'dir_list': dir_list,
|
||||||
|
'zipped': zipped,
|
||||||
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
def view_file_via_shared_dir(request, token):
|
||||||
|
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 = request.GET.get('p', '')
|
||||||
|
if not path:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
if not path.startswith(fileshare.path): # Can not view upper dir of shared dir
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
repo = get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
file_name = os.path.basename(path)
|
||||||
|
quote_filename = urllib2.quote(file_name.encode('utf-8'))
|
||||||
|
file_id = get_file_id_by_path(repo_id, path)
|
||||||
|
if not file_id:
|
||||||
|
return render_error(request, _(u'File not exists'))
|
||||||
|
|
||||||
|
access_token = seafserv_rpc.web_get_access_token(repo.id, file_id,
|
||||||
|
'view', '')
|
||||||
|
filetype, fileext = valid_previewed_file(file_name)
|
||||||
|
# Raw path
|
||||||
|
raw_path = gen_file_get_url(access_token, quote_filename)
|
||||||
|
# get file content
|
||||||
|
err = ''
|
||||||
|
file_content = ''
|
||||||
|
swf_exists = False
|
||||||
|
if filetype == 'Text' or filetype == 'Markdown' or filetype == 'Sf':
|
||||||
|
err, file_content, encoding = repo_file_get(raw_path)
|
||||||
|
elif filetype == 'Document' or filetype == 'PDF':
|
||||||
|
err, swf_exists = flash_prepare(raw_path, obj_id, fileext)
|
||||||
|
|
||||||
|
zipped = gen_path_link(path, '')
|
||||||
|
|
||||||
|
return render_to_response('shared_file_view.html', {
|
||||||
|
'repo': repo,
|
||||||
|
'obj_id': file_id,
|
||||||
|
'path': path,
|
||||||
|
'file_name': file_name,
|
||||||
|
'shared_token': token,
|
||||||
|
'access_token': access_token,
|
||||||
|
'filetype': filetype,
|
||||||
|
'fileext': fileext,
|
||||||
|
'raw_path': raw_path,
|
||||||
|
'username': username,
|
||||||
|
'err': err,
|
||||||
|
'file_content': file_content,
|
||||||
|
'swf_exists': swf_exists,
|
||||||
|
'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT,
|
||||||
|
'zipped': zipped,
|
||||||
|
'token': token,
|
||||||
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
def flash_prepare(raw_path, obj_id, doctype):
|
def flash_prepare(raw_path, obj_id, doctype):
|
||||||
curl = DOCUMENT_CONVERTOR_ROOT + 'convert'
|
curl = DOCUMENT_CONVERTOR_ROOT + 'convert'
|
||||||
data = {'doctype': doctype,
|
data = {'doctype': doctype,
|
||||||
@ -2653,22 +2788,34 @@ def repo_star_file(request, repo_id):
|
|||||||
unstar_file(request.user.username, repo_id, path)
|
unstar_file(request.user.username, repo_id, path)
|
||||||
return HttpResponse(json.dumps({'success':True}), content_type=content_type)
|
return HttpResponse(json.dumps({'success':True}), content_type=content_type)
|
||||||
|
|
||||||
@login_required
|
|
||||||
def repo_download_dir(request, repo_id):
|
def repo_download_dir(request, repo_id):
|
||||||
repo = get_repo(repo_id)
|
repo = get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
return render_error(request, _(u'Library not exists'))
|
return render_error(request, _(u'Library not exists'))
|
||||||
|
|
||||||
try:
|
path = request.GET.get('p', '/')
|
||||||
parent_dir = request.GET['parent']
|
if path[-1] != '/': # Normalize dir path
|
||||||
dirname = request.GET['dirname']
|
path += '/'
|
||||||
except KeyError:
|
|
||||||
return render_error(request, _(u'Invalid arguments'))
|
|
||||||
|
|
||||||
path = os.path.join(parent_dir, dirname.rstrip('/'))
|
if len(path) > 1:
|
||||||
|
dirname = os.path.basename(path.rstrip('/')) # Here use `rstrip` to cut out last '/' in path
|
||||||
|
else:
|
||||||
|
dirname = repo.name
|
||||||
|
|
||||||
|
allow_download = False
|
||||||
|
fileshare_token = request.GET.get('t', '')
|
||||||
|
if fileshare_token: # download dir from dir shared link
|
||||||
|
try:
|
||||||
|
fileshare = FileShare.objects.get(token=fileshare_token)
|
||||||
|
except FileShare.DoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
permission = get_user_permission(request, repo_id)
|
# Can not download upper dir of shared dir.
|
||||||
if permission:
|
allow_download = True if path.startswith(fileshare.path) else False
|
||||||
|
else:
|
||||||
|
allow_download = True if get_user_permission(request, repo_id) else False
|
||||||
|
|
||||||
|
if allow_download:
|
||||||
dir_id = seafserv_threaded_rpc.get_dirid_by_path (repo.head_cmmt_id,
|
dir_id = seafserv_threaded_rpc.get_dirid_by_path (repo.head_cmmt_id,
|
||||||
path.encode('utf-8'))
|
path.encode('utf-8'))
|
||||||
token = seafserv_rpc.web_get_access_token(repo_id,
|
token = seafserv_rpc.web_get_access_token(repo_id,
|
||||||
@ -2676,14 +2823,9 @@ def repo_download_dir(request, repo_id):
|
|||||||
'download-dir',
|
'download-dir',
|
||||||
request.user.username)
|
request.user.username)
|
||||||
else:
|
else:
|
||||||
return render_permission_error(request, _(u'Unable to access file'))
|
return render_error(request, _(u'Unable to download "%s"') % dirname )
|
||||||
|
|
||||||
if len(path) > 1:
|
|
||||||
filename = os.path.basename(path)
|
|
||||||
else:
|
|
||||||
filename = repo.name
|
|
||||||
url = gen_file_get_url(token, filename)
|
|
||||||
|
|
||||||
|
url = gen_file_get_url(token, dirname)
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
|
|
||||||
def events(request):
|
def events(request):
|
||||||
|
Loading…
Reference in New Issue
Block a user