diff --git a/media/css/seahub.css b/media/css/seahub.css index 7c1a98861e..a5e78750b4 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -92,6 +92,7 @@ .icon-eye:before { content: "\f06e"; } .icon-eye-slash:before { content: "\f070"; } .icon-plus-square:before { content: "\f0fe"; } +.icon-envelope:before { content: "\f0e0"; } .fa-1x { font-size: 1.3em; } @@ -3275,18 +3276,18 @@ button.sf-dropdown-toggle:focus { #user-profile .avatar { border-radius: 0; } -#user-profile p { +.user-profile-nickname { padding: 8px 15px; margin: 0; -} -#user-profile .nickname { font-size: 18px; } -#user-profile .info { +.user-profile-info { + padding: 8px 15px; border-top: 1px solid #eee; } -#user-profile .info-detail { - padding-left: 6px; +.user-profile-info-icon { + display:inline-block; + width:1.4em; } /* shared file view */ #shared-file-view-hd .cur-path, diff --git a/seahub/api2/endpoints/admin/sysinfo.py b/seahub/api2/endpoints/admin/sysinfo.py index 7a717257fa..336e34425c 100644 --- a/seahub/api2/endpoints/admin/sysinfo.py +++ b/seahub/api2/endpoints/admin/sysinfo.py @@ -5,7 +5,6 @@ from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAdminUser from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework import status from seaserv import seafile_api, ccnet_api from pysearpc import SearpcError @@ -15,7 +14,6 @@ from seahub.utils.licenseparse import parse_license from seahub.api2.authentication import TokenAuthentication from seahub.api2.throttling import UserRateThrottle -from seahub.api2.utils import api_error import seahub.settings try: @@ -29,9 +27,14 @@ class SysInfo(APIView): """Show system info. """ authentication_classes = (TokenAuthentication, SessionAuthentication) - throttle_classes = (UserRateThrottle, ) + throttle_classes = (UserRateThrottle,) permission_classes = (IsAdminUser,) + def _get_license_dict(self): + license_file = os.path.join(seahub.settings.PROJECT_ROOT, '../../seafile-license.txt') + license_dict = parse_license(license_file) + return license_dict + def get(self, request, format=None): # count repos try: @@ -92,15 +95,20 @@ class SysInfo(APIView): is_pro = is_pro_version() if is_pro: - license_file = os.path.join(seahub.settings.PROJECT_ROOT, '../../seafile-license.txt') - license_dict = parse_license(license_file) + license_dict = self._get_license_dict() else: license_dict = {} if license_dict: with_license = True + try: + max_users = int(license_dict.get('MaxUsers', '')) + except ValueError as e: + logger.error(e) + max_users = 0 else: with_license = False + max_users = 0 info = { 'users_count': active_users + inactive_users, @@ -111,7 +119,9 @@ class SysInfo(APIView): 'multi_tenancy_enabled': multi_tenancy_enabled, 'is_pro': is_pro, 'with_license': with_license, - 'license': license_dict + 'license_expiration': license_dict.get('Expiration', ''), + 'license_maxusers': max_users, + 'license_to': license_dict.get('Name', ''), } return Response(info) diff --git a/seahub/api2/endpoints/search_user.py b/seahub/api2/endpoints/search_user.py index 2d642dc57f..4c1497c806 100644 --- a/seahub/api2/endpoints/search_user.py +++ b/seahub/api2/endpoints/search_user.py @@ -8,19 +8,20 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from rest_framework import status +from django.conf import settings + import seaserv from seahub.api2.authentication import TokenAuthentication from seahub.api2.throttling import UserRateThrottle from seahub.api2.utils import api_error -from seahub.utils import is_org_context +from seahub.utils import is_valid_email, is_org_context from seahub.base.accounts import User from seahub.base.templatetags.seahub_tags import email2nickname from seahub.profile.models import Profile from seahub.contacts.models import Contact from seahub.avatar.templatetags.avatar_tags import api_avatar_url -from seahub.settings import ENABLE_GLOBAL_ADDRESSBOOK, ENABLE_SEARCH_FROM_LDAP_DIRECTLY class SearchUser(APIView): @@ -50,7 +51,7 @@ class SearchUser(APIView): users_result = [] username = request.user.username - if request.cloud_mode: + if settings.CLOUD_MODE: if is_org_context(request): url_prefix = request.user.org.url_prefix users = seaserv.get_org_users_by_url_prefix(url_prefix, -1, -1) @@ -61,13 +62,14 @@ class SearchUser(APIView): # when search profile, only search users in org # 'nickname__icontains' for search by nickname # 'contact_email__icontains' for search by contact email - users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \ - (Q(nickname__icontains=q)) | \ - Q(contact_email__icontains=q)).values('user') - elif ENABLE_GLOBAL_ADDRESSBOOK: + users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & + (Q(nickname__icontains=q)) | Q(contact_email__icontains=q)).values('user') + + elif settings.ENABLE_GLOBAL_ADDRESSBOOK: users_from_ccnet = search_user_from_ccnet(q) - users_from_profile = Profile.objects.filter(Q(contact_email__icontains=q) | \ + users_from_profile = Profile.objects.filter(Q(contact_email__icontains=q) | Q(nickname__icontains=q)).values('user') + else: # in cloud mode, user will be added to Contact when share repo users = [] @@ -83,16 +85,18 @@ class SearchUser(APIView): users.append(c) users_from_ccnet = filter(lambda u: q in u.email, users) + if is_valid_email(q): + users_from_ccnet += search_user_from_ccnet(q) + # 'user__in' for only get profile of contacts # 'nickname__icontains' for search by nickname # 'contact_email__icontains' for search by contact - users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \ - (Q(nickname__icontains=q)) | \ - Q(contact_email__icontains=q)).values('user') + users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & + (Q(nickname__icontains=q)) | Q(contact_email__icontains=q)).values('user') else: users_from_ccnet = search_user_from_ccnet(q) - users_from_profile = Profile.objects.filter(Q(contact_email__icontains=q) | \ + users_from_profile = Profile.objects.filter(Q(contact_email__icontains=q) | Q(nickname__icontains=q)).values('user') # remove inactive users and add to result @@ -158,7 +162,7 @@ def search_user_from_ccnet(q): users.extend(ldap_imported_users) count = len(users) - if count < 10 and ENABLE_SEARCH_FROM_LDAP_DIRECTLY: + if count < 10 and settings.ENABLE_SEARCH_FROM_LDAP_DIRECTLY: all_ldap_users = seaserv.ccnet_threaded_rpc.search_ldapusers(q, 0, 10 - count) users.extend(all_ldap_users) diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index c0ec601338..c9f74c332a 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -61,7 +61,7 @@ urlpatterns = patterns('', url(r'^repos/(?P[-0-9-a-f]{36})/file/revert/$', FileRevert.as_view(), name='api2-file-revert'), url(r'^repos/(?P[-0-9-a-f]{36})/file/shared-link/$', FileSharedLinkView.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='DirView'), - url(r'^repos/(?P[-0-9-a-f]{36})/dir/sub_repo/$', DirSubRepoView.as_view()), + url(r'^repos/(?P[-0-9-a-f]{36})/dir/sub_repo/$', DirSubRepoView.as_view(), name="api2-dir-sub-repo"), url(r'^repos/(?P[-0-9-a-f]{36})/dir/shared_items/$', DirSharedItemsEndpoint.as_view(), name="api2-dir-shared-items"), url(r'^repos/(?P[-0-9-a-f]{36})/dir/download/$', DirDownloadView.as_view(), name='api2-dir-download'), url(r'^repos/(?P[-0-9-a-f]{36})/dir/revert/$', DirRevert.as_view(), name='api2-dir-revert'), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 71a4d53d5f..52fbf0b41d 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -2716,67 +2716,94 @@ class DirRevert(APIView): class DirSubRepoView(APIView): - authentication_classes = (TokenAuthentication, ) + authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated,) - throttle_classes = (UserRateThrottle, ) + throttle_classes = (UserRateThrottle,) - # from seahub.views.ajax.py::sub_repo def get(self, request, repo_id, format=None): - ''' - check if a dir has a corresponding sub_repo - if it does not have, create one - ''' + """ Create sub-repo for folder - result = {} + Permission checking: + 1. user with `r` or `rw` permission. + 2. password correct for encrypted repo. + """ - path = request.GET.get('p') - name = request.GET.get('name') - password = request.GET.get('password', None) + # argument check + path = request.GET.get('p', None) + if not path: + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + name = request.GET.get('name', None) + if not name: + error_msg = 'name invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # recourse check repo = get_repo(repo_id) if not repo: - result['error'] = 'Library not found.' - return HttpResponse(json.dumps(result), status=404, content_type=json_content_type) + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) - if not (path and name): - result['error'] = 'Argument missing' - return HttpResponse(json.dumps(result), status=400, content_type=json_content_type) + # permission check + if not check_folder_permission(request, repo_id, path) or \ + not request.user.permissions.can_add_repo(): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username - - # check if the sub-lib exist - try: - sub_repo = seafile_api.get_virtual_repo(repo_id, path, username) - except SearpcError, e: - result['error'] = e.msg - return HttpResponse(json.dumps(result), status=500, content_type=json_content_type) - - if sub_repo: - result['sub_repo_id'] = sub_repo.id - else: - if not request.user.permissions.can_add_repo(): - return api_error(status.HTTP_403_FORBIDDEN, - 'You do not have permission to create library.') - - # create a sub-lib - try: - # use name as 'repo_name' & 'repo_desc' for sub_repo - if repo.encrypted: - if password: - sub_repo_id = seafile_api.create_virtual_repo(repo_id, - path, name, name, username, password) + password = request.GET.get('password', '') + if repo.encrypted: + # check password for encrypted repo + if not password: + error_msg = 'password invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + else: + try: + seafile_api.set_passwd(repo_id, username, password) + except SearpcError as e: + if e.msg == 'Bad arguments': + error_msg = 'Bad arguments' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + elif e.msg == 'Incorrect password': + error_msg = _(u'Wrong password') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + elif e.msg == 'Internal server error': + error_msg = _(u'Internal server error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) else: - result['error'] = 'Password Required.' - return HttpResponse(json.dumps(result), status=403, content_type=json_content_type) + error_msg = _(u'Decrypt library error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + # create sub-lib for encrypted repo + try: + if is_org_context(request): + org_id = request.user.org.org_id + sub_repo_id = seafile_api.create_org_virtual_repo( + org_id, repo_id, path, name, name, username, password) else: - sub_repo_id = seafile_api.create_virtual_repo(repo_id, path, name, name, username) + sub_repo_id = seafile_api.create_virtual_repo( + repo_id, path, name, name, username, password) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + else: + # create sub-lib for common repo + try: + if is_org_context(request): + org_id = request.user.org.org_id + sub_repo_id = seafile_api.create_org_virtual_repo( + org_id, repo_id, path, name, name, username) + else: + sub_repo_id = seafile_api.create_virtual_repo( + repo_id, path, name, name, username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - result['sub_repo_id'] = sub_repo_id - except SearpcError, e: - result['error'] = e.msg - return HttpResponse(json.dumps(result), status=500, content_type=json_content_type) - - return HttpResponse(json.dumps(result), content_type=json_content_type) + return Response({'sub_repo_id': sub_repo_id}) ########## Sharing class SharedRepos(APIView): diff --git a/seahub/base/accounts.py b/seahub/base/accounts.py index c93d4ae5d4..c2ea62cba0 100644 --- a/seahub/base/accounts.py +++ b/seahub/base/accounts.py @@ -1,4 +1,6 @@ # encoding: utf-8 +import re + from django import forms from django.core.mail import send_mail from django.utils import translation @@ -8,7 +10,7 @@ from django.contrib.sites.models import RequestSite from django.contrib.sites.models import Site import seaserv from seaserv import ccnet_threaded_rpc, unset_repo_passwd, is_passwd_set, \ - seafile_api + seafile_api, ccnet_api from constance import config from registration import signals @@ -220,7 +222,9 @@ class User(object): seafile_api.remove_repo(r.id) clear_token(self.username) - ccnet_threaded_rpc.remove_emailuser(source, self.username) + # remove current user from joined groups + ccnet_api.remove_group_user(self.username) + ccnet_api.remove_emailuser(source, self.username) Profile.objects.delete_profile_by_user(self.username) def get_and_delete_messages(self): @@ -552,9 +556,15 @@ class RegistrationForm(forms.Form): password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), label=_("Password (again)")) + @classmethod + def allow_register(self, email): + prog = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", + re.IGNORECASE) + return False if prog.match(email) is None else True + def clean_email(self): email = self.cleaned_data['email'] - if not is_valid_username(email): + if not self.allow_register(email): raise forms.ValidationError(_("Enter a valid email address.")) emailuser = ccnet_threaded_rpc.get_emailuser(email) diff --git a/seahub/institutions/templates/institutions/useradmin.html b/seahub/institutions/templates/institutions/useradmin.html index 7aa79fee76..5d5a86eac9 100644 --- a/seahub/institutions/templates/institutions/useradmin.html +++ b/seahub/institutions/templates/institutions/useradmin.html @@ -31,7 +31,12 @@ {% else %} {% trans "Inactive" %} {% endif %} + +

{{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}

@@ -58,6 +63,10 @@

{% trans "Empty" %}

{% endif %} +
+

{% trans "Activating..., please wait" %}

+
+ {% endblock %} {% block extra_script %} @@ -68,5 +77,75 @@ 'post': true // post request }); +$('tr:gt(0)').hover( + function() { + $(this).find('.user-status-edit-icon, .user-role-edit-icon').removeClass('vh'); + }, + function() { + $(this).find('.user-status-edit-icon, .user-role-edit-icon').addClass('vh'); + } +); +$('.user-status-edit-icon, .user-role-edit-icon').click(function() { + $(this).parent().addClass('hide'); + $(this).parent().next().removeClass('hide'); // show 'select' +}); +$('.user-status-select, .user-role-select').change(function() { + var select = $(this), + select_val = select.val(), + uid = select.parents('tr').attr('data-userid'), + $select_prev = $(this).prev('.user-status, .user-role'), // .user-status, .user-role + url, data; + + if (select.hasClass('user-status-select')) { + url = "{{ SITE_ROOT }}inst/useradmin/toggle_status/" + uid + "/"; + data = {'s': select_val}; + } else { + url = "{{ SITE_ROOT }}inst/useradmin/toggle_role/" + uid + "/"; + data = {'r': select_val}; + } + + if (select_val == 1) { + // show activating popup + $('#activate-msg').modal(); + $('#simplemodal-container').css({'height':'auto'}); + } + $.ajax({ + url: url, + type: 'POST', + dataType: 'json', + data: data, + cache: false, + beforeSend: prepareCSRFToken, + 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'); + } + $('.user-status-cur-value', $select_prev).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(); + } + }); +}); +// select shows, but the user doesn't select a value, or doesn't change the permission, click other place to hide the select +$(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'); + } +}); + {% endblock %} diff --git a/seahub/institutions/urls.py b/seahub/institutions/urls.py index fededaf9eb..c1f828af2c 100644 --- a/seahub/institutions/urls.py +++ b/seahub/institutions/urls.py @@ -1,6 +1,7 @@ from django.conf.urls import patterns, url -from .views import info, useradmin, user_info, user_remove, useradmin_search +from .views import (info, useradmin, user_info, user_remove, useradmin_search, + user_toggle_status) urlpatterns = patterns( '', @@ -9,4 +10,5 @@ urlpatterns = patterns( url(r'^useradmin/info/(?P[^/]+)/$', user_info, name='user_info'), url(r'^useradmin/remove/(?P[^/]+)/$', user_remove, name='user_remove'), url('^useradmin/search/$', useradmin_search, name="useradmin_search"), + url(r'^useradmin/toggle_status/(?P[^/]+)/$', user_toggle_status, name='user_toggle_status'), ) diff --git a/seahub/institutions/views.py b/seahub/institutions/views.py index b2f869c91b..316cf1530a 100644 --- a/seahub/institutions/views.py +++ b/seahub/institutions/views.py @@ -1,8 +1,9 @@ +import json import logging from django.core.urlresolvers import reverse from django.contrib import messages -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext as _ @@ -10,13 +11,16 @@ import seaserv from seaserv import seafile_api from pysearpc import SearpcError +from seahub.auth.decorators import login_required_ajax 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 import is_valid_username, clear_token from seahub.utils.rpc import mute_seafile_api +from seahub.views.sysadmin import email_user_on_activation logger = logging.getLogger(__name__) @@ -181,3 +185,46 @@ def user_remove(request, email): messages.error(request, _(u'Failed to delete: the user does not exist')) return HttpResponseRedirect(next) + +@login_required_ajax +@require_POST +@inst_admin_required +@inst_admin_can_manage_user +def user_toggle_status(request, email): + content_type = 'application/json; charset=utf-8' + + if not is_valid_username(email): + return HttpResponse(json.dumps({'success': False}), status=400, + content_type=content_type) + + try: + user_status = int(request.POST.get('s', 0)) + except ValueError: + user_status = 0 + + try: + user = User.objects.get(email) + user.is_active = bool(user_status) + result_code = user.save() + if result_code == -1: + return HttpResponse(json.dumps({'success': False}), status=403, + content_type=content_type) + + if user.is_active is True: + try: + email_user_on_activation(user) + email_sent = True + except Exception as e: + logger.error(e) + email_sent = False + + return HttpResponse(json.dumps({'success': True, + 'email_sent': email_sent, + }), content_type=content_type) + else: + clear_token(user.email) + return HttpResponse(json.dumps({'success': True}), + content_type=content_type) + except User.DoesNotExist: + return HttpResponse(json.dumps({'success': False}), status=500, + content_type=content_type) diff --git a/seahub/profile/templates/profile/user_profile.html b/seahub/profile/templates/profile/user_profile.html index 1b9057b9e4..dd9685ce3d 100644 --- a/seahub/profile/templates/profile/user_profile.html +++ b/seahub/profile/templates/profile/user_profile.html @@ -3,25 +3,32 @@ {% block main_panel %}
- {% if user %} +{% if user %} {% avatar user.username 290 %} - {% else %} - {% avatar "" 290 %} - {% endif %} -

{{ nickname }}

- {% if d_profile %} + +
    + {% if d_profile %} {% if d_profile.department %} -

    - - {{ d_profile.department }} -

    + {% endif %} {% if d_profile.telephone %} -

    - - {{ d_profile.telephone }} -

    + {% endif %} - {% endif %} + {% endif %} + +
+ +{% else %} + {% avatar "" 290 %} +{% endif %}
{% endblock %} diff --git a/seahub/profile/views.py b/seahub/profile/views.py index b90e2c20c2..e4ab87a6eb 100644 --- a/seahub/profile/views.py +++ b/seahub/profile/views.py @@ -113,17 +113,20 @@ def user_profile(request, username): else: user = None - nickname = '' if user is None else email2nickname(user.username) - if user is not None: + nickname = email2nickname(user.username) + contact_email = Profile.objects.get_contact_email_by_user(user.username) d_profile = DetailedProfile.objects.get_detailed_profile_by_user( user.username) else: + nickname = '' + contact_email = '' d_profile = None return render_to_response('profile/user_profile.html', { 'user': user, 'nickname': nickname, + 'contact_email': contact_email, 'd_profile': d_profile, }, context_instance=RequestContext(request)) diff --git a/seahub/settings.py b/seahub/settings.py index c3be3bdae1..9e28a83bfa 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -345,6 +345,8 @@ REST_FRAMEWORK = { 'anon': '5/minute', 'user': '300/minute', }, + # https://github.com/tomchristie/django-rest-framework/issues/2891 + 'UNICODE_JSON': False, } # file and path diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html index 7fd2dec1b6..50d80a9587 100644 --- a/seahub/templates/js/sysadmin-templates.html +++ b/seahub/templates/js/sysadmin-templates.html @@ -91,7 +91,9 @@
<% if (is_pro) { %> {% trans "Professional Edition" %} <% if (with_license) { %> - {% trans "expires on" %} <%- license.Expiration %> + {% trans "licensed to" %} <%- license_to %> + , + {% trans "expires on" %} <%- license_expiration %> <% } %> <% } else { %> {% trans "Community Edition" %} @@ -110,7 +112,7 @@ <%- users_count %> / <% if (with_license) { %> - <%- license.MaxUsers %> + <%- license_maxusers %> <% } else { %> -- <% } %> diff --git a/seahub/templates/sysadmin/sys_info.html b/seahub/templates/sysadmin/sys_info.html deleted file mode 100644 index a6c61174db..0000000000 --- a/seahub/templates/sysadmin/sys_info.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends "sysadmin/base.html" %} -{% load seahub_tags i18n %} - -{% block cur_info %}tab-cur{% endblock %} - -{% block right_panel %} -

{% trans "Info" %}

- -
-
{% trans "System Info" %}
-
{% if is_pro %} - {% trans "Professional Edition" %} {% if license_dict %} {% trans "expires on" %} {{ license_dict.Expiration}}{% endif %} - {% else %} - {% trans "Community Edition" %} - {% trans "Upgrade to Pro Edition" %} - {% endif %} -
- -
{% trans "Libraries" %}
-
{{repos_count}}
- - {% if is_pro %} -
{% trans "Active Users" %} / {% trans "Total Users" %} / {% trans "Limits" %}
-
- {% if active_users_count %}{{ active_users_count }}{% else %}--{% endif %} - / - {% if users_count %}{{ users_count }}{% else %}--{% endif %} - / - {% if license_dict %}{{ license_dict.MaxUsers }}{% else %}--{% endif %} -
- {% else %} -
{% trans "Active Users" %} / {% trans "Total Users" %}
-
- {% if active_users_count %}{{ active_users_count }}{% else %}--{% endif %} - / - {% if users_count %}{{ users_count }}{% else %}--{% endif %} -
- {% endif %} - -
{% trans "Groups" %}
-
{{groups_count}}
- - {% if multi_tenancy %} -
{% trans "Organizations" %}
-
{{org_count}}
- {% endif %} - -
-{% endblock %} - diff --git a/seahub/templates/sysadmin/sys_org_info_user.html b/seahub/templates/sysadmin/sys_org_info_user.html index 7745b088f8..f49df4a31c 100644 --- a/seahub/templates/sysadmin/sys_org_info_user.html +++ b/seahub/templates/sysadmin/sys_org_info_user.html @@ -45,8 +45,8 @@ {% if not user.is_self %} - {% trans "Delete" %} - {% trans "ResetPwd" %} + {% trans "Delete" %} + {% trans "ResetPwd" %} {% endif %} @@ -63,12 +63,14 @@