mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-19 18:29:23 +00:00
[org] New org-info UI & Improve quota feature
Use <table> instead of <li> at org-info page. Remove related repos when delete a org-user.
This commit is contained in:
@@ -48,40 +48,79 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="members">
|
<div id="members">
|
||||||
<ul class="user-list">
|
<table>
|
||||||
{% for u in users %}
|
<tr>
|
||||||
{% with e=u.email id=u.email|id_or_email %}
|
<th width="25%">{% trans "Email" %}</th>
|
||||||
<li class="user ovhd">
|
<th width="10%">{% trans "Status" %}</th>
|
||||||
<a href="{% url 'user_profile' id %}" class="pic fleft">{% avatar e 48 %}</a>
|
<th width="20%">{% trans "Space Used" %}</th>
|
||||||
<div class="txt fright">
|
<th width="25%">{% trans "Create At / Last Login" %}</th>
|
||||||
<a class="name" href="{% url 'user_profile' id %}">{{ e|email2nickname }}</a>
|
<th width="20%">{% trans "Operations" %}</th>
|
||||||
<p>{{ e }}</p>
|
</tr>
|
||||||
</div>
|
|
||||||
</li>
|
{% for user in users %}
|
||||||
{% endwith %}
|
<tr data-userid="{{user.email}}">
|
||||||
{% endfor %}
|
<td><a href="{% url 'user_info' user.email %}">{{ user.email }}</a></td>
|
||||||
</ul>
|
<td>
|
||||||
|
<div class="user-status">
|
||||||
|
{% if user.is_active %}
|
||||||
|
<span class="user-status-cur-value">{% trans "Active" %}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="user-status-cur-value">{% trans "Inactive" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
<img src="{{MEDIA_URL}}img/edit_12.png" alt="{% trans "Edit"%}" title="{% trans "Edit"%}" class="user-status-edit-icon cspt vh" />
|
||||||
|
</div>
|
||||||
|
<select name="permission" class="user-status-select hide">
|
||||||
|
<option value="1" {%if user.is_active %}selected="selected"{% endif %}>{% trans "Active" %}</option>
|
||||||
|
<option value="0" {%if not user.is_active %}selected="selected"{% endif %}>{% trans "Inactive"%}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if CALC_SHARE_USAGE %}
|
||||||
|
{{ user.self_usage|filesizeformat }} + {{ user.share_usage|filesizeformat }} {% if user.quota > 0 %} / {{ user.quota|filesizeformat }} {% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ user.self_usage|filesizeformat }} {% if user.quota > 0 %} / {{ user.quota|filesizeformat }} {% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td style="font-size:11px;">
|
||||||
|
{{ user.ctime|tsstr_sec }} / {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if not user.is_self %}
|
||||||
|
<a href="#" class="remove-user-btn op vh" data-url="{% url 'user_remove' user.id %}" data-target="{{ user.email }}">{% trans "Delete" %}</a>
|
||||||
|
<a href="#" class="reset-user-btn op vh" data-url="{% url 'user_reset' user.id %}" data-target="{{ user.email }}">{% trans "ResetPwd" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="grps" class="hide">
|
<div id="grps" class="hide">
|
||||||
{% if groups %}
|
{% if groups %}
|
||||||
<ul class="group-list">
|
<table>
|
||||||
{% for grp in groups %}
|
<tr>
|
||||||
<li class="group ovhd">
|
<th width="30%">{% trans "Name" %}</th>
|
||||||
<a href="{{ SITE_ROOT }}group/{{ grp.id }}/" class="no-deco pic fleft">{% grp_avatar grp.id 48 %}</a>
|
<th width="30%">{% trans "Creator" %}</th>
|
||||||
<div class="txt fright">
|
<th width="25%">{% trans "Create At" %}</th>
|
||||||
<h4><a href="{{ SITE_ROOT }}group/{{ grp.id }}/">{{ grp.group_name }}</a></h4>
|
<th width="15%">{% trans "Operations" %}</th>
|
||||||
<p><span class="item-name">{% trans "Creator: "%}</span>{{ grp.creator_name }}</p>
|
</tr>
|
||||||
<p><span class="item-name">{% trans "Time: "%}</span>{{ grp.timestamp|tsstr_sec }}</p>
|
{% for group in groups %}
|
||||||
</div>
|
<tr>
|
||||||
</li>
|
<td><a href="{{ SITE_ROOT }}group/{{ group.id }}/">{{ group.group_name }}</a></td>
|
||||||
|
<td>{{ group.creator_name }}</td>
|
||||||
|
<td>{{ group.timestamp|tsstr_sec }}</td>
|
||||||
|
<td><a href="#" data-url="{% url 'group_remove' group.id %}" data-target="{{ group.group_name }}" class="group-remove-btn op vh">{% trans "Delete" %}</a></td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans "None" %}</p>
|
<p>{% trans "None" %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="activate-msg" class="hide">
|
||||||
|
<p>{% trans "Activating..., please wait" %}</p>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_script %}
|
{% block extra_script %}
|
||||||
@@ -126,6 +165,76 @@ $('#set-quota-form .submit').click(function() {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
addConfirmTo($('.group-remove-btn'), {
|
||||||
|
'title': "{% trans "Delete Group" %}",
|
||||||
|
'con': "{% trans "Are you sure you want to delete %s ?" %}"
|
||||||
|
});
|
||||||
|
addConfirmTo($('.remove-user-btn'), {
|
||||||
|
'title':"{% trans "Delete User" %}",
|
||||||
|
'con':"{% trans "Are you sure you want to delete %s ?" %}"
|
||||||
|
});
|
||||||
|
addConfirmTo($('.reset-user-btn'), {
|
||||||
|
'title':"{% trans "Password Reset" %}",
|
||||||
|
'con':"{% trans "Are you sure you want to reset the password of %s ?" %}"
|
||||||
|
});
|
||||||
|
$('tr:gt(0)').hover(
|
||||||
|
function() {
|
||||||
|
$(this).find('.user-status-edit-icon').removeClass('vh');
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
$(this).find('.user-status-edit-icon').addClass('vh');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$('.user-status-edit-icon').click(function() {
|
||||||
|
$(this).parent().addClass('hide');
|
||||||
|
$(this).parent().next().removeClass('hide'); // show 'select'
|
||||||
|
});
|
||||||
|
$('.user-status-select').change(function() {
|
||||||
|
var select = $(this),
|
||||||
|
select_val = select.val(),
|
||||||
|
uid = select.parents('tr').attr('data-userid'),
|
||||||
|
url = "{{ SITE_ROOT }}useradmin/toggle_status/" + uid + "/?s=" + select_val;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
cache: false,
|
||||||
|
beforeSend: function() {
|
||||||
|
if (select_val == 1) {
|
||||||
|
// show activating popup
|
||||||
|
$('#activate-msg').modal();
|
||||||
|
$('#simplemodal-container').css({'height':'auto'});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
if (data['email_sent']) {
|
||||||
|
feedback("{% trans "Edit succeeded, an email has been sent." %}", 'success');
|
||||||
|
} else if (data['email_sent'] === false) {
|
||||||
|
feedback("{% trans "Edit succeeded, but failed to send email, please check your email configuration." %}", 'success');
|
||||||
|
} else {
|
||||||
|
feedback("{% trans "Edit succeeded" %}", 'success');
|
||||||
|
}
|
||||||
|
select.prev().children('span').html(select.children('option[value="' +select.val() + '"]').text());
|
||||||
|
select.addClass('hide');
|
||||||
|
select.prev().removeClass('hide');
|
||||||
|
$.modal.close();
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
feedback("{% trans "Edit failed." %}", 'error');
|
||||||
|
select.addClass('hide');
|
||||||
|
select.prev().removeClass('hide');
|
||||||
|
$.modal.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$(document).click(function(e) {
|
||||||
|
var target = e.target || event.srcElement;
|
||||||
|
// target can't be edit-icon
|
||||||
|
if (!$('.user-status-edit-icon, .user-status-select').is(target)) {
|
||||||
|
$('.user-status').removeClass('hide');
|
||||||
|
$('.user-status-select').addClass('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -191,7 +191,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^sys/publinkadmin/$', sys_publink_admin, name='sys_publink_admin'),
|
url(r'^sys/publinkadmin/$', sys_publink_admin, name='sys_publink_admin'),
|
||||||
url(r'^sys/notificationadmin/', notification_list, name='notification_list'),
|
url(r'^sys/notificationadmin/', notification_list, name='notification_list'),
|
||||||
url(r'^useradmin/add/$', user_add, name="user_add"),
|
url(r'^useradmin/add/$', user_add, name="user_add"),
|
||||||
(r'^useradmin/remove/(?P<user_id>[^/]+)/$', user_remove),
|
url(r'^useradmin/remove/(?P<user_id>[^/]+)/$', user_remove, name="user_remove"),
|
||||||
url(r'^useradmin/search/$', user_search, name="user_search"),
|
url(r'^useradmin/search/$', user_search, name="user_search"),
|
||||||
url(r'^useradmin/makeadmin/(?P<user_id>[^/]+)/$', user_make_admin, name='user_make_admin'),
|
url(r'^useradmin/makeadmin/(?P<user_id>[^/]+)/$', user_make_admin, name='user_make_admin'),
|
||||||
url(r'^useradmin/removeadmin/(?P<user_id>[^/]+)/$', user_remove_admin, name='user_remove_admin'),
|
url(r'^useradmin/removeadmin/(?P<user_id>[^/]+)/$', user_remove_admin, name='user_remove_admin'),
|
||||||
|
@@ -14,7 +14,7 @@ from django.utils.translation import ugettext as _
|
|||||||
import seaserv
|
import seaserv
|
||||||
from seaserv import seafile_api, seafserv_rpc, is_passwd_set, \
|
from seaserv import seafile_api, seafserv_rpc, is_passwd_set, \
|
||||||
get_related_users_by_repo, get_related_users_by_org_repo, \
|
get_related_users_by_repo, get_related_users_by_org_repo, \
|
||||||
CALC_SHARE_USAGE, seafserv_threaded_rpc, \
|
CALC_SHARE_USAGE, seafserv_threaded_rpc, ccnet_threaded_rpc, \
|
||||||
get_user_quota_usage, get_user_share_usage
|
get_user_quota_usage, get_user_share_usage
|
||||||
from pysearpc import SearpcError
|
from pysearpc import SearpcError
|
||||||
|
|
||||||
@@ -1356,10 +1356,17 @@ def space_and_traffic(request):
|
|||||||
|
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
|
|
||||||
quota = seafserv_threaded_rpc.get_user_quota(username)
|
org = ccnet_threaded_rpc.get_orgs_by_user(username)
|
||||||
quota_usage = 0
|
if not org:
|
||||||
share_usage = 0
|
quota = seafserv_threaded_rpc.get_user_quota(username)
|
||||||
my_usage = get_user_quota_usage(username)
|
my_usage = get_user_quota_usage(username)
|
||||||
|
else:
|
||||||
|
org_id = org[0].org_id
|
||||||
|
quota = seafserv_threaded_rpc.get_org_user_quota(org_id,
|
||||||
|
username)
|
||||||
|
my_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org_id,
|
||||||
|
username)
|
||||||
|
|
||||||
rates = {}
|
rates = {}
|
||||||
if CALC_SHARE_USAGE:
|
if CALC_SHARE_USAGE:
|
||||||
share_usage = get_user_share_usage(username)
|
share_usage = get_user_share_usage(username)
|
||||||
@@ -1369,6 +1376,7 @@ def space_and_traffic(request):
|
|||||||
rates['share_usage'] = str(float(share_usage)/quota * 100) + '%'
|
rates['share_usage'] = str(float(share_usage)/quota * 100) + '%'
|
||||||
else:
|
else:
|
||||||
quota_usage = my_usage
|
quota_usage = my_usage
|
||||||
|
share_usage = 0
|
||||||
if quota > 0:
|
if quota > 0:
|
||||||
rates['quota_usage'] = str(float(my_usage)/quota * 100) + '%'
|
rates['quota_usage'] = str(float(my_usage)/quota * 100) + '%'
|
||||||
|
|
||||||
|
@@ -173,14 +173,23 @@ def sys_user_admin(request):
|
|||||||
for user in users:
|
for user in users:
|
||||||
if user.props.id == request.user.id:
|
if user.props.id == request.user.id:
|
||||||
user.is_self = True
|
user.is_self = True
|
||||||
|
|
||||||
|
org = ccnet_threaded_rpc.get_orgs_by_user(user.email)
|
||||||
try:
|
try:
|
||||||
user.self_usage = seafile_api.get_user_self_usage(user.email)
|
if not org:
|
||||||
user.share_usage = seafile_api.get_user_share_usage(user.email)
|
user.self_usage = seafile_api.get_user_self_usage(user.email)
|
||||||
user.quota = seafile_api.get_user_quota(user.email)
|
user.share_usage = seafile_api.get_user_share_usage(user.email)
|
||||||
|
user.quota = seafile_api.get_user_quota(user.email)
|
||||||
|
else:
|
||||||
|
org_id = org[0].org_id
|
||||||
|
user.self_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org_id, user.email)
|
||||||
|
user.share_usage = 0 #seafile_api.get_user_share_usage(user.email)
|
||||||
|
user.quota = seafserv_threaded_rpc.get_org_user_quota(org_id, user.email)
|
||||||
except:
|
except:
|
||||||
user.self_usage = -1
|
user.self_usage = -1
|
||||||
user.share_usage = -1
|
user.share_usage = -1
|
||||||
user.quota = -1
|
user.quota = -1
|
||||||
|
|
||||||
# check user's role
|
# check user's role
|
||||||
if user.role == GUEST_USER:
|
if user.role == GUEST_USER:
|
||||||
user.is_guest = True
|
user.is_guest = True
|
||||||
@@ -320,11 +329,17 @@ def sys_user_admin_admins(request):
|
|||||||
def user_info(request, email):
|
def user_info(request, email):
|
||||||
owned_repos = seafile_api.get_owned_repo_list(email)
|
owned_repos = seafile_api.get_owned_repo_list(email)
|
||||||
|
|
||||||
quota = seafile_api.get_user_quota(email)
|
org = ccnet_threaded_rpc.get_orgs_by_user(email)
|
||||||
quota_usage = 0
|
if not org:
|
||||||
share_usage = 0
|
my_usage = seafile_api.get_user_self_usage(email)
|
||||||
my_usage = 0
|
quota = seafile_api.get_user_quota(email)
|
||||||
my_usage = seafile_api.get_user_self_usage(email)
|
else:
|
||||||
|
org_id = org[0].org_id
|
||||||
|
my_usage =seafserv_threaded_rpc. \
|
||||||
|
get_org_user_quota_usage(org_id, email)
|
||||||
|
quota = seafserv_threaded_rpc. \
|
||||||
|
get_org_user_quota(org_id, email)
|
||||||
|
|
||||||
if CALC_SHARE_USAGE:
|
if CALC_SHARE_USAGE:
|
||||||
try:
|
try:
|
||||||
share_usage = seafile_api.get_user_share_usage(email)
|
share_usage = seafile_api.get_user_share_usage(email)
|
||||||
@@ -333,6 +348,7 @@ def user_info(request, email):
|
|||||||
share_usage = 0
|
share_usage = 0
|
||||||
quota_usage = my_usage + share_usage
|
quota_usage = my_usage + share_usage
|
||||||
else:
|
else:
|
||||||
|
share_usage = 0
|
||||||
quota_usage = my_usage
|
quota_usage = my_usage
|
||||||
|
|
||||||
# Repos that are share to user
|
# Repos that are share to user
|
||||||
@@ -371,8 +387,13 @@ def user_set_quota(request, email):
|
|||||||
quota_mb = f.cleaned_data['quota']
|
quota_mb = f.cleaned_data['quota']
|
||||||
quota = quota_mb * (1 << 20)
|
quota = quota_mb * (1 << 20)
|
||||||
|
|
||||||
|
org = ccnet_threaded_rpc.get_orgs_by_user(email)
|
||||||
try:
|
try:
|
||||||
seafile_api.set_user_quota(email, quota)
|
if not org:
|
||||||
|
seafile_api.set_user_quota(email, quota)
|
||||||
|
else:
|
||||||
|
org_id = org[0].org_id
|
||||||
|
seafserv_threaded_rpc.set_org_user_quota(org_id, email, quota)
|
||||||
except:
|
except:
|
||||||
result['error'] = _(u'Failed to set quota: internal server error')
|
result['error'] = _(u'Failed to set quota: internal server error')
|
||||||
return HttpResponse(json.dumps(result), status=500, content_type=content_type)
|
return HttpResponse(json.dumps(result), status=500, content_type=content_type)
|
||||||
@@ -383,7 +404,6 @@ def user_set_quota(request, email):
|
|||||||
result['error'] = str(f.errors.values()[0])
|
result['error'] = str(f.errors.values()[0])
|
||||||
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
|
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
|
||||||
|
|
||||||
|
|
||||||
@login_required_ajax
|
@login_required_ajax
|
||||||
@sys_staff_required
|
@sys_staff_required
|
||||||
def sys_org_set_quota(request, org_id):
|
def sys_org_set_quota(request, org_id):
|
||||||
@@ -413,6 +433,12 @@ def user_remove(request, user_id):
|
|||||||
"""Remove user, also remove group relationship."""
|
"""Remove user, also remove group relationship."""
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(id=int(user_id))
|
user = User.objects.get(id=int(user_id))
|
||||||
|
org = ccnet_threaded_rpc.get_orgs_by_user(user.email)
|
||||||
|
if org:
|
||||||
|
org_id = org[0].org_id
|
||||||
|
org_repos = seafile_api.get_org_owned_repo_list(org_id, user.email)
|
||||||
|
for repo in org_repos:
|
||||||
|
seafile_api.remove_repo(repo.id)
|
||||||
user.delete()
|
user.delete()
|
||||||
messages.success(request, _(u'Successfully deleted %s') % user.username)
|
messages.success(request, _(u'Successfully deleted %s') % user.username)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
@@ -753,6 +779,7 @@ def sys_org_admin(request):
|
|||||||
@login_required
|
@login_required
|
||||||
@sys_staff_required
|
@sys_staff_required
|
||||||
def sys_org_info(request, org_id):
|
def sys_org_info(request, org_id):
|
||||||
|
|
||||||
org_id = int(org_id)
|
org_id = int(org_id)
|
||||||
org = ccnet_threaded_rpc.get_org_by_id(org_id)
|
org = ccnet_threaded_rpc.get_org_by_id(org_id)
|
||||||
|
|
||||||
@@ -763,6 +790,28 @@ def sys_org_info(request, org_id):
|
|||||||
total_quota = seafserv_threaded_rpc.get_org_quota(org_id)
|
total_quota = seafserv_threaded_rpc.get_org_quota(org_id)
|
||||||
quota_usage = seafserv_threaded_rpc.get_org_quota_usage(org_id)
|
quota_usage = seafserv_threaded_rpc.get_org_quota_usage(org_id)
|
||||||
|
|
||||||
|
last_logins = UserLastLogin.objects.filter(username__in=[x.email for x in users])
|
||||||
|
for user in users:
|
||||||
|
if user.id == request.user.id:
|
||||||
|
user.is_self = True
|
||||||
|
try:
|
||||||
|
user.self_usage =seafserv_threaded_rpc. \
|
||||||
|
get_org_user_quota_usage(org_id, user.email)
|
||||||
|
user.share_usage = 0 #seafile_api.get_user_share_usage(user.email)
|
||||||
|
user.quota = seafserv_threaded_rpc. \
|
||||||
|
get_org_user_quota(org_id, user.email)
|
||||||
|
except SearpcError as e:
|
||||||
|
logger.error(e)
|
||||||
|
user.self_usage = -1
|
||||||
|
user.share_usage = -1
|
||||||
|
user.quota = -1
|
||||||
|
|
||||||
|
# populate user last login time
|
||||||
|
user.last_login = None
|
||||||
|
for last_login in last_logins:
|
||||||
|
if last_login.username == user.email:
|
||||||
|
user.last_login = last_login.last_login
|
||||||
|
|
||||||
# groups
|
# groups
|
||||||
groups = ccnet_threaded_rpc.get_org_groups(org_id, -1, -1)
|
groups = ccnet_threaded_rpc.get_org_groups(org_id, -1, -1)
|
||||||
groups_count = len(groups)
|
groups_count = len(groups)
|
||||||
|
Reference in New Issue
Block a user