1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-18 00:00:00 +00:00

Merge pull request #1067 from haiwen/multi_institution

[inst] Add institution feature
This commit is contained in:
Daniel Pan
2016-03-19 13:34:01 +08:00
24 changed files with 845 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ RequestContext.
import re
from django.conf import settings as dj_settings
from constance import config
from seahub.settings import SEAFILE_VERSION, SITE_TITLE, SITE_NAME, \
@@ -85,6 +86,7 @@ def base(request):
'sysadmin_extra_enabled': ENABLE_SYSADMIN_EXTRA,
'grps': grps,
'multi_tenancy': MULTI_TENANCY,
'multi_institution': getattr(dj_settings, 'MULTI_INSTITUTION', False),
'search_repo_id': search_repo_id,
'SITE_ROOT': SITE_ROOT,
}

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,27 @@
from django.http import Http404
from seahub.profile.models import Profile
def inst_admin_required(func):
"""
Decorator for views check whether user is a institution admin.
"""
def _decorated(request, *args, **kwargs):
if request.user.inst_admin is True:
return func(request, *args, **kwargs)
raise Http404
return _decorated
def inst_admin_can_manage_user(func):
"""
Decorator for views check whether inst admin has permission to manage that
user.
"""
def _decorated(request, *args, **kwargs):
if request.user.inst_admin is True:
email = kwargs['email']
p = Profile.objects.get_profile_by_user(email)
if p and p.institution == request.user.institution.name:
return func(request, *args, **kwargs)
raise Http404
return _decorated

View File

@@ -0,0 +1,22 @@
from django.conf import settings
from seahub.institutions.models import InstitutionAdmin
class InstitutionMiddleware(object):
def process_request(self, request):
if not getattr(settings, 'MULTI_INSTITUTION', False):
return None
username = request.user.username
# todo: record to session to avoid database query
try:
inst_admin = InstitutionAdmin.objects.get(user=username)
except InstitutionAdmin.DoesNotExist:
return None
request.user.institution = inst_admin.institution
request.user.inst_admin = True
return None

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Institution',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=200)),
('create_time', models.DateTimeField(default=django.utils.timezone.now)),
],
),
migrations.CreateModel(
name='InstitutionAdmin',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('user', models.EmailField(max_length=254)),
('institution', models.ForeignKey(to='institutions.Institution')),
],
),
]

View File

@@ -0,0 +1,12 @@
from django.db import models
from django.utils import timezone
class Institution(models.Model):
name = models.CharField(max_length=200)
create_time = models.DateTimeField(default=timezone.now)
class InstitutionAdmin(models.Model):
institution = models.ForeignKey(Institution)
user = models.EmailField()

View File

@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% load i18n %}
{% block inst_admin %}<a href="{{ SITE_ROOT }}" title="{% trans "Exit" %}"><img src="{{ MEDIA_URL }}img/admin_out.png" alt="" /></a>{% endblock %}
{% block left_panel %}
<div class="side-tabnav">
<h3 class="hd">{{ request.user.institution.name }}</h3>
<ul class="side-tabnav-tabs">
<li class="tab {% block cur_users %}{% endblock %}">
<a href="{% url "institutions:useradmin" %}"><span class="sf2-icon-user"></span>{% trans "Users" %}</a>
</li>
</ul>
</div>
{% endblock %}

View File

@@ -0,0 +1,27 @@
{% extends "institutions/base.html" %}
{% load seahub_tags i18n %}
{% block cur_info %}tab-cur{% endblock %}
{% block right_panel %}
<h3 class="hd">{% trans "Info" %}</h3>
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ inst.name }}</dd>
<dt>{% trans "Libraries" %}</dt>
<dd>{{repos_count}}</dd>
<dt>{% trans "Active Users" %} / {% trans "Total Users" %}</dt>
<dd>
{% if active_users_count %}{{ active_users_count }}{% else %}--{% endif %}
/
{% if users_count %}{{ users_count }}{% else %}--{% endif %}
</dd>
<dt>{% trans "Groups" %}</dt>
<dd>{{groups_count}}</dd>
</dl>
{% endblock %}

View File

