1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-27 11:01:14 +00:00

[sub-lib] divided into 'sync' & 'syncable share'

This commit is contained in:
llj 2013-08-12 15:28:55 +08:00
parent 896542a27c
commit af0d89743d
12 changed files with 261 additions and 92 deletions

View File

@ -1061,20 +1061,23 @@ textarea:-moz-placeholder {/* for FF */
margin-bottom:10px;
}
#tabs,
#repo-share-tabs {
#repo-share-tabs,
#file-share-tabs {
font-size:1em;
padding:0;
border:0;
min-height:150px;
margin-bottom:75px;
}
#repo-share-tabs {
#repo-share-tabs,
#file-share-tabs {
margin-bottom:0;
}
#tabs-nav {
padding:0;
}
#repo-share-tabs-nav {
#repo-share-tabs-nav,
#file-share-tabs-nav {
height:30px;
padding:0 0 0 12px;
border-bottom:1px solid #ddd;
@ -1085,13 +1088,15 @@ textarea:-moz-placeholder {/* for FF */
border:0;
background:none;
}
#repo-share-tabs-nav li {
#repo-share-tabs-nav li,
#file-share-tabs-nav li {
display:inline-block;
border:1px solid transparent;
border-bottom:0;
background:none;
}
#repo-share-tabs-nav li a {
#repo-share-tabs-nav li a,
#file-share-tabs-nav li a {
color:#666;
line-height:16px;
padding:6px 13px;
@ -1113,7 +1118,8 @@ textarea:-moz-placeholder {/* for FF */
#tabs-nav li a:hover {
color:#DD4B39;
}
#repo-share-tabs-nav li a:hover {
#repo-share-tabs-nav li a:hover,
#file-share-tabs-nav li a:hover {
color:#333;
}
#tabs .ui-tabs-panel {
@ -1619,17 +1625,20 @@ textarea:-moz-placeholder {/* for FF */
.tab-popup .hd {
padding-left:12px;
}
#repo-share-tabs-nav .ui-state-active {
#repo-share-tabs-nav .ui-state-active,
#file-share-tabs-nav .ui-state-active {
border-color:#ddd;
margin-bottom:-1px;
}
#repo-share-tabs-nav .ui-state-active,
#file-share-tabs-nav .ui-state-active,
.tab-popup .ui-tabs ,
.tab-popup .ui-state-active a,
.tab-popup .bot {
background:#fafafa;
}
#repo-share-tabs-nav .ui-state-active,
#file-share-tabs-nav .ui-state-active,
.tab-popup .ui-state-active a {
border-radius:3px 3px 0 0;
}

View File

