1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-01 23:38:37 +00:00

Merge branch 'feature/share_dir'

Conflicts:
	utils/__init__.py
	views.py
This commit is contained in:
zhengxie 2013-01-03 19:30:50 +08:00
commit 37eacd22c3
16 changed files with 577 additions and 185 deletions

View File

@ -896,10 +896,14 @@ textarea:-moz-placeholder {/* for FF */
.repo-op .op-btn {
*margin-left:5px;/* for ie 7*/
}
#upload-file {
#upload-file{
padding-left:19px;
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 {
padding-left:23px;
background-image:url('../img/folder-add.png');
@ -1364,7 +1368,8 @@ textarea:-moz-placeholder {/* for FF */
color:#444;
text-align:right;
}
#shared-link {
#shared-link,
#shared-link-text {
border:0;
}
.file-op {

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

View File

@ -12,7 +12,7 @@ class AnonymousShare(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)
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)
ctime = models.DateTimeField(default=datetime.datetime.now)
view_cnt = models.IntegerField(default=0)
s_type = models.CharField(max_length=2, db_index=True, default='f') # `f` or `d`

View File

@ -80,18 +80,22 @@
{% if fileshares %}
<table class="sharelink-list">
<tr>
<th width="45%">{% trans "File"%}</th>
<th width="45%">{% trans "File or Folder"%}</th>
<th width="25%">{% trans "Library"%}</th>
<th width="15%">{% trans "Visits"%}</th>
<th width="15%">{% trans "Operations"%}</th>
</tr>
{% for fs in fileshares %}
<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>{{ fs.view_cnt }}</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>
</td>
</tr>
@ -173,8 +177,7 @@ $('.cancel-share').click(function() {
});
$(".view-link").click(function() {
var token = $(this).attr('data'),
link = '{{ protocol }}://' + '{{ domain }}{{ SITE_ROOT }}f/' + token + '/';
var link = $(this).attr('data');
$('#link').before('<p class="hide">' + link + '</p>');
$('#shared-link').val(link).css('width', $('#link').prev().width() + 2);
$("#link").modal({appendTo:'#main'});

View File

@ -3,7 +3,8 @@ import os
import simplejson as json
from django.core.mail import send_mail
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.template import Context, loader, RequestContext
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.views import validate_owner, is_registered_user
from seahub.utils import render_permission_error, string2list, render_error, \
gen_token
gen_token, gen_shared_link
try:
from seahub.settings import CLOUD_MODE
except ImportError:
CLOUD_MODE = False
from seahub.settings import SITE_ROOT
@login_required
def share_repo(request):
@ -176,7 +178,6 @@ def repo_remove_share(request):
next = request.META.get('HTTP_REFERER', None)
if not next:
from seahub.settings import SITE_ROOT
next = SITE_ROOT
return HttpResponseRedirect(next)
@ -233,13 +234,17 @@ def share_admin(request):
# link.repo_name = repo.name
# link.remain_time = anon_share_token_generator.get_remain_time(link.token)
# File shared links
# Shared links
fileshares = FileShare.objects.filter(username=username)
p_fileshares = [] # personal file share
for fs in fileshares:
if is_personal_repo(fs.repo_id):
# only list files in personal repos
fs.filename = os.path.basename(fs.path)
if is_personal_repo(fs.repo_id): # only list files in personal repos
if fs.s_type == 'f':
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)
p_fileshares.append(fs)
@ -248,8 +253,6 @@ def share_admin(request):
"shared_repos": shared_repos,
# "out_links": out_links,
"fileshares": p_fileshares,
"protocol": request.is_secure() and 'https' or 'http',
"domain": RequestSite(request).domain,
}, context_instance=RequestContext(request))
@login_required
@ -371,17 +374,32 @@ def remove_anonymous_share(request, token):
@login_required
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():
raise Http404
content_type = 'application/json; charset=utf-8'
repo_id = request.GET.get('repo_id')
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
repo_id = request.GET.get('repo_id', '')
share_type = request.GET.get('type', 'f') # `f` or `d`
path = request.GET.get('p', '')
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(
username=request.user.username).filter(path=path)
@ -396,6 +414,7 @@ def get_shared_link(request):
fs.repo_id = repo_id
fs.path = path
fs.token = token
fs.s_type = 'f' if share_type == 'f' else 'd'
try:
fs.save()
@ -403,8 +422,10 @@ def get_shared_link(request):
err = _('Failed to get the link, please retry it.')
data = json.dumps([{'error': err}])
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)
@login_required