@@ -0,0 +1,130 @@
{% extends "institutions/base.html" %}
{% load i18n avatar_tags seahub_tags %}
{% load staticfiles %}
{% block extra_style %}
<link rel="stylesheet" type="text/css" href="{% static "css/select2-3.5.2.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "css/select2.custom.css" %}" />
<style type="text/css">
#left-panel { position:relative; }
</style>
{% endblock %}
{% block left_panel %}
<a class="go-back" title="{% trans "Back to user list" %}" href="{% url 'institutions:useradmin' %}"><span class="icon-chevron-left"></span></a>
<div class="side-info">
<h3 class="hd">{% trans "Profile" %}</h3>
{% avatar email 48 %}
<dl>
<dt>{% trans "Email" %}</dt>
<dd>{{ email }}</dd>
{% if profile %}
<dt>{% trans "Name" context "true name" %}</dt>
<dd>{{ profile.nickname }}</dd>
{% endif %}
{% if d_profile %}
<dt>{% trans "Department" %}</dt>
<dd>{{ d_profile.department }}</dd>
<dt>{% trans "Telephone" %}</dt>
<dd>{{ d_profile.telephone }}</dd>
{% endif %}
</dl>
<h3 class="hd">{% trans "Space Used" %}</h3>
<p>{% trans "Used" %}: {{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %}</p>
</div>
{% endblock %}
{% block right_panel %}
<div id="tabs" class="tab-tabs">
<div class="hd ovhd">
<ul class="tab-tabs-nav fleft">
<li class="tab"><a href="#owned" class="a">{% trans "Owned Libs" %}</a></li>
<li class="tab"><a href="#user-admin-groups" class="a">{% trans "Groups" %}</a></li>
</ul>
</div>
<div id="owned">
{% if owned_repos %}
<table class="repo-list">
<tr>
<th width="4%"><!--icon--></th>
<th width="35%">{% trans "Name" %}</th>
<th width="16%">{% trans "Size"%}</th>
<th width="25%">{% trans "Last Update"%}</th>
<th width="20%">{% trans "Operations" %}</th>
</tr>
{% for repo in owned_repos %}
<tr>
{% if repo.encrypted %}
<td><img src="{{MEDIA_URL}}img/sync-folder-encrypt-20.png" title="{% trans "Encrypted"%}" alt="{% trans "library icon" %}" /></td>
{% else %}
<td><img src="{{MEDIA_URL}}img/sync-folder-20.png?t=1387267140" title="{% trans "Read-Write" %}" alt="{% trans "library icon" %}" /></td>
{% endif %}
{% if not repo.name %}
<td>Broken ({{repo.id}})</td>
{% else %}
{% if repo.encrypted %}
<td>{{ repo.name }}</td>
{% elif enable_sys_admin_view_repo %}
<td><a href="{% url 'sys_admin_repo' repo.id %}">{{ repo.name }}</a></td>
{% else %}
<td>{{ repo.name }}</td>
{% endif %}
{% endif %}
<td>{{ repo.size|filesizeformat }}</td>
<td>{{ repo.last_modify|translate_seahub_time }}</td>
<td data-id="{{ repo.props.id }}" data-name="{{repo.name}}">
<div>
</div>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="empty-tips">
<h2 class="alc">{% trans "This user has not created any libraries" %}</h2>
</div>
{% endif %}
</div>
<div id="user-admin-groups">
{% if personal_groups %}
<table>
<tr>
<th width="30%">{% trans "Name" %}</th>
<th width="30%">{% trans "Role" %}</th>
<th width="25%">{% trans "Create At" %}</th>
<th width="15%">{% trans "Operations" %}</th>
</tr>
{% for group in personal_groups %}
<tr>
<td><a href="{% url 'sys_admin_group_info' group.id %}">{{ group.group_name }}</a></td>
<td>{{ group.role }}</td>
<td>{{ group.timestamp|tsstr_sec }}</td>
<td></td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="empty-tips">
<h2 class="alc">{% trans "This user has not created or joined any groups" %}</h2>
</div>
{% endif %}
</div>
</div>
{% include "sysadmin/repo_transfer_form.html" %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
</script>
{% endblock %}

View File

@@ -0,0 +1,66 @@
{% extends "institutions/base.html" %}
{% load seahub_tags i18n %}
{% block cur_users %}tab-cur{% endblock %}
{% block right_panel %}
<div class="tabnav ovhd">
<ul class="tabnav-tabs fleft">
<li class="tabnav-tab tabnav-tab-cur"><a href="{% url 'institutions:useradmin' %}">{% trans "Users" %}</a></li>
</ul>
</div>
<table>
<tr>
<th width="36%">{% trans "Email" %}</th>
<th width="12%">{% trans "Status" %}</th>
<th width="16%">{% trans "Space Used" %}</th>
<th width="22%">{% trans "Create At / Last Login" %}</th>
<th width="14%">{% trans "Operations" %}</th>
</tr>
{% for user in users %}
<tr data-userid="{{user.email}}">
<td><a href="{% url 'institutions:user_info' user.email %}">{{ user.email }}</a>
</td>
<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 %}
</div>
</td>
<td style="font-size:11px;">
<p> {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %} </p>
</td>
<td>
{% if user.source == "DB" %}
{{ user.ctime|tsstr_sec }} /<br />
{% else %}
-- /
{% endif %}
{% 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 'institutions:user_remove' user.email %}" data-target="{{ user.email }}">{% trans "Delete" %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.remove-user-btn'), {
'title':"{% trans "Delete User" %}",
'con':"{% trans "Are you sure you want to delete %s ?" %}",
'post': true // post request
});
</script>
{% endblock %}

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,11 @@
from django.conf.urls import patterns, url
from .views import info, useradmin, user_info, user_remove
urlpatterns = patterns(
'',
url('^info/$', info, name="info"),
url('^useradmin/$', useradmin, name="useradmin"),
url(r'^useradmin/info/(?P<email>[^/]+)/$', user_info, name='user_info'),
url(r'^useradmin/remove/(?P<email>[^/]+)/$', user_remove, name='user_remove'),
)

