1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-25 14:50:29 +00:00

[institutions] Inst add user

This commit is contained in:
zming
2017-07-28 13:24:41 +08:00
committed by llj
parent d597623b14
commit 53b795faf8
10 changed files with 375 additions and 12 deletions

View File

@@ -20,6 +20,7 @@ from seahub.api2.utils import api_error, to_python_boolean
from seahub.base.accounts import User
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.profile.models import Profile, DetailedProfile
from seahub.institutions.models import Institution
from seahub.utils import is_valid_username, is_org_context
from seahub.utils.file_size import get_file_size_unit
@@ -36,6 +37,7 @@ def get_account_info(user):
info['email'] = email
info['name'] = email2nickname(email)
info['department'] = d_profile.department if d_profile else ''
info['institution_name'] = profile.institution if profile else ''
info['id'] = user.id
info['is_staff'] = user.is_staff
info['is_active'] = user.is_active
@@ -143,6 +145,15 @@ class Account(APIView):
else:
seafile_api.set_user_quota(email, space_quota)
# update user institution
institution_name = request.data.get("institution_name", None)
if institution_name is not None:
inst_profile = Profile.objects.get_profile_by_user(email)
if inst_profile is None:
inst_profile = Profile(user=email)
inst_profile.institution = institution_name
inst_profile.save()
# update is_trial
is_trial = request.data.get("is_trial", None)
if is_trial is not None:
@@ -194,6 +205,15 @@ class Account(APIView):
return api_error(status.HTTP_400_BAD_REQUEST,
_(u'Department is too long (maximum is 512 characters)'))
# argument check for institution
institution_name = request.data.get("institution_name", None)
if institution_name is not None and institution_name != '':
try:
obj_insti = Institution.objects.get(name=institution_name)
except Institution.DoesNotExist:
return api_error(status.HTTP_400_BAD_REQUEST,
"Institution %s does not exists" % institution_name)
# argument check for storage
space_quota_mb = request.data.get("storage", None)
if space_quota_mb is not None:

View File