View File

@ -156,122 +156,9 @@ $('#view-original, #download').click(function() {
$('#edit').click(function() {
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 %}
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('{{ 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();
});
//share link
{% include "snippets/shared_link_js.html" %}
//star
$('#star').click(function() {

View File

@ -17,10 +17,18 @@
<div class="w100 ovhd">
<h2 class="fleft">{{repo.props.name}}</h2>
<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>
{% if user_perm == 'rw' %}
<button id="recycle-btn" data="{% url 'repo_recycle_view' repo.id %}">{% trans "Trash"%}</button>
{% endif %}
{% endif %}
</div>
</div>
{% if user_perm == 'r' %}
@ -80,13 +88,16 @@
{% endif %}
{% endfor %}
</p>
{% if user_perm == 'rw' %}
<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 id="add-new-dir" class="op-btn">{% trans "New Directory"%}</button>
<button id="add-new-file" class="op-btn">{% trans "New File"%}</button>
{% endif %}
</div>
{% endif %}
</div>
<!-- /.repo-file-list-topbar -->
<table class="repo-file-list">
@ -119,7 +130,8 @@
{% if user_perm %}
<div class="repo-file-op vh">
<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>
{% 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" />
@ -298,15 +310,16 @@
<div id="file-share" class="hide">
<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>
<form id="link-send-form" class="hide">
<label>{% trans "Send to:"%}</label><br />
<textarea id="link-send-input" name="email" placeholder="{% trans "Emails, Seperated by ','"%}"></textarea><br />
<input type="hidden" name="file_shared_link" value="" />
<input type="submit" value="{% trans "Submit"%}" class="submit" />
<p class="error hide"></p>
<p id="sending" class="hide">{% trans "Sending..."%}</p>
<form id="link-send-form" action="" method="post" class="hide">
<h3>Send Link</h3>
<label>{% trans "Send to:"%}</label><br />
<textarea id="link-send-input" name="email" placeholder="{% trans "Emails, Seperated by ','"%}"></textarea><br />
<input type="hidden" name="file_shared_link" value="{{ dir_shared_link }}" />
<input type="submit" value="{% trans "Submit"%}" class="submit" />
<p class="error hide"></p>
<p id="sending" class="hide">{% trans "Sending..."%}</p>
</form>
</div>
@ -602,7 +615,7 @@ $('#add-new-file-form').submit(function() {
$('#add-new-dir-form').submit(function() {
var new_dir = $(this).find('input[name="new_dir_name"]').val();
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;
}
@ -755,24 +768,31 @@ $('.file-share').click(function() {
var filename = $(this).data('name');
function showPopup(link) {
$('#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>');
$('#shared-link').css({'width':$('#linkwidth').width() + 2});
$('#file-share').modal({'focus':false}); // in ff: if 'focus' is true, 'shared-link' gets the focus
$('#shared-link-text').css({'width':$('#linkwidth').width() + 2});
$('#file-share').modal({'focus':false}); // in ff: if 'focus' is true, 'shared-link-text' gets the focus
$('#linkwidth').remove();
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
}
if ($(this).data('link')) {
showPopup($(this).data('link'));
} 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({
url: '{% url 'get_shared_link' %}?repo_id={{ repo.id }}&p={{ path|urlencode }}' + e(filename),
url: aj_url,
dataType: 'json',
cache: false,
contentType: 'application/json; charset=utf-8',
success: function(data) {
if (data.length > 0) {
showPopup('{{ protocol }}://{{ domain }}{{ SITE_ROOT }}f/' + data[0]['token'] + '/');
showPopup(data[0]['shared_link']);
}
},
error: function(xhr, ajaxOptions, thrownError) {
@ -839,9 +859,15 @@ $("#link-send-form").submit(function() {
});
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/bottom_bar.html" %}
</script>
{% include 'snippets/file_upload_progress_js.html' %}
{% endblock %}

View File

@ -13,7 +13,20 @@
<h2 id="view-hd">{{ file_name }}</h2>
<div id="file">
<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>
{% endif %}
{% 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>

View 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();
});

View 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 %}

View File

@ -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, \
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, \
server_repo_size
server_repo_size, get_file_id_by_path
from service import get_binding_peerids, is_valid_filename, check_permission,\
is_passwd_set

View File

@ -669,6 +669,13 @@ def get_file_size(file_id):
fs = 0
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):
"""Give a repo id, returns a list of users of:
- the repo owner

View File

@ -57,7 +57,9 @@ urlpatterns = patterns('',
(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'^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'^publicrepo/create/$', public_repo_create),
(r'^events/$', events),
@ -77,7 +79,7 @@ urlpatterns = patterns('',
url(r'^useradmin/password/reset/(?P<user_id>[^/]+)/$', user_reset, name='user_reset'),
### Apps ###
(r'^api/', include('api.urls')),
# (r'^api/', include('api.urls')),
(r'^api2/', include('api2.urls')),
(r'^avatar/', include('avatar.urls')),
(r'^notification/', include('notifications.urls')),

View File

@ -7,6 +7,7 @@ import stat
import urllib2
import json
from django.contrib.sites.models import RequestSite
from django.shortcuts import render_to_response
from django.template import RequestContext
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]
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
View File

@ -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, \
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_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 signals import repo_created, repo_deleted
@ -64,7 +64,7 @@ from forms import AddUserForm, RepoCreateForm, RepoNewDirForm, RepoNewFileForm,\
FileCommentForm, RepoRenameFileForm, RepoPassowrdForm, SharedRepoCreateForm,\
SetUserQuotaForm
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, \
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1, \
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)
org_id = -1
if request.user.org:
if hasattr(request.user, 'org') and request.user.org:
org_id = request.user.org['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:
dirent.last_modified = last_modified_info.get(dirent.obj_name, 0)
dirent.sharelink = ''
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)
else:
file_list.append(dirent)
dirent.file_size = get_file_size(dirent.obj_id)
dirent.starred = False
dirent.sharelink = ''
fpath = os.path.join(path, dirent.obj_name)
if fpath in starred_files:
dirent.starred = True
for share in fileshares:
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
break
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')
else:
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):
kwargs['repo'] = self.repo
@ -379,6 +406,9 @@ class RepoView(LoginRequiredMixin, CtxSwitchRequiredMixin, RepoMixin,
kwargs['protocol'] = self.protocol
kwargs['domain'] = self.domain
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
@ -1297,13 +1327,12 @@ def repo_view_file(request, repo_id):
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)
file_shared_link = gen_shared_link(request, fileshare.token, 'f')
else:
file_shared_link = ''
# my constacts
contacts = Contact.objects.filter(user_email=request.user.username)
# Get groups this repo is shared.
if request.user.org:
@ -2416,7 +2445,7 @@ def view_shared_file(request, token):
path = fileshare.path
http_server_root = get_httpserver_root()
if path[-1] == '/':
if path[-1] == '/': # Normalize file path
path = path[:-1]
filename = os.path.basename(path)
quote_filename = urllib2.quote(filename.encode('utf-8'))
@ -2472,7 +2501,113 @@ def view_shared_file(request, token):
'DOCUMENT_CONVERTOR_ROOT': DOCUMENT_CONVERTOR_ROOT,
}, 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):
curl = DOCUMENT_CONVERTOR_ROOT + 'convert'
data = {'doctype': doctype,
@ -2653,22 +2788,34 @@ def repo_star_file(request, repo_id):
unstar_file(request.user.username, repo_id, path)
return HttpResponse(json.dumps({'success':True}), content_type=content_type)
@login_required
def repo_download_dir(request, repo_id):
repo = get_repo(repo_id)
if not repo:
return render_error(request, _(u'Library not exists'))
try:
parent_dir = request.GET['parent']
dirname = request.GET['dirname']
except KeyError:
return render_error(request, _(u'Invalid arguments'))
path = request.GET.get('p', '/')
if path[-1] != '/': # Normalize dir path
path += '/'
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)
if permission:
# Can not download upper dir of shared dir.
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,
path.encode('utf-8'))
token = seafserv_rpc.web_get_access_token(repo_id,
@ -2676,14 +2823,9 @@ def repo_download_dir(request, repo_id):
'download-dir',
request.user.username)
else:
return render_permission_error(request, _(u'Unable to access file'))
if len(path) > 1:
filename = os.path.basename(path)
else:
filename = repo.name
url = gen_file_get_url(token, filename)
return render_error(request, _(u'Unable to download "%s"') % dirname )
url = gen_file_get_url(token, dirname)
return redirect(url)
def events(request):