View File

@@ -0,0 +1,133 @@
import logging
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.translation import ugettext as _
import seaserv
from seaserv import seafile_api
from pysearpc import SearpcError
from seahub.base.accounts import User
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.profile.models import Profile, DetailedProfile
from seahub.utils.rpc import mute_seafile_api
logger = logging.getLogger(__name__)
def _populate_user_quota_usage(user):
"""Populate space/share quota to user.
Arguments:
- `user`:
"""
try:
user.space_usage = seafile_api.get_user_self_usage(user.email)
user.space_quota = seafile_api.get_user_quota(user.email)
except SearpcError as e:
logger.error(e)
user.space_usage = -1
user.space_quota = -1
@inst_admin_required
def info(request):
"""List instituion info.
"""
inst = request.user.institution
return render_to_response('institutions/info.html', {
'inst': inst,
}, context_instance=RequestContext(request))
@inst_admin_required
def useradmin(request):
"""List users in the institution.
"""
inst = request.user.institution
usernames = [x.user for x in Profile.objects.filter(institution=inst.name)]
users = [User.objects.get(x) for x in usernames]
last_logins = UserLastLogin.objects.filter(username__in=[x.username for x in users])
for u in users:
if u.username == request.user.username:
u.is_self = True
_populate_user_quota_usage(u)
for e in last_logins:
if e.username == u.username:
u.last_login = e.last_login
return render_to_response('institutions/useradmin.html', {
'inst': inst,
'users': users,
}, context_instance=RequestContext(request))
@inst_admin_required
@inst_admin_can_manage_user
def user_info(request, email):
"""Show user info, libraries and groups.
"""
owned_repos = mute_seafile_api.get_owned_repo_list(email,
ret_corrupted=True)
in_repos = mute_seafile_api.get_share_in_repo_list(email, -1, -1)
space_usage = mute_seafile_api.get_user_self_usage(email)
space_quota = mute_seafile_api.get_user_quota(email)
# get user profile
profile = Profile.objects.get_profile_by_user(email)
d_profile = DetailedProfile.objects.get_detailed_profile_by_user(email)
try:
personal_groups = seaserv.get_personal_groups_by_user(email)
except SearpcError as e:
logger.error(e)
personal_groups = []
for g in personal_groups:
try:
is_group_staff = seaserv.check_group_staff(g.id, email)
except SearpcError as e:
logger.error(e)
is_group_staff = False
if email == g.creator_name:
g.role = _('Owner')
elif is_group_staff:
g.role = _('Admin')
else:
g.role = _('Member')
return render_to_response(
'institutions/user_info.html', {
'owned_repos': owned_repos,
'space_quota': space_quota,
'space_usage': space_usage,
'in_repos': in_repos,
'email': email,
'profile': profile,
'd_profile': d_profile,
'personal_groups': personal_groups,
}, context_instance=RequestContext(request))
@require_POST
@inst_admin_required
@inst_admin_can_manage_user
def user_remove(request, email):
"""Remove a institution user.
"""
referer = request.META.get('HTTP_REFERER', None)
next = reverse('institutions:useradmin') if referer is None else referer
try:
user = User.objects.get(email=email)
user.delete()
messages.success(request, _(u'Successfully deleted %s') % user.username)
except User.DoesNotExist:
messages.error(request, _(u'Failed to delete: the user does not exist'))
return HttpResponseRedirect(next)

