mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-21 11:27:18 +00:00
[institution] Add institution quota
This commit is contained in:
22
seahub/institutions/migrations/0002_institutionquota.py
Normal file
22
seahub/institutions/migrations/0002_institutionquota.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('institutions', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InstitutionQuota',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('quota', models.BigIntegerField()),
|
||||
('institution', models.ForeignKey(to='institutions.Institution')),
|
||||
],
|
||||
),
|
||||
]
|
@@ -11,3 +11,17 @@ class Institution(models.Model):
|
||||
class InstitutionAdmin(models.Model):
|
||||
institution = models.ForeignKey(Institution)
|
||||
user = models.EmailField()
|
||||
|
||||
|
||||
class InstitutionQuotaManager(models.Manager):
|
||||
def get_or_none(self, *args, **kwargs):
|
||||
try:
|
||||
return self.get(*args, **kwargs).quota
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class InstitutionQuota(models.Model):
|
||||
institution = models.ForeignKey(Institution)
|
||||
quota = models.BigIntegerField()
|
||||
objects = InstitutionQuotaManager()
|
||||
|
@@ -45,17 +45,14 @@
|
||||
{% endif %}
|
||||
|
||||
<dt>{% trans "Space Used" %}</dt>
|
||||
<dd>{{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %}</dd>
|
||||
<dd>{{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %} <a href="#" class="sf-btn-link" style="margin-left:20px;" id="set-quota">{% trans "Set Quota" %}</a></dd>
|
||||
</dl>
|
||||
|
||||
<form id="set-quota-form" method="post" class="hide">{% csrf_token %}
|
||||
<h3>{% trans "Set user storage limit" %}</h3>
|
||||
<input type="hidden" name="email" value="{{ email }}" />
|
||||
<input type="text" name="space_quota" class="input" /> MB
|
||||
<p class="tip">
|
||||
<span>{% trans "An integer that is greater than or equal to 0." %}</span><br />
|
||||
<span>{% trans "Tip: 0 means default limit" %}</span>
|
||||
</p>
|
||||
<p class="tip">{% trans "Available quota:" %} {{ available_quota|seahub_filesizeformat}}</p>
|
||||
<p class="error hide"></p>
|
||||
<input type="submit" value="{% trans "Submit" %}" class="submit" />
|
||||
</form>
|
||||
@@ -140,5 +137,46 @@
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$('#set-quota').click(function() {
|
||||
$("#set-quota-form").modal({appendTo: "#main"});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#set-quota-form .submit').click(function() {
|
||||
var form = $('#set-quota-form'),
|
||||
form_id = form.attr('id'),
|
||||
space_quota = $('input[name="space_quota"]', form).val();
|
||||
|
||||
if (!$.trim(space_quota)) {
|
||||
apply_form_error(form_id, "{% trans "Space Quota can't be empty" %}");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = { 'email': $('input[name="email"]', form).val(), 'space_quota': space_quota };
|
||||
|
||||
var sb_btn = $(this);
|
||||
disable(sb_btn);
|
||||
$.ajax({
|
||||
url: '{% url 'institutions:user_set_quota' email %}',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: data,
|
||||
success: function(data) {
|
||||
location.reload(true);
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
if (xhr.responseText) {
|
||||
apply_form_error(form_id, $.parseJSON(xhr.responseText).error);
|
||||
} else {
|
||||
apply_form_error(form_id, "{% trans "Failed. Please check the network." %}");
|
||||
}
|
||||
enable(sb_btn);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import (info, useradmin, user_info, user_remove, useradmin_search,
|
||||
user_toggle_status)
|
||||
user_toggle_status, user_set_quota)
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
@@ -11,5 +11,6 @@ urlpatterns = patterns(
|
||||
url(r'^useradmin/info/(?P<email>[^/]+)/$', user_info, name='user_info'),
|
||||
url(r'^useradmin/remove/(?P<email>[^/]+)/$', user_remove, name='user_remove'),
|
||||
url('^useradmin/search/$', useradmin_search, name="useradmin_search"),
|
||||
url(r'^useradmin/set_quota/(?P<email>[^/]+)/$', user_set_quota, name='user_set_quota'),
|
||||
url(r'^useradmin/toggle_status/(?P<email>[^/]+)/$', user_toggle_status, name='user_toggle_status'),
|
||||
)
|
||||
|
25
seahub/institutions/utils.py
Normal file
25
seahub/institutions/utils.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
from seaserv import seafile_api
|
||||
from seahub.profile.models import Profile
|
||||
from seahub.institutions.models import InstitutionQuota
|
||||
|
||||
|
||||
def get_institution_space_usage(inst):
|
||||
# TODO: need to refactor
|
||||
usernames = [x.user for x in Profile.objects.filter(institution=inst.name)]
|
||||
total = 0
|
||||
for user in usernames:
|
||||
total += seafile_api.get_user_self_usage(user)
|
||||
return total
|
||||
|
||||
def get_institution_available_quota(inst):
|
||||
inst_quota = InstitutionQuota.objects.get_or_none(institution=inst)
|
||||
if inst_quota is None:
|
||||
return None
|
||||
|
||||
usernames = [x.user for x in Profile.objects.filter(institution=inst.name)]
|
||||
allocated = 0
|
||||
for user in usernames:
|
||||
allocated += seafile_api.get_user_quota(user)
|
||||
|
||||
return 0 if allocated >= inst_quota else inst_quota - allocated
|
@@ -18,13 +18,16 @@ from seahub.base.decorators import require_POST
|
||||
from seahub.base.models import UserLastLogin
|
||||
from seahub.institutions.decorators import (inst_admin_required,
|
||||
inst_admin_can_manage_user)
|
||||
from seahub.institutions.utils import get_institution_available_quota
|
||||
from seahub.profile.models import Profile, DetailedProfile
|
||||
from seahub.utils import is_valid_username, clear_token
|
||||
from seahub.utils.rpc import mute_seafile_api
|
||||
from seahub.utils.file_size import get_file_size_unit
|
||||
from seahub.views.sysadmin import email_user_on_activation, populate_user_info
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _populate_user_quota_usage(user):
|
||||
"""Populate space/share quota to user.
|
||||
|
||||
@@ -162,6 +165,8 @@ def user_info(request, email):
|
||||
else:
|
||||
g.role = _('Member')
|
||||
|
||||
available_quota = get_institution_available_quota(request.user.institution)
|
||||
|
||||
return render_to_response(
|
||||
'institutions/user_info.html', {
|
||||
'owned_repos': owned_repos,
|
||||
@@ -172,6 +177,7 @@ def user_info(request, email):
|
||||
'profile': profile,
|
||||
'd_profile': d_profile,
|
||||
'personal_groups': personal_groups,
|
||||
'available_quota': available_quota,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@require_POST
|
||||
@@ -192,6 +198,26 @@ def user_remove(request, email):
|
||||
|
||||
return HttpResponseRedirect(next)
|
||||
|
||||
@login_required_ajax
|
||||
@require_POST
|
||||
@inst_admin_required
|
||||
@inst_admin_can_manage_user
|
||||
def user_set_quota(request, email):
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
quota_mb = int(request.POST.get('space_quota', 0))
|
||||
quota = quota_mb * get_file_size_unit('MB')
|
||||
|
||||
available_quota = get_institution_available_quota(request.user.institution)
|
||||
if available_quota < quota:
|
||||
result = {}
|
||||
result['error'] = _(u'Failed to set quota: maximum quota is %d MB' % \
|
||||
(available_quota / 10 ** 6))
|
||||
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
|
||||
|
||||
seafile_api.set_user_quota(email, quota)
|
||||
|
||||
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
|
||||
|
||||
@login_required_ajax
|
||||
@require_POST
|
||||
@inst_admin_required
|
||||
@@ -234,3 +260,4 @@ def user_toggle_status(request, email):
|
||||
except User.DoesNotExist:
|
||||
return HttpResponse(json.dumps({'success': False}), status=500,
|
||||
content_type=content_type)
|
||||
|
||||
|
@@ -49,7 +49,7 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
{% block extra_script %}{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
|
||||
addConfirmTo($('.js-toggle-admin'), {
|
||||
|
@@ -19,11 +19,68 @@
|
||||
<dl>
|
||||
<dt>{% trans "Number of members" %}</dt>
|
||||
<dd>{{ users_count }}</dd>
|
||||
|
||||
<dt>{% trans "Space Used" %}</dt>
|
||||
<dd>{{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %} <a href="#" class="sf-btn-link" style="margin-left:20px;" id="set-quota">{% trans "Set Quota" %}</a></dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<form id="set-quota-form" method="post" class="hide">{% csrf_token %}
|
||||
<h3>{% trans "Set storage limit" %}</h3>
|
||||
<input type="text" name="space_quota" /> MB
|
||||
<p class="tip">{% trans "Tip: 0 means default limit" %}</p>
|
||||
<p class="error hide"></p>
|
||||
<input type="submit" value="{% trans "Submit" %}" class="submit" />
|
||||
</form>
|
||||
|
||||
<form action="{% url 'sys_inst_search_user' inst.pk %}" method="get" class="side-search-form">
|
||||
<input type="text" name="q" class="input" value="{{q}}" placeholder="{% trans "Search users..." %}" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$('#set-quota').click(function() {
|
||||
$("#set-quota-form").modal({appendTo: "#main"});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#set-quota-form .submit').click(function() {
|
||||
var form = $('#set-quota-form'),
|
||||
form_id = form.attr('id'),
|
||||
space_quota = $('input[name="space_quota"]', form).val();
|
||||
|
||||
if (!$.trim(space_quota)) {
|
||||
apply_form_error(form_id, "{% trans "Space Quota can't be empty" %}");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = { 'space_quota': space_quota };
|
||||
|
||||
var sb_btn = $(this);
|
||||
disable(sb_btn);
|
||||
$.ajax({
|
||||
url: '{% url 'sys_inst_set_quota' inst.pk %}',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: data,
|
||||
success: function(data) {
|
||||
location.reload(true);
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
if (xhr.responseText) {
|
||||
apply_form_error(form_id, $.parseJSON(xhr.responseText).error);
|
||||
} else {
|
||||
apply_form_error(form_id, "{% trans "Failed. Please check the network." %}");
|
||||
}
|
||||
enable(sb_btn);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -54,7 +54,7 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
{% block extra_script %}{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
|
||||
addConfirmTo($('.js-toggle-admin'), {
|
||||
|
@@ -307,6 +307,7 @@ urlpatterns = patterns(
|
||||
url(r'^sys/instadmin/(?P<inst_id>\d+)/users/search/$', sys_inst_search_user, name='sys_inst_search_user'),
|
||||
url(r'^sys/instadmin/(?P<inst_id>\d+)/admins/$', sys_inst_info_admins, name='sys_inst_info_admins'),
|
||||
url(r'^sys/instadmin/(?P<inst_id>\d+)/toggleadmin/(?P<email>[^/]+)/$', sys_inst_toggle_admin, name='sys_inst_toggle_admin'),
|
||||
url(r'^sys/instadmin/(?P<inst_id>\d+)/set_quota/$', sys_inst_set_quota, name='sys_inst_set_quota'),
|
||||
url(r'^sys/publinkadmin/$', sys_publink_admin, name='sys_publink_admin'),
|
||||
url(r'^sys/publink/remove/$', sys_publink_remove, name='sys_publink_remove'),
|
||||
url(r'^sys/uploadlink/remove/$', sys_upload_link_remove, name='sys_upload_link_remove'),
|
||||
|
@@ -35,7 +35,9 @@ from seahub.base.templatetags.seahub_tags import tsstr_sec, email2nickname
|
||||
from seahub.auth import authenticate
|
||||
from seahub.auth.decorators import login_required, login_required_ajax
|
||||
from seahub.constants import GUEST_USER, DEFAULT_USER
|
||||
from seahub.institutions.models import Institution, InstitutionAdmin
|
||||
from seahub.institutions.models import (Institution, InstitutionAdmin,
|
||||
InstitutionQuota)
|
||||
from seahub.institutions.utils import get_institution_space_usage
|
||||
from seahub.invitations.models import Invitation
|
||||
from seahub.role_permissions.utils import get_available_roles
|
||||
from seahub.utils import IS_EMAIL_CONFIGURED, string2list, is_valid_username, \
|
||||
@@ -2127,6 +2129,8 @@ def sys_inst_info_user(request, inst_id):
|
||||
u.last_login = last_login.last_login
|
||||
|
||||
users_count = Profile.objects.filter(institution=inst.name).count()
|
||||
space_quota = InstitutionQuota.objects.get_or_none(institution=inst)
|
||||
space_usage = get_institution_space_usage(inst)
|
||||
|
||||
return render_to_response('sysadmin/sys_inst_info_user.html', {
|
||||
'inst': inst,
|
||||
@@ -2137,6 +2141,8 @@ def sys_inst_info_user(request, inst_id):
|
||||
'next_page': current_page + 1,
|
||||
'per_page': per_page,
|
||||
'page_next': page_next,
|
||||
'space_usage': space_usage,
|
||||
'space_quota': space_quota,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
@@ -2250,6 +2256,31 @@ def sys_inst_toggle_admin(request, inst_id, email):
|
||||
messages.success(request, _('Success'))
|
||||
return HttpResponseRedirect(next)
|
||||
|
||||
@login_required
|
||||
@sys_staff_required
|
||||
@require_POST
|
||||
def sys_inst_set_quota(request, inst_id):
|
||||
"""Set institution quota"""
|
||||
try:
|
||||
inst = Institution.objects.get(pk=inst_id)
|
||||
except Institution.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
next = request.META.get('HTTP_REFERER', None)
|
||||
if not next:
|
||||
next = reverse('sys_inst_info_users', args=[inst.pk])
|
||||
|
||||
quota_mb = int(request.POST.get('space_quota', ''))
|
||||
quota = quota_mb * get_file_size_unit('MB')
|
||||
|
||||
obj, created = InstitutionQuota.objects.update_or_create(
|
||||
institution=inst,
|
||||
defaults={'quota': quota},
|
||||
)
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
return HttpResponse(json.dumps({'success': True}), status=200,
|
||||
content_type=content_type)
|
||||
|
||||
@login_required
|
||||
@sys_staff_required
|
||||
def sys_invitation_admin(request):
|
||||
|
Reference in New Issue
Block a user