@ -152,6 +152,9 @@ def share_repo(request):
Handle POST method to share a repo to public/groups/users based on form
data. Return to ``myhome`` page and notify user whether success or failure.
"""
next = request.META.get('HTTP_REFERER', None)
if not next:
next = settings.SITE_ROOT
form = RepoShareForm(request.POST)
if not form.is_valid():
@ -171,7 +174,7 @@ def share_repo(request):
if not validate_owner(request, repo_id):
msg = _(u'Only the owner of the library has permission to share it.')
messages.error(request, msg)
return HttpResponseRedirect(reverse('myhome'))
return HttpResponseRedirect(next)
# Parsing input values.
@ -200,7 +203,7 @@ def share_repo(request):
if not check_user_share_quota(from_email, repo, users=share_to_users,
groups=share_to_groups):
messages.error(request, _('Failed to share "%s", no enough quota. <a href="http://seafile.com/">Upgrade account.</a>') % repo.name)
return HttpResponseRedirect(reverse('myhome'))
return HttpResponseRedirect(next)
for group in share_to_groups:
share_to_group(request, repo, from_email, group, permission)
@ -210,7 +213,7 @@ def share_repo(request):
mail_sended.send(sender=None, user=request.user.username, email=email)
share_to_user(request, repo, from_email, email, permission)
return HttpResponseRedirect(reverse('myhome'))
return HttpResponseRedirect(next)
@login_required
def repo_remove_share(request):

View File

@ -218,6 +218,13 @@
<script type="text/javascript" src="{{ MEDIA_URL }}js/base.js?t=1375754100"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/select2.min.js?t=1375754100"></script>
<script type="text/javascript">
function ajaxErrorHandler(xhr, textStatus, errorThrown) {
if (xhr.responseText) {
feedback(jQuery.parseJSON(xhr.responseText).error, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
}
{% if request.user.is_authenticated %}
{% if request.cur_note %}
$('#info-bar .close').click(function() {

View File

@ -193,7 +193,23 @@
{% include "snippets/group_recommend_form.html" %}
{% endwith %}
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
<div id="dir-sync-popup" class="hide">
<h3 class="hd">{% trans "Sync %(dir_name)s" %}</h3>
<p class="sub-repo-create-tip hide">{% trans "You need to create a sub-library from this directory to sync it." %}</p>
<button class="create-sub-repo hide">{% trans "Create" %}</button>
<p class="sub-repo-creating-progress hide"></p>
<p class="sub-repo-exist-tip hide">{% trans "The sub-library used to sync this directory already exists." %}</p>
<button id="sync-sub-repo" class="hide">{% trans "Sync" %}</button>
<p class="tip" style="margin-bottom:0">{% trans "A sub-library can be created from any folder. It can be independently synced and shared. Files in the sub-library will be automatically kept in sync with the directory of the origin library." %}</p>
</div>
{% endif %}
{% url 'share_repo' as repo_share_url %}
{% with post_url=repo_share_url groups=joined_groups %}
{% include "snippets/repo_share_form.html" %} {# for sub-lib share #}
{% endwith %}
{% endblock %}
{% block extra_script %}
@ -766,7 +782,7 @@ $('.file-share, .dir-share, #share-cur-dir').click(function() {
if (op.hasClass('dir-share')) {
aj_url += '&type=d';
type = 'd';
}
}
}
showSharePopup(op, name, aj_url, type, cur_path);
@ -774,40 +790,39 @@ $('.file-share, .dir-share, #share-cur-dir').click(function() {
});
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
$('.create-sub-repo').click(function() {
var repo_name = $(this).parents('.dir-item').data('name');
feedback('{% trans "Creating sub-library..." %}', 'info');
$('.dir-sync').click(function() {
var dir_name = $(this).parents('.dir-item').data('name');
// check if the sub-library exists
$.ajax({
url: '{{ SITE_ROOT }}repo/create_sub_repo/',
type: 'POST',
cache: false,
beforeSend: prepareCSRFToken,
url: '{% url 'check_sub_repo' repo.id %}?p=' + e(cur_path + dir_name),
dataType: 'json',
data: {
orig_repo_id: '{{repo.id}}',
orig_path: cur_path + repo_name,
repo_name: repo_name,
repo_desc: repo_name
},
success: function(data) {
if (data['success']) {
var msg = "{% trans "Created sub-library %(name)s" %}";
msg = msg.replace('%(name)s', '<a href="{{SITE_ROOT}}repo/' + data['repo_id'] + '">' + repo_name + '</a>');
feedback(msg, 'success');
var popup = $('#dir-sync-popup'),
hd = $('.hd', popup);
popup.modal({appendTo:'#main', minWidth:250, maxWidth:400});
$('#simplemodal-container').css({'height':'auto'});
hd.html(hd.html().replace('%(dir_name)s', '<span class="op-target">' + dir_name + '</span>'));
if (data['exist']) {
// sub-lib already exists
$('.sub-repo-exist-tip', popup).removeClass('hide');
$('#sync-sub-repo').data('sub_repo_id', data['sub_repo_id']).removeClass('hide');
} else {
feedback(data['error'], 'error');
// sub-lib doesn't exist
$('.sub-repo-create-tip, .create-sub-repo', popup).removeClass('hide');
$('.create-sub-repo', popup).click(createSubRepo).data('dir_name', dir_name).data('context', popup);
}
$('#sync-sub-repo').click(function() {
window.open('{{ SITE_ROOT }}seafile_access_check/?repo_id=' + $(this).data('sub_repo_id'));
});
},
error: function(xhr, textStatus, errorThrown) {
if (xhr.responseText) {
feedback(jQuery.parseJSON(xhr.responseText).error, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
}
});
error: ajaxErrorHandler
});
return false;
});
{% endif %}
} //function 'opOnDirent' ends here
@ -1028,16 +1043,64 @@ function updateCmt() {
return false;
});
},
error:function(xhr, textStatus, errorThrown) {
if (xhr.responseText) {
feedback(jQuery.parseJSON(xhr.responseText).error, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
}
error: ajaxErrorHandler
});
}
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
function createSubRepo() {
var repo_name = $(this).data('dir_name'),
path = $(this).data('path'), // for '.create-sub-repo' in share panel
context = $(this).data('context'),
progress = $('.sub-repo-creating-progress', context);
$('.sub-repo-create-tip, .create-sub-repo', context).addClass('hide');
progress.html('{% trans "Creating sub-library..." %}').removeClass('hide').css({'color':'red'});
$.ajax({
url: '{% url 'create_sub_repo' repo.id %}',
type: 'POST',
cache: false,
beforeSend: prepareCSRFToken,
dataType: 'json',
data: {
orig_path: path || (cur_path + repo_name),
repo_name: repo_name,
repo_desc: repo_name
},
success: function(data) {
progress.html('{% trans "Sub-library is created." %}');
$('#sync-sub-repo, #share-sub-repo', context).removeClass('hide').data('sub_repo_id', data['sub_repo_id']);
},
error: ajaxErrorHandler
});
}
// sub-repo-share submit
$('#share-submit-btn').click(function() {
var cur_tab_id = $('#repo-share-tabs-nav .ui-tabs-selected a').attr('href');
var post_data = '';
switch(cur_tab_id) {
case '#share-grp-options':
case '#share-contact-options':
$(cur_tab_id + ' .checkbox-checked .checkbox-orig').each(function() {
post_data += $(this).val() + ',';
});
}
if (!post_data) {
apply_form_error('repo-share-form', '{% trans "Please select at least 1 group or 1 contact." %}');
return false;
}
var form = $("#repo-share-form");
//'email_or_group' is required. It was in 'share-enter' tab, but 'share-enter' was removed for sub-lib share.
form.append('<input type="hidden" name="email_or_group" />');
$('[name="email_or_group"]', form).val(post_data);
form.submit();
disable($(this));
});
{% endif %}
{% include "snippets/list_commit_detail.html" %}
{% include "snippets/shared_link_js.html" %}
{% include "snippets/bottom_bar.html" %}

View File

@ -1,10 +1,13 @@
{% load i18n %}
<div id="file-share" class="tab-popup hide">
<h3 class="hd">{% trans 'Share %(name)s' %}</h3>
<div id="repo-share-tabs">
<ul id="repo-share-tabs-nav">
<li><a href="#private-share">{% trans "Private Share" %}</a></li>
<div id="file-share-tabs">
<ul id="file-share-tabs-nav">
<li id="private-share-tab"><a href="#private-share">{% trans "Private Share" %}</a></li>
<li><a href="#link-share">{% trans "Share Link" %}</a></li>
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
<li id="syncable-share-tab"><a href="#syncable-share">{% trans "Syncable Share" %}</a></li>
{% endif %}
</ul>
<div id="private-share">
@ -36,6 +39,17 @@
</div>
</div>
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
<div id="syncable-share">
<p class="sub-repo-create-tip hide">{% trans "You need to create a sub-library from this directory to let others sync it." %}</p>
<button class="create-sub-repo hide">{% trans "Create" %}</button>
<p class="sub-repo-creating-progress hide"></p>
<p class="sub-repo-exist-tip hide">{% trans "The sub-library used to share this directory already exists." %}</p>
<button id="share-sub-repo" class="hide">{% trans "Share" %}</button>
<p class="tip" style="margin-top:0.5em;">{% trans "A sub-library can be created from any folder. It can be independently synced and shared. Files in the sub-library will be automatically kept in sync with the directory of the origin library." %}</p>
</div>
{% endif %}
</div>
</div>

View File

@ -58,7 +58,7 @@
<a class="op dir-share" href="#" data-link="{{ dirent.sharelink }}" data-token="{{ dirent.sharetoken }}">{% trans "Share" %}</a>
{% endif %}
{% if ENABLE_SUB_LIBRARY and not repo.is_virtual %}
<a class="op create-sub-repo" href="#">{% trans "Sub-library" %}</a>
<a class="op dir-sync" href="#">{% trans "Sync" %}</a>
{% endif %}
</div>
{% if user_perm == 'rw' %}

View File

@ -3,7 +3,7 @@
<h3 class="hd">{% trans 'Share <span class="op-target"></span> '%}</h3>
<div id="repo-share-tabs">
<ul id="repo-share-tabs-nav">
<li><a href="#share-enter">{% trans 'Enter' %}</a></li>
<li id="share-enter-tab"><a href="#share-enter">{% trans 'Enter' %}</a></li>
<li><a href="#share-grp-options">{% trans 'Groups' %}</a></li>
<li><a href="#share-contact-options">{% trans 'Contacts' %}</a></li>
</ul>

View File

@ -7,10 +7,16 @@ share_list.push({value:'{{ contact.contact_email }}', label:'{{ contact.contact_
{% endfor %}
function showSharePopup(op, name, aj_url, type, cur_path) {
$('#file-share').modal({appendTo: "#main",'focus':false, containerCss:{"padding": "10px 0 0"}}); // in ff: if 'focus' is true, 'shared-link-text' gets the focus
var path;
if (op.attr('id') == 'share-cur-dir') {
path = cur_path.substr(0, cur_path.length - 1); // rm the last '/' as seafile_api treats '/xx' and '/xx/' as different
} else {
path = cur_path + name;
}
$('#file-share').modal({appendTo: "#main",'focus':false, containerCss:{"padding":"10px 0 0"}});
$("#private-share-form input[name=s_type]").val(type);
$("#private-share-form input[name=path]").val(cur_path + name);
$("#private-share-form input[name=path]").val(path);
var contacts = [];
{% for contact in contacts %}
contact_email = '{{ contact.contact_email }}';
@ -47,10 +53,43 @@ function showSharePopup(op, name, aj_url, type, cur_path) {
hd.html(hd.html().replace('%(name)s', '<span class="op-target">' + trimFilename(name, 30) + '</span>'));
if (type == 'd') {
$('#repo-share-tabs-nav li:first-child').remove();
$('#private-share').remove();
$('#private-share-tab, #private-share').remove();
} else {
$('#syncable-share-tab, #syncable-share').remove();
}
$("#file-share-tabs").tabs();
if ($('#syncable-share').length == 1) {
$.ajax({
url: '{% url 'check_sub_repo' repo.id %}?p=' + e(path),
dataType: 'json',
success: function(data) {
var context = $('#syncable-share');
if (data['exist']) {
$('.sub-repo-exist-tip, #share-sub-repo', context).removeClass('hide');
$('#share-sub-repo').data('sub_repo_id', data['sub_repo_id']);
} else {
$('.sub-repo-create-tip, .create-sub-repo', context).removeClass('hide');
$('.create-sub-repo', context).click(createSubRepo).data('dir_name', name).data('path', path).data('context', context);
}
$('#share-sub-repo').data('sub_repo_name', name).click(function() {
// get id & name before popup close
var sub_repo_id = $(this).data("sub_repo_id"),
sub_repo_name = $(this).data("sub_repo_name");
$.modal.close();
setTimeout(function() {
$("#repo-share-form").modal({appendTo: "#main", focus:false, containerCss:{"padding":"10px 0 0"}});
$('#simplemodal-container').css({'height':'auto'});
$("#repo_id").val(sub_repo_id);
$("#repo-share-form .op-target").html(sub_repo_name);
$('#share-enter-tab, #share-enter').remove();
$("#repo-share-tabs").tabs();
}, 10);
});
},
error: ajaxErrorHandler
});
}
$("#repo-share-tabs").tabs();
$('#private-share [name="emails"]').html(opts).select2({
placeholder: "{% trans "Select contacts" %}",

View File

@ -81,7 +81,6 @@ urlpatterns = patterns('',
url(r'^repo/(?P<repo_id>[-0-9a-f]{36})/privshare/$', gen_private_file_share, name='gen_private_file_share'),
url(r'^repo/(?P<repo_id>[-0-9a-f]{36})/(?P<obj_id>[0-9a-f]{40})/$', repo_access_file, name='repo_access_file'),
(r'^repo/save_settings$', repo_save_settings),
url(r'^repo/create_sub_repo/$', create_sub_repo, name='create_sub_repo'),
### share file/dir ###
url(r'^s/f/(?P<token>[a-f0-9]{10})/$', view_priv_shared_file, name="view_priv_shared_file"),
@ -114,6 +113,9 @@ urlpatterns = patterns('',
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/delete/$', delete_dirent, name='delete_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/mv/$', mv_dir, name='mv_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/cp/$', cp_dir, name='cp_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/check_sub_repo/$', check_sub_repo, name='check_sub_repo'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/create_sub_repo/$', create_sub_repo, name='create_sub_repo'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/new/$', new_file, name='new_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/rename/$', rename_dirent, name='rename_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/delete/$', delete_dirent, name='delete_file'),
@ -121,6 +123,7 @@ urlpatterns = patterns('',
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/cp/$', cp_file, name='cp_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/star/$', repo_star_file, name='repo_star_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/unstar/$', repo_unstar_file, name='repo_unstar_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/current_commit/$', get_current_commit, name='get_current_commit'),
### Apps ###

View File

@ -1305,39 +1305,6 @@ def repo_create(request):
return HttpResponseBadRequest(json.dumps(form.errors),
content_type=content_type)
@login_required
def create_sub_repo(request):
if not request.is_ajax() or request.method != 'POST':
return Http404
result = {}
content_type = 'application/json; charset=utf-8'
orig_repo_id = request.POST.get('orig_repo_id', '')
orig_path = request.POST.get('orig_path', '')
repo_name = request.POST.get('repo_name', '')
repo_desc = request.POST.get('repo_desc', '')
owner = request.user.username
if not orig_repo_id or not orig_path or not repo_name or not repo_desc:
return HttpResponseBadRequest("Invalid arguments", content_type=content_type)
try:
repo_id = seafile_api.create_virtual_repo(orig_repo_id, orig_path,
repo_name, repo_desc, owner)
except SearpcError, e:
result['error'] = e.msg
return HttpResponse(json.dumps(result), content_type=content_type)
result['success'] = True
result['repo_id'] = repo_id
repo_created.send(sender=None,
org_id=-1,
creator=owner,
repo_id=repo_id,
repo_name=repo_name)
return HttpResponse(json.dumps(result), content_type=content_type)
def render_file_revisions (request, repo_id):
"""List all history versions of a file."""
path = request.GET.get('p', '/')

View File

@ -21,6 +21,7 @@ from seahub.forms import RepoNewDirentForm, RepoRenameDirentForm
from seahub.views import get_repo_dirents
from seahub.views.repo import get_nav_path, get_fileshare, get_dir_share_link
import seahub.settings as settings
from seahub.signals import repo_created
from seahub.utils import check_filename_with_rename, star_file, unstar_file
# Get an instance of a logger
@ -644,3 +645,63 @@ def get_current_commit(request, repo_id):
return HttpResponse(json.dumps({'html': html}),
content_type=content_type)
@login_required
def check_sub_repo(request, repo_id):
'''
check if a dir has a corresponding sub_repo
'''
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
result = {}
path = request.GET.get('p')
if not path:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
try:
sub_repo = seafile_api.get_virtual_repo(repo_id, path, request.user.username)
except SearpcError, e:
result['error'] = e.msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
if sub_repo:
result['exist'] = True
result['sub_repo_id'] = sub_repo.id
else:
result['exist'] = False
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required
def create_sub_repo(request, repo_id):
if not request.is_ajax() or request.method != 'POST':
return Http404
result = {}
content_type = 'application/json; charset=utf-8'
orig_repo_id = repo_id
orig_path = request.POST.get('orig_path', '')
repo_name = request.POST.get('repo_name', '')
repo_desc = request.POST.get('repo_desc', '')
owner = request.user.username
if not orig_path or not repo_name or not repo_desc:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
try:
sub_repo_id = seafile_api.create_virtual_repo(orig_repo_id, orig_path,
repo_name, repo_desc, owner)
except SearpcError, e:
result['error'] = e.msg
return HttpResponse(json.dumps(result), status=500, content_type=content_type)
result['success'] = True
result['sub_repo_id'] = sub_repo_id
repo_created.send(sender=None, org_id=-1, creator=owner, repo_id=sub_repo_id, repo_name=repo_name)
return HttpResponse(json.dumps(result), content_type=content_type)

View File

@ -9,7 +9,7 @@ from django.template import RequestContext
from django.template.loader import render_to_string
import seaserv
from seaserv import seafile_api, MAX_UPLOAD_FILE_SIZE
from seaserv import seafile_api, MAX_UPLOAD_FILE_SIZE, get_personal_groups_by_user
from seahub.auth.decorators import login_required
from seahub.contacts.models import Contact
@ -206,6 +206,8 @@ def render_repo(request, repo):
fileshare = get_fileshare(repo.id, username, path)
dir_shared_link = get_dir_share_link(fileshare)
joined_groups = get_personal_groups_by_user(request.user.username)
return render_to_response('repo.html', {
'repo': repo,
'user_perm': user_perm,
@ -220,6 +222,7 @@ def render_repo(request, repo):
'accessible_repos': accessible_repos,
'applet_root': applet_root,
'groups': repo_groups,
'joined_groups': joined_groups,
'repo_group_str': repo_group_str,
'no_quota': no_quota,
'max_upload_file_size': max_upload_file_size,