View File

@@ -199,6 +199,7 @@ INSTALLED_APPS = (
'seahub.avatar',
'seahub.base',
'seahub.contacts',
'seahub.institutions',
'seahub.wiki',
'seahub.group',
'seahub.message',

View File

@@ -91,6 +91,11 @@
{% block org_admin %}<a href="{% url 'org_user_admin'%}" title="{% trans "Admin" %}" class="a"><img src="{{ MEDIA_URL }}img/admin_in.png" alt="" /></a>{% endblock %}
</div>
{% endif %}
{% if request.user.inst_admin %}
<div class="manage">
{% block inst_admin %}<a href="{% url "institutions:useradmin" %}" title="{% trans "Admin" %}" class="a"><img src="{{ MEDIA_URL }}img/admin_in.png" alt="" /></a>{% endblock %}
</div>
{% endif %}
</div>
</div>
{% else %} {# for non-logged-in user #}

View File

@@ -102,6 +102,12 @@
{% block org_admin %}<a href="{% url 'org_user_admin'%}" title="{% trans "Admin" %}" class="a"><img src="{{ MEDIA_URL }}img/admin_in.png" alt="" /></a>{% endblock %}
</div>
{% endif %}
{% if request.user.inst_admin %}
<div class="manage">
{% block inst_admin %}<a href="{% url "institutions:useradmin" %}" title="{% trans "Admin" %}" class="a"><img src="{{ MEDIA_URL }}img/admin_in.png" alt="" /></a>{% endblock %}
</div>
{% endif %}
</div>
</div>
{% endif %}

View File

@@ -25,6 +25,11 @@
<a href="{{ SITE_ROOT }}sys/orgadmin/"><span class="sf2-icon-organization"></span>{% trans "Organizations" %}</a>
</li>
{% endif %}
{% if multi_institution %}
<li class="tab {% block cur_inst %}{% endblock %}">
<a href="{{ SITE_ROOT }}sys/instadmin/"><span class="sf2-icon-organization"></span>{% trans "Institutions" %}</a>
</li>
{% endif %}
<li class="tab {% block cur_notice %}{% endblock %}">
<a href="{{ SITE_ROOT }}sys/notificationadmin/"><span class="sf2-icon-msgs"></span>{% trans "Notifications" %}</a>
</li>

View File

@@ -0,0 +1,71 @@
{% extends "sysadmin/base.html" %}
{% load i18n seahub_tags %}
{% block cur_inst %}tab-cur{% endblock %}
{% block right_panel %}
<div class="tabnav ovhd">
<ul class="tabnav-tabs fleft">
<h3>{% trans "All Institutions" %}</h3>
</ul>
<div class="fright">
<button id="add-inst-btn">{% trans "Add" %}</button>
</div>
</div>
<form id="add-inst-form" action="" method="post" class="hide">{% csrf_token %}
<h3>{% trans "Add institution" %}</h3>
<label for="id_name">{% trans "Name" %}</label><br />
<input type="text" name="name" id="id_name" class="input" /><br />
<p class="error hide"></p>
<button type="submit" class="submit">{% trans "Submit" %}</button>
</form>
{% if insts %}
<table>
<tr>
<th width="62%">{% trans "Name" %}</th>
<th width="20%">{% trans "Create At" %}</th>
<th width="18%">{% trans "Operations" %}</th>
</tr>
{% for inst in insts %}
<tr>
<td><a href="{% url "sys_inst_info_user" inst.pk %}">{{ inst.name }}</a></td>
<td>{{ inst.create_time|translate_seahub_time }} </td>
<td>
<a class="op vh rm-link" data-target="{{ inst.name }}" data-url="{% url "sys_inst_remove" inst.pk %}" href="#">{% trans "Remove" %}</a>
</td>
</tr>
{% endfor %}
</table>
{% include "snippets/admin_paginator.html" %}
{% else %}
<p>{% trans "Empty" %}</p>
{% endif %}
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
$('#add-inst-btn').click(function() {
$('#add-inst-form').modal();
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
});
$('#add-inst-form').submit(function() {
var form = $(this),
form_id = $(this).attr('id'),
name = $.trim(form.find('[name="name"]').val());
if (!name) {
apply_form_error(form_id, "{% trans "Name cannot be blank" %}");
return false;
}
});
addConfirmTo($('.rm-link'), {
'title':"{% trans "Delete Institution" %}",
'con':"{% trans "Are you sure you want to delete %s ?" %}",
'post': true // post request
});
</script>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends "admin_base.html" %}
{% load i18n seahub_tags %}
{% block nav_orgadmin_class %}class="cur"{% endblock %}
{% block extra_style %}
<style type="text/css">
#left-panel { position:relative; }
#set-quota-form {
min-width:255px;
}
</style>
{% endblock %}
{% block left_panel %}
<a class="go-back" title="{% trans "Back" %}" href="{% url 'sys_inst_admin' %}"><span class="icon-chevron-left"></span></a>
<div class="side-info">
<h3 class="hd">{{ inst.name }}</h3>
<dl>
<dt>{% trans "Number of members" %}</dt>
<dd>{{ users_count }}</dd>
</dl>
</div>
{% endblock %}