@@ -16,6 +16,8 @@ from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from seahub.base.accounts import User
from seahub.profile.models import Profile
from seahub.institutions.models import Institution
from seahub.utils.file_size import get_file_size_unit
from seahub.admin_log.models import USER_DELETE
from seahub.admin_log.signals import admin_operation
@@ -42,7 +44,7 @@ class AdminUsersBatch(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
operation = request.POST.get('operation', None)
if operation not in ('set-quota', 'delete-user'):
if operation not in ('set-quota', 'delete-user', 'set-institution'):
error_msg = "operation can only be 'set-quota' or 'delete-user'."
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
@@ -121,4 +123,29 @@ class AdminUsersBatch(APIView):
admin_operation.send(sender=None, admin_name=request.user.username,
operation=USER_DELETE, detail=admin_op_detail)
if operation == 'set-institution':
institution_name = request.POST.get('institution_name', None)
if institution_name is None:
error_msg = 'institution name can not be blank.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
emails = [email.strip() for email in emails if email.strip()]
if institution_name != '':
try:
obj_insti = Institution.objects.get(name=institution_name)
except Institution.DoesNotExist:
error_msg = 'institution %s does not exists' % institution_name
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
for email in emails:
try:
User.objects.get(email=email)
except User.DoesNotExist:
continue
for email in emails:
profile = Profile.objects.get_profile_by_user(email)
if profile is None:
profile = Profile(user=email)
profile.institution = institution_name
profile.save()
return Response(result)

View File

@@ -8,7 +8,9 @@ from django.dispatch import receiver
from seahub.base.fields import LowerCaseCharField
from seahub.profile.settings import EMAIL_ID_CACHE_PREFIX, EMAIL_ID_CACHE_TIMEOUT
from seahub.institutions.models import Institution
from registration.signals import user_registered
from seahub.signals import institution_deleted
# Get an instance of a logger
logger = logging.getLogger(__name__)
@@ -162,3 +164,9 @@ def update_profile_cache(sender, instance, **kwargs):
Set profile data to cache when profile data change.
"""
refresh_cache(instance.user)
@receiver(institution_deleted)
def remove_user_for_inst_deleted(sender, **kwargs):
inst_name = kwargs.get("inst_name", "")
Profile.objects.filter(institution=inst_name).delete()

View File

@@ -6,3 +6,4 @@ repo_created = django.dispatch.Signal(providing_args=["org_id", "creator", "repo
repo_deleted = django.dispatch.Signal(providing_args=["org_id", "usernames", "repo_owner", "repo_id", "repo_name"])
upload_file_successful = django.dispatch.Signal(providing_args=["repo_id", "file_path", "owner"])
comment_file_successful = django.dispatch.Signal(providing_args=["repo", "file_path", "comment", "author", "notify_users"])
institution_deleted = django.dispatch.Signal(providing_args=["inst_name"])

View File

@@ -1,5 +1,6 @@
{% extends "sysadmin/sys_inst_info_base.html" %}
{% load i18n seahub_tags %}
{% load staticfiles %}
{% block right_panel %}
<div class="tabnav">
@@ -7,8 +8,18 @@
<li class="tabnav-tab tabnav-tab-cur"><a href="{% url 'sys_inst_info_users' inst.pk %}">{% trans "Members" %}</a></li>
<li class="tabnav-tab"><a href="{% url 'sys_inst_info_admins' inst.pk %}">{% trans "Admins" %}</a></li>
</ul>
<div class="js-op-for-all fright">
<button id="add-user-btn">{% trans "Add user" %}</button>
</div>
</div>
<form id="add-user-form" action="" method="post" class="hide">{% csrf_token %}
<h3>{% trans "Add user" %}</h3>
<label for="id_email">{% trans "Email" %}</label><br />
<input id="id_email" style="width:300px;height:30px;"/>
<button type="submit" class="submit">{% trans "Submit" %}</button>
<p id="id_error" class="error hide" style="margin-top:30px;"></p>
</form>
{% if users %}
<table>
<tr>
@@ -54,9 +65,13 @@
{% endblock %}
{% block extra_style %}
<link rel="stylesheet" type="text/css" href="{% static "css/select2-3.5.2.css" %}"/>
{% endblock %}
{% block extra_script %}{{ block.super }}
<script type="text/javascript">
<script type="text/javascript" src= "{% static "scripts/lib/select2-3.5.2.js" %}"></script>
<script type="text/javascript">
addConfirmTo($('.js-toggle-admin'), {
'title': "Toggle Admin",
'con': "Sure ?",
@@ -75,6 +90,89 @@ $('.user-status-edit-icon').click(function() {
$(this).parent().addClass('hide');
$(this).parent().next().removeClass('hide'); // show 'select'
});
$('#add-user-btn').click(function(){
$('#add-user-form').modal();
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
$("#id_email").select2({
minimumInputLength: 1,
maximumSelectionSize:5,
multiple: true,
tags: [],
ajax: {
url: "{% url 'search-user' %}",
dataType: 'json',
delay: 250,
data: function (term, page) {
return {
q: term, // search term
};
},
results: function(data, page){
var group_list = [], groups = data.users;
for (var i =0, len = groups.length; i < len; i++){
group_list.push({
"id": i,
"email": groups[i].email,
"avatar_url": groups[i].avatar_url,
"name": groups[i].name
});
}
return {results:group_list};
}
},
formatResult: function(item) {
var markup = '<li>'+
'<div ><img src="' + item.avatar_url + '" width="32" height="32" class="avatar" />'+
'<span class="text ellipsis">' + item.name + '<br>' + item.email + '</span></div></li>'
return markup;
},
formatSelection:function(item) {
return (item.email);
},
escapeMarkup: function (m) { return m; }
});
});
$('#add-user-form').submit(function(){
var form = $(this),
data = ""
form_id = $(this).attr('id'),
email = $("#id_email").select2("data"),
submit_btn = $(this).find('[type="submit"]');
if (!email){
apply_form_error(form_id, "{% trans "Email can not be blank "%}");
return false;
} else {
for (var i=0; i < email.length; i++){
data += email[i].email;
data += ',';
}
}
disable(submit_btn);
$.ajax({
url: "{% url 'sys_inst_add_user' inst.id %}",
type: 'POST',
dataType: 'json',
data: {
'email': data
},
beforeSend: prepareCSRFToken,
success: function(data){
if(data['success']){
location.reload(true);
}
},
error: function(xhr, textStatus, errorThrown){
if (xhr.responseText) {
var error = $.parseJSON(xhr.responseText).error;
apply_form_error(form_id, error);
enable(submit_btn);
}
}
});
return false;
});
$('.user-status-select').change(function() {
var select = $(this),
select_val = select.val(),

View File

@@ -1,5 +1,6 @@
{% extends "sysadmin/base.html" %}
{% load seahub_tags i18n %}
{% load staticfiles %}
{% block cur_users %}tab-cur{% endblock %}
{% block left_panel %}{{block.super}}
@@ -28,6 +29,9 @@
<button id="export-excel">{% trans "Export Excel" %}</button>
</div>
<div class="js-op-for-selected fright hide">
{% if multi_institution %}
<button id="set-institution-btn">{% trans "Set institution" %}</button>
{% endif %}
<button id="set-quota-btn">{% trans "Set quota" %}</button>
<button id="delete-users-btn">{% trans "Delete users" %}</button>
</div>
@@ -85,8 +89,11 @@
<p>{% trans "Activating..., please wait" %}</p>
</div>
{% endblock %}
{% block extra_style %}
<link rel="stylesheet" type="text/css" href="{% static "css/select2-3.5.2.css" %}"/>
{% endblock %}
{% block extra_script %}
<script type="text/javascript" src= "{% static "scripts/lib/select2-3.5.2.js" %}"></script>
<script type="text/javascript">
$(function(){
// check if server version is the latest one

View File

@@ -28,20 +28,25 @@ $('tr:gt(0)').hover(
$(this).find('.user-status-edit-icon, .user-role-edit-icon').addClass('vh');
}
);
$('.user-status-edit-icon, .user-role-edit-icon').click(function() {
$('.user-status-edit-icon, .user-role-edit-icon, .user-institution-edit-icon').click(function() {
$(this).parent().addClass('hide');
$(this).parent().next().removeClass('hide'); // show 'select'
});
$('.user-status-select, .user-role-select').change(function() {
$('.user-status-select, .user-role-select, .user-institution-select').change(function() {
var select = $(this),
select_val = select.val(),
ajax_type = 'POST',
uid = select.parents('tr').attr('data-userid'),
$select_prev = $(this).prev('.user-status, .user-role'), // .user-status, .user-role
$select_prev = $(this).prev('.user-status, .user-role, .user-institution'), // .user-status, .user-role, .user-institution
url, data;
if (select.hasClass('user-status-select')) {
url = "{{ SITE_ROOT }}useradmin/toggle_status/" + uid + "/";
data = {'s': select_val};
} else if (select.hasClass('user-institution-select')){
url = "{{ SITE_ROOT }}api2/accounts/" + uid + "/";
data = {'institution_name': select_val};
ajax_type = 'PUT';
} else {
url = "{{ SITE_ROOT }}useradmin/toggle_role/" + uid + "/";
data = {'r': select_val};
@@ -54,7 +59,7 @@ $('.user-status-select, .user-role-select').change(function() {
}
$.ajax({
url: url,
type: 'POST',
type: ajax_type,
dataType: 'json',
data: data,
cache: false,
@@ -67,7 +72,12 @@ $('.user-status-select, .user-role-select').change(function() {
} else {
feedback("{% trans "Edit succeeded" %}", 'success');
}
$('.user-status-cur-value, .user-role-cur-value', $select_prev).html(select.children('option[value="' +select.val() + '"]').text());
if (select.children('option[value="' +select.val() + '"]').text() == "None"){
$('.user-status-cur-value, .user-role-cur-value, .user-institution-cur-value', $select_prev).html("");
}
else{
$('.user-status-cur-value, .user-role-cur-value, .user-institution-cur-value', $select_prev).html(select.children('option[value="' +select.val() + '"]').text());
}
select.addClass('hide');
$select_prev.removeClass('hide');
$.modal.close();
@@ -118,6 +128,95 @@ $('#set-quota-btn').click(function() {
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
});
$('#set-institution-btn').click(function(){
$('#set-institution-form').data({'batch': true}).modal();
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
$("#institution_name").select2({
minimumInputLength: 1,
maximumSelectionSize:1,
multiple: true,
tags: [],
ajax: {
url: "{% url 'sys_inst_search_inst' %}",
dataType: 'json',
delay: 250,
data: function (term, page) {
return {
q: term, // search term
};
},
results: function(data, page){
var insts_list = [], insts = data.insts;
for (var i =0, len = insts.length; i < len; i++){
insts_list.push({
"id": i,
"name": insts[i].name
});
}
return {results:insts_list};
}
},
formatResult: function(item) {
var markup = '<li><div ><span class="text ellipsis">' + item.name + '</span></div></li>'
return markup;
},
formatSelection:function(item) {
return (item.name);
},
escapeMarkup: function (m) { return m; }
});
});
$("#set-institution-form").submit(function(){
var insts = $("#institution_name").select2("data"),
data = "",
emails = [],
form_id = $(this).attr('id'),
submit_btn = $(this).find('[type="submit"]');
if (!insts){
apply_form_error(form_id, "Institution can not be blank" );
return false;
} else {
data = insts[0].name;
}
$('td [type="checkbox"]:checked').closest('tr').each(function(index, item) {
emails.push($(item).attr('data-userid'));
});
if (data == "None"){
data = "";
}
disable(submit_btn);
var batch = $(this).data('batch');
if (batch){
$.ajax({
url: "{% url 'api-v2.1-admin-users-batch' %}",
type: "POST",
dataType: 'json',
cache: false,
data: {
'operation': 'set-institution',
'email': emails,
'institution_name': data
},
traditional: true,
beforeSend: prepareCSRFToken,
success: function(data){
if(data['success']){
location.reload(true);
}
},
error: function(xhr, textStatus, errorThrown){
if (xhr.responseText) {
var error = $.parseJSON(xhr.responseText).error;
apply_form_error(form_id, error);
enable(submit_btn);
}
}
});
return false;
}
});
$('#delete-users-btn').click(function() {
var title = "{% trans "Delete User" %}";
var content = "{% trans "Are you sure you want to delete the selected user(s) ?" %}";
@@ -294,4 +393,8 @@ $(document).click(function(e) {
$('.user-role').removeClass('hide');
$('.user-role-select').addClass('hide');
}
if (!$('.user-institution-edit-icon, .user-institution-select').is(target)) {
$('.user-institution').removeClass('hide');
$('.user-institution-select').addClass('hide');
}
});

View File

@@ -10,9 +10,16 @@
<th width="33%">{% trans "Email" %} / {% trans "Name" %} / {% trans "Contact Email" %}</th>
<th width="12%">{% trans "Status" %}</th>
{% endif %}
<th width="16%">{% trans "Space Used / Quota" %}</th>
<th width="22%">{% trans "Create At / Last Login" %}</th>
<th width="14%"></th>
{% if multi_institution %}
<th width="10%">{% trans "Institution" %}</th>
<th width="16%">{% trans "Space Used / Quota" %}</th>
<th width="15%">{% trans "Create At / Last Login" %}</th>
<th width="11%"></th>
{% else %}
<th width="16%">{% trans "Space Used / Quota" %}</th>
<th width="22%">{% trans "Create At / Last Login" %}</th>
<th width="14%"></th>
{% endif %}
</tr>
{% for user in users %}
@@ -66,6 +73,20 @@
</select>
</td>
{% endif %}
{% if multi_institution %}
<td>
<div class="user-institution">
<span class="user-institution-cur-value"> {{ user.institution }} </span>
<span title="{% trans "Edit"%}" class="user-institution-edit-icon sf2-icon-edit op-icon vh"></span>
</div>
<select name="institution" class="user-institution-select hide">
{% for inst in institutions %}
<option value={{inst}} {% if user.institution == inst %} selected="selected"{% endif %}>{{inst}}</option>
{% endfor %}
<option value="" {% if user.institution == "" %} selected="selected"{% endif %}>None</option>
</select>
</td>
{% endif %}
<td style="font-size:11px;">
{{ user.space_usage|seahub_filesizeformat }} /
@@ -111,3 +132,22 @@
<p class="error hide"></p>
<input type="submit" value="{% trans "Submit" %}" class="submit" />
</form>
<form id="set-institution-form" method="post" action="" class="hide">{% csrf_token %}
<table>
<thead>
<tr>
<th width="75%">{% trans "Institution" %}</th>
<th width="25%"></th>
</tr>
</thead>
<tbody>
<td>
<input id="institution_name" class="w100"/>
<p class="error hide"></p>
</td>
<td>
<input type="submit" value="{% trans "Submit" %}" class="submit" />
</td>
</tbody>
</table>
</form>

View File

@@ -350,8 +350,10 @@ urlpatterns = patterns(
url(r'^sys/orgadmin/(?P<org_id>\d+)/library/$', sys_org_info_library, name='sys_org_info_library'),
url(r'^sys/orgadmin/(?P<org_id>\d+)/setting/$', sys_org_info_setting, name='sys_org_info_setting'),
url(r'^sys/instadmin/$', sys_inst_admin, name='sys_inst_admin'),
url(r'^sys/instadmin/search-inst/$$', sys_inst_search_inst, name='sys_inst_search_inst'),
url(r'^sys/instadmin/(?P<inst_id>\d+)/remove/$', sys_inst_remove, name='sys_inst_remove'),
url(r'^sys/instadmin/(?P<inst_id>\d+)/users/$', sys_inst_info_user, name='sys_inst_info_users'),
url(r'^sys/instadmin/(?P<inst_id>\d+)/users/add/$', sys_inst_add_user, name='sys_inst_add_user'),
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'),

View File

@@ -58,7 +58,7 @@ from seahub.forms import SetUserQuotaForm, AddUserForm, BatchAddUserForm, \
TermsAndConditionsForm
from seahub.options.models import UserOptions
from seahub.profile.models import Profile, DetailedProfile
from seahub.signals import repo_deleted
from seahub.signals import repo_deleted, institution_deleted
from seahub.share.models import FileShare, UploadLinkShare
from seahub.admin_log.signals import admin_operation
from seahub.admin_log.models import USER_DELETE, USER_ADD
@@ -228,11 +228,15 @@ def sys_user_admin(request):
if trial_user.user_or_org == user.email:
user.trial_info = {'expire_date': trial_user.expire_date}
profile = Profile.objects.get_profile_by_user(user.email)
user.institution = profile.institution if profile else ''
platform = get_platform_name()
server_id = get_server_id()
pro_server = 1 if is_pro_version() else 0
extra_user_roles = [x for x in get_available_roles()
if x not in get_basic_user_roles()]
institutions = [inst.name for inst in Institution.objects.all()]
return render_to_response(
'sysadmin/sys_useradmin.html', {
@@ -251,6 +255,8 @@ def sys_user_admin(request):
'pro_server': pro_server,
'enable_user_plan': enable_user_plan,
'extra_user_roles': extra_user_roles,
'institutions': institutions,
'multi_institution': getattr(dj_settings, 'MULTI_INSTITUTION', False),
}, context_instance=RequestContext(request))
@login_required
@@ -2092,6 +2098,55 @@ def sys_inst_admin(request):
'page_next': page_next,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_inst_search_inst(request):
key = request.GET.get('q', '')
if not key:
return HttpResponse(json.dumps({'error': "q invalid"}),
status=400)
institutions = [dict([('name', inst.name)]) for inst in Institution.objects.filter(name__contains=key)]
institutions.append({'name': 'None'})
return HttpResponse(json.dumps({"insts": institutions}), status=200,
content_type='application/json; charset=utf-8')
@login_required
@sys_staff_required
@require_POST
def sys_inst_add_user(request, inst_id):
content_type = 'application/json; charset=utf-8'
get_email = request.POST.get('email', '')
email_list = [em.strip() for em in get_email.split(',') if em.strip()]
if len(email_list) == 0:
return HttpResponse(json.dumps({'error': "User can't be empty"}),
status=400)
try:
inst = Institution.objects.get(pk=inst_id)
except Institution.DoesNotExist:
return HttpResponse(json.dumps({'error': "Inst does not exists"}),
status=400)
for email in email_list:
try:
User.objects.get(email=email)
except Exception as e:
messages.error(request, u'Failed to add %s to institution: user does not exist.' % email)
continue
profile = Profile.objects.get_profile_by_user(email)
if not profile:
profile = Profile.objects.add_or_update(email, email)
if profile.institution:
messages.error(request, _(u"Failed to add %s to institution: user already have institution") % email)
continue
else:
profile.institution = inst.name
profile.save()
messages.success(request, _(u'Successfully add %s to institution.') % email)
return HttpResponse(json.dumps({'success': True}),
content_type=content_type)
@login_required
@sys_staff_required
@require_POST
@@ -2103,7 +2158,9 @@ def sys_inst_remove(request, inst_id):
except Institution.DoesNotExist:
raise Http404
inst_name = inst.name
inst.delete()
institution_deleted.send(sender=None, inst_name = inst_name)
messages.success(request, _('Success'))
return HttpResponseRedirect(reverse('sys_inst_admin'))