diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py
index 78fb53ef68..562125d9a9 100644
--- a/seahub/base/context_processors.py
+++ b/seahub/base/context_processors.py
@@ -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,
}
diff --git a/seahub/institutions/__init__.py b/seahub/institutions/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/seahub/institutions/admin.py b/seahub/institutions/admin.py
new file mode 100644
index 0000000000..8c38f3f3da
--- /dev/null
+++ b/seahub/institutions/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/seahub/institutions/decorators.py b/seahub/institutions/decorators.py
new file mode 100644
index 0000000000..6b1d9e7574
--- /dev/null
+++ b/seahub/institutions/decorators.py
@@ -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
diff --git a/seahub/institutions/middleware.py b/seahub/institutions/middleware.py
new file mode 100644
index 0000000000..ca75c6ddeb
--- /dev/null
+++ b/seahub/institutions/middleware.py
@@ -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
diff --git a/seahub/institutions/migrations/0001_initial.py b/seahub/institutions/migrations/0001_initial.py
new file mode 100644
index 0000000000..f4e846a877
--- /dev/null
+++ b/seahub/institutions/migrations/0001_initial.py
@@ -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')),
+ ],
+ ),
+ ]
diff --git a/seahub/institutions/migrations/__init__.py b/seahub/institutions/migrations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/seahub/institutions/models.py b/seahub/institutions/models.py
new file mode 100644
index 0000000000..6b4288f0c1
--- /dev/null
+++ b/seahub/institutions/models.py
@@ -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()
diff --git a/seahub/institutions/templates/institutions/base.html b/seahub/institutions/templates/institutions/base.html
new file mode 100644
index 0000000000..aedb9bbf59
--- /dev/null
+++ b/seahub/institutions/templates/institutions/base.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block inst_admin %}
{% endblock %}
+
+{% block left_panel %}
+
+
{{ request.user.institution.name }}
+
+
+{% endblock %}
diff --git a/seahub/institutions/templates/institutions/info.html b/seahub/institutions/templates/institutions/info.html
new file mode 100644
index 0000000000..a2ae259f38
--- /dev/null
+++ b/seahub/institutions/templates/institutions/info.html
@@ -0,0 +1,27 @@
+{% extends "institutions/base.html" %}
+{% load seahub_tags i18n %}
+
+{% block cur_info %}tab-cur{% endblock %}
+
+{% block right_panel %}
+{% trans "Info" %}
+
+
+ - {% trans "Name" %}
+ - {{ inst.name }}
+
+ - {% trans "Libraries" %}
+ - {{repos_count}}
+
+ - {% trans "Active Users" %} / {% trans "Total Users" %}
+ -
+ {% if active_users_count %}{{ active_users_count }}{% else %}--{% endif %}
+ /
+ {% if users_count %}{{ users_count }}{% else %}--{% endif %}
+
+
+ - {% trans "Groups" %}
+ - {{groups_count}}
+
+{% endblock %}
+
diff --git a/seahub/institutions/templates/institutions/user_info.html b/seahub/institutions/templates/institutions/user_info.html
new file mode 100644
index 0000000000..8efba60a64
--- /dev/null
+++ b/seahub/institutions/templates/institutions/user_info.html
@@ -0,0 +1,130 @@
+{% extends "institutions/base.html" %}
+{% load i18n avatar_tags seahub_tags %}
+{% load staticfiles %}
+
+{% block extra_style %}
+
+
+
+{% endblock %}
+
+{% block left_panel %}
+
+
+
{% trans "Profile" %}
+ {% avatar email 48 %}
+
+ - {% trans "Email" %}
+ - {{ email }}
+
+ {% if profile %}
+ - {% trans "Name" context "true name" %}
+ - {{ profile.nickname }}
+ {% endif %}
+
+ {% if d_profile %}
+ - {% trans "Department" %}
+ - {{ d_profile.department }}
+
+ - {% trans "Telephone" %}
+ - {{ d_profile.telephone }}
+ {% endif %}
+
+
+
{% trans "Space Used" %}
+
{% trans "Used" %}: {{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %}
+
+{% endblock %}
+
+{% block right_panel %}
+
+
+
+
+ {% if owned_repos %}
+
+
+ |
+ {% trans "Name" %} |
+ {% trans "Size"%} |
+ {% trans "Last Update"%} |
+ {% trans "Operations" %} |
+
+
+ {% for repo in owned_repos %}
+
+ {% if repo.encrypted %}
+  |
+ {% else %}
+  |
+ {% endif %}
+
+ {% if not repo.name %}
+ Broken ({{repo.id}}) |
+ {% else %}
+ {% if repo.encrypted %}
+ {{ repo.name }} |
+ {% elif enable_sys_admin_view_repo %}
+ {{ repo.name }} |
+ {% else %}
+ {{ repo.name }} |
+ {% endif %}
+ {% endif %}
+
+ {{ repo.size|filesizeformat }} |
+ {{ repo.last_modify|translate_seahub_time }} |
+
+
+
+ |
+
+ {% endfor %}
+
+ {% else %}
+
+
{% trans "This user has not created any libraries" %}
+
+ {% endif %}
+
+
+
+ {% if personal_groups %}
+
+
+ {% trans "Name" %} |
+ {% trans "Role" %} |
+ {% trans "Create At" %} |
+ {% trans "Operations" %} |
+
+ {% for group in personal_groups %}
+
+ {{ group.group_name }} |
+ {{ group.role }} |
+ {{ group.timestamp|tsstr_sec }} |
+ |
+
+ {% endfor %}
+
+ {% else %}
+
+
{% trans "This user has not created or joined any groups" %}
+
+ {% endif %}
+
+
+
+{% include "sysadmin/repo_transfer_form.html" %}
+{% endblock %}
+
+
+{% block extra_script %}
+
+{% endblock %}
diff --git a/seahub/institutions/templates/institutions/useradmin.html b/seahub/institutions/templates/institutions/useradmin.html
new file mode 100644
index 0000000000..20dd812927
--- /dev/null
+++ b/seahub/institutions/templates/institutions/useradmin.html
@@ -0,0 +1,66 @@
+{% extends "institutions/base.html" %}
+{% load seahub_tags i18n %}
+{% block cur_users %}tab-cur{% endblock %}
+
+
+{% block right_panel %}
+
+
+
+
+ {% trans "Email" %} |
+ {% trans "Status" %} |
+ {% trans "Space Used" %} |
+ {% trans "Create At / Last Login" %} |
+ {% trans "Operations" %} |
+
+
+ {% for user in users %}
+
+ {{ user.email }}
+ |
+
+
+ {% if user.is_active %}
+ {% trans "Active" %}
+ {% else %}
+ {% trans "Inactive" %}
+ {% endif %}
+
+ |
+
+ {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}
+ |
+
+ {% if user.source == "DB" %}
+ {{ user.ctime|tsstr_sec }} /
+ {% else %}
+ -- /
+ {% endif %}
+ {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %}
+ |
+
+ {% if not user.is_self %}
+ {% trans "Delete" %}
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+{% endblock %}
+
+{% block extra_script %}
+
+{% endblock %}
diff --git a/seahub/institutions/tests.py b/seahub/institutions/tests.py
new file mode 100644
index 0000000000..7ce503c2dd
--- /dev/null
+++ b/seahub/institutions/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/seahub/institutions/urls.py b/seahub/institutions/urls.py
new file mode 100644
index 0000000000..a0a85cd74b
--- /dev/null
+++ b/seahub/institutions/urls.py
@@ -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[^/]+)/$', user_info, name='user_info'),
+ url(r'^useradmin/remove/(?P[^/]+)/$', user_remove, name='user_remove'),
+)
diff --git a/seahub/institutions/views.py b/seahub/institutions/views.py
new file mode 100644
index 0000000000..25d62907e7
--- /dev/null
+++ b/seahub/institutions/views.py
@@ -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)
diff --git a/seahub/settings.py b/seahub/settings.py
index 2f17665ee2..1a414d886d 100644
--- a/seahub/settings.py
+++ b/seahub/settings.py
@@ -199,6 +199,7 @@ INSTALLED_APPS = (
'seahub.avatar',
'seahub.base',
'seahub.contacts',
+ 'seahub.institutions',
'seahub.wiki',
'seahub.group',
'seahub.message',
diff --git a/seahub/templates/base.html b/seahub/templates/base.html
index dda8306b83..f5f61c4f91 100644
--- a/seahub/templates/base.html
+++ b/seahub/templates/base.html
@@ -91,6 +91,11 @@
{% block org_admin %}
{% endblock %}
{% endif %}
+ {% if request.user.inst_admin %}
+
+ {% block inst_admin %}

{% endblock %}
+
+ {% endif %}
{% else %} {# for non-logged-in user #}
diff --git a/seahub/templates/base_for_backbone.html b/seahub/templates/base_for_backbone.html
index 110f2fc872..115c1e2fc0 100644
--- a/seahub/templates/base_for_backbone.html
+++ b/seahub/templates/base_for_backbone.html
@@ -102,6 +102,12 @@
{% block org_admin %}
{% endblock %}
{% endif %}
+ {% if request.user.inst_admin %}
+
+ {% block inst_admin %}

{% endblock %}
+
+ {% endif %}
+
{% endif %}
diff --git a/seahub/templates/sysadmin/base.html b/seahub/templates/sysadmin/base.html
index ab400e9439..47b95b6b89 100644
--- a/seahub/templates/sysadmin/base.html
+++ b/seahub/templates/sysadmin/base.html
@@ -25,6 +25,11 @@
{% trans "Organizations" %}
{% endif %}
+ {% if multi_institution %}
+
+ {% trans "Institutions" %}
+
+ {% endif %}
{% trans "Notifications" %}
diff --git a/seahub/templates/sysadmin/sys_inst_admin.html b/seahub/templates/sysadmin/sys_inst_admin.html
new file mode 100644
index 0000000000..2b4bcc9a6f
--- /dev/null
+++ b/seahub/templates/sysadmin/sys_inst_admin.html
@@ -0,0 +1,71 @@
+{% extends "sysadmin/base.html" %}
+{% load i18n seahub_tags %}
+
+{% block cur_inst %}tab-cur{% endblock %}
+
+{% block right_panel %}
+
+
+ {% trans "All Institutions" %}
+
+
+
+
+
+
+
+
+{% if insts %}
+
+
+ {% trans "Name" %} |
+ {% trans "Create At" %} |
+ {% trans "Operations" %} |
+
+ {% for inst in insts %}
+
+ {{ inst.name }} |
+ {{ inst.create_time|translate_seahub_time }} |
+
+ {% trans "Remove" %}
+ |
+
+ {% endfor %}
+
+
+{% include "snippets/admin_paginator.html" %}
+{% else %}
+{% trans "Empty" %}
+{% endif %}
+{% endblock %}
+
+{% block extra_script %}
+
+{% endblock %}
diff --git a/seahub/templates/sysadmin/sys_inst_info_base.html b/seahub/templates/sysadmin/sys_inst_info_base.html
new file mode 100644
index 0000000000..471b3db6ba
--- /dev/null
+++ b/seahub/templates/sysadmin/sys_inst_info_base.html
@@ -0,0 +1,24 @@
+{% extends "admin_base.html" %}
+{% load i18n seahub_tags %}
+
+{% block nav_orgadmin_class %}class="cur"{% endblock %}
+
+{% block extra_style %}
+
+{% endblock %}
+
+{% block left_panel %}
+
+
+
{{ inst.name }}
+
+ - {% trans "Number of members" %}
+ - {{ users_count }}
+
+
+{% endblock %}
diff --git a/seahub/templates/sysadmin/sys_inst_info_user.html b/seahub/templates/sysadmin/sys_inst_info_user.html
new file mode 100644
index 0000000000..001cc1107b
--- /dev/null
+++ b/seahub/templates/sysadmin/sys_inst_info_user.html
@@ -0,0 +1,120 @@
+{% extends "sysadmin/sys_inst_info_base.html" %}
+{% load i18n seahub_tags %}
+
+{% block right_panel %}
+
+
+
+
+ {% trans "Email" %} |
+ {% trans "Status" %} |
+ {% trans "Space Used" %} |
+ {% trans "Create At / Last Login" %} |
+ {% trans "Operations" %} |
+
+
+ {% for user in users %}
+
+ {{ user.email }} |
+
+
+ {% if user.is_active %}
+ {% trans "Active" %}
+ {% else %}
+ {% trans "Inactive" %}
+ {% endif %}
+
+ |
+
+ {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}
+ |
+
+ {{ user.ctime|tsstr_sec }} / {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %}
+ |
+
+ {% if user.inst_admin %}{% trans "Revoke InstAdmin" %}{% else %}{% trans "Set InstAdmin" %}{% endif %}
+ |
+
+ {% endfor %}
+
+
+
+
{% trans "Activating..., please wait" %}
+
+
+{% endblock %}
+
+{% block extra_script %}
+
+{% endblock %}
diff --git a/seahub/urls.py b/seahub/urls.py
index 85cd34aec5..c9f37b569b 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -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\d+)/group/$', sys_org_info_group, name='sys_org_info_group'),
url(r'^sys/orgadmin/(?P\d+)/library/$', sys_org_info_library, name='sys_org_info_library'),
url(r'^sys/orgadmin/(?P\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\d+)/remove/$', sys_inst_remove, name='sys_inst_remove'),
+ url(r'^sys/instadmin/(?P\d+)/user/$', sys_inst_info_user, name='sys_inst_info_user'),
+ url(r'^sys/instadmin/(?P\d+)/toggleadmin/(?P[^/]+)/$', 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'),
diff --git a/seahub/views/sysadmin.py b/seahub/views/sysadmin.py
index 109db0bc18..6db1e3a8f3 100644
--- a/seahub/views/sysadmin.py
+++ b/seahub/views/sysadmin.py
@@ -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]))