View File

@@ -0,0 +1,120 @@
{% extends "sysadmin/sys_inst_info_base.html" %}
{% load i18n seahub_tags %}
{% block right_panel %}
<div class="tabnav">
<ul class="tabnav-tabs">
<li class="tabnav-tab tabnav-tab-cur"><a href="{% url 'sys_inst_info_user' inst.pk %}">{% trans "Members" %}</a></li>
</ul>
</div>
<table>
<tr>
<th width="25%">{% trans "Email" %}</th>
<th width="10%">{% trans "Status" %}</th>
<th width="20%">{% trans "Space Used" %}</th>
<th width="25%">{% trans "Create At / Last Login" %}</th>
<th width="20%">{% trans "Operations" %}</th>
</tr>
{% for user in users %}
<tr data-userid="{{user.email}}">
<td><a href="{% url 'user_info' user.email %}">{{ user.email }}</a></td>
<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 %}
</div>
</td>
<td style="font-size:11px;">
<p> {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %} </p>
</td>
<td style="font-size:11px;">
{{ user.ctime|tsstr_sec }} / {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %}
</td>
<td>
<a href="#" class="js-toggle-admin op vh" data-url="{% url 'sys_inst_toggle_admin' inst.pk user.email %}" data-target="{{ user.email }}">{% if user.inst_admin %}{% trans "Revoke InstAdmin" %}{% else %}{% trans "Set InstAdmin" %}{% endif %}</a>
</td>
</tr>
{% endfor %}
</table>
<div id="activate-msg" class="hide">
<p>{% trans "Activating..., please wait" %}</p>
</div>
{% endblock %}
{% block extra_script %}
<script type="text/javascript">
addConfirmTo($('.js-toggle-admin'), {
'title':"{% trans "Toggle Admin" %}",
'con':"{% trans "Sure ?" %}",
'post': true
});
$('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>
{% endblock %}

View File

@@ -201,6 +201,7 @@ urlpatterns = patterns(
(r'^help/', include('seahub.help.urls')),
url(r'^captcha/', include('captcha.urls')),
(r'^thumbnail/', include('seahub.thumbnail.urls')),
url(r'^inst/', include('seahub.institutions.urls', app_name='institutions', namespace='institutions')),
### system admin ###
url(r'^sys/info/$', sys_info, name='sys_info'),
@@ -234,6 +235,10 @@ urlpatterns = patterns(
url(r'^sys/orgadmin/(?P<org_id>\d+)/group/$', sys_org_info_group, name='sys_org_info_group'),
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/(?P<inst_id>\d+)/remove/$', sys_inst_remove, name='sys_inst_remove'),
url(r'^sys/instadmin/(?P<inst_id>\d+)/user/$', sys_inst_info_user, name='sys_inst_info_user'),
url(r'^sys/instadmin/(?P<inst_id>\d+)/toggleadmin/(?P<email>[^/]+)/$', sys_inst_toggle_admin, name='sys_inst_toggle_admin'),
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'),

View File

@@ -34,7 +34,7 @@ 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.utils import IS_EMAIL_CONFIGURED, string2list, is_valid_username, \
is_pro_version, send_html_email, get_user_traffic_list, get_server_id, \
clear_token, gen_file_get_url, is_org_context, handle_virus_record, \
@@ -2244,3 +2244,129 @@ def sys_check_license(request):
result['expiration_date'] = expiration
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required
@sys_staff_required
def sys_inst_admin(request):
"""List institutions.
"""
if request.method == "POST":
inst_name = request.POST.get('name').strip()
if not inst_name:
messages.error(request, 'Name is required.')
return HttpResponseRedirect(reverse('sys_inst_admin'))
Institution.objects.create(name=inst_name)
messages.success(request, _('Success'))
return HttpResponseRedirect(reverse('sys_inst_admin'))
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '100'))
except ValueError:
current_page = 1
per_page = 100
offset = per_page * (current_page - 1)
limit = per_page + 1
insts = Institution.objects.all()[offset:offset + limit]
if len(insts) == per_page + 1:
page_next = True
else:
page_next = False
return render_to_response(
'sysadmin/sys_inst_admin.html', {
'insts': insts,
'current_page': current_page,
'prev_page': current_page - 1,
'next_page': current_page + 1,
'per_page': per_page,
'page_next': page_next,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
@require_POST
def sys_inst_remove(request, inst_id):
"""Delete an institution.
"""
try:
inst = Institution.objects.get(pk=inst_id)
except Institution.DoesNotExist:
raise Http404
inst.delete()
messages.success(request, _('Success'))
return HttpResponseRedirect(reverse('sys_inst_admin'))
@login_required
@sys_staff_required
def sys_inst_info_user(request, inst_id):
"""List institution members.
"""
try:
inst = Institution.objects.get(pk=inst_id)
except Institution.DoesNotExist:
raise Http404
inst_admins = [x.user for x in InstitutionAdmin.objects.filter(institution=inst)]
usernames = [x.user for x in Profile.objects.filter(institution=inst.name)]
users = [User.objects.get(x) for x in usernames]
last_logins = UserLastLogin.objects.filter(username__in=[x.email for x in users])
for u in users:
_populate_user_quota_usage(u)
if u.username in inst_admins:
u.inst_admin = True
else:
u.inst_admin = False
# populate user last login time
u.last_login = None
for last_login in last_logins:
if last_login.username == u.email:
u.last_login = last_login.last_login
users_count = len(users)
return render_to_response('sysadmin/sys_inst_info_user.html', {
'inst': inst,
'users': users,
'users_count': users_count,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
@require_POST
def sys_inst_toggle_admin(request, inst_id, email):
"""Set or revoke an institution admin.
"""
try:
inst = Institution.objects.get(pk=inst_id)
except Institution.DoesNotExist:
raise Http404
try:
u = User.objects.get(email=email)
except User.DoesNotExist:
assert False, 'TODO'
if u.is_staff:
assert False
res = InstitutionAdmin.objects.filter(institution=inst, user=email)
if len(res) == 0:
InstitutionAdmin.objects.create(institution=inst, user=email)
elif len(res) == 1:
res[0].delete()
# todo: expire user's session
else:
assert False
messages.success(request, _('Success'))
return HttpResponseRedirect(reverse('sys_inst_info_user', args=[inst.pk]))