diff --git a/media/css/seahub.css b/media/css/seahub.css
index c529a2c2b4..87f212b2ed 100644
--- a/media/css/seahub.css
+++ b/media/css/seahub.css
@@ -820,6 +820,16 @@ textarea:-moz-placeholder {/* for FF */
margin:0;
color:#000;
}
+/***** header *****/
+.header-bar {
+ padding:9px 10px;
+ background:#f2f2f2;
+ margin-bottom:.5em;
+ border-radius:2px;
+ padding-bottom:0;
+ height:39px;
+ overflow:hidden;
+}
/**** narrow-panel ****/
.narrow-panel {
width:320px;
diff --git a/seahub/api2/endpoints/admin/sysinfo.py b/seahub/api2/endpoints/admin/sysinfo.py
new file mode 100644
index 0000000000..1ad51837be
--- /dev/null
+++ b/seahub/api2/endpoints/admin/sysinfo.py
@@ -0,0 +1,117 @@
+import logging
+import os
+
+from django.utils.dateformat import DateFormat
+from django.utils.translation import ugettext as _
+from django.template.defaultfilters import filesizeformat
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAuthenticated, 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
+
+from seahub.utils.rpc import mute_seafile_api
+from seahub.utils import is_pro_version
+from seahub.utils.licenseparse import parse_license
+
+from seahub.api2.utils import api_error
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+
+import seahub.settings
+try:
+ from seahub.settings import MULTI_TENANCY
+except ImportError:
+ MULTI_TENANCY = False
+
+logger = logging.getLogger(__name__)
+
+class SysInfo(APIView):
+ """Show system info.
+ """
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAdminUser,)
+
+ def get(self, request, format=None):
+ # count repos
+ repos_count = mute_seafile_api.count_repos()
+
+ # count groups
+ try:
+ groups_count = len(ccnet_api.get_all_groups(-1, -1))
+ except Exception as e:
+ logger.error(e)
+ groups_count = 0
+
+ # count orgs
+ if MULTI_TENANCY:
+ multi_tenacy_enabled = True
+ try:
+ org_count = ccnet_api.count_orgs()
+ except Exception as e:
+ logger.error(e)
+ org_count = 0
+ else:
+ multi_tenacy_enabled = False
+ org_count = 0
+
+ # count users
+ try:
+ active_db_users = ccnet_api.count_emailusers('DB')
+ except Exception as e:
+ logger.error(e)
+ active_db_users = 0
+
+ try:
+ active_ldap_users = ccnet_api.count_emailusers('LDAP')
+ except Exception as e:
+ logger.error(e)
+ active_ldap_users = 0
+
+ try:
+ inactive_db_users = ccnet_api.count_inactive_emailusers('DB')
+ except Exception as e:
+ logger.error(e)
+ inactive_db_users = 0
+
+ try:
+ inactive_ldap_users = ccnet_api.count_inactive_emailusers('LDAP')
+ except Exception as e:
+ logger.error(e)
+ inactive_ldap_users = 0
+
+ active_users = active_db_users + active_ldap_users if active_ldap_users > 0 \
+ else active_db_users
+
+ inactive_users = inactive_db_users + inactive_ldap_users if inactive_ldap_users > 0 \
+ else inactive_db_users
+
+ 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)
+ else:
+ license_dict = {}
+
+ if license_dict:
+ with_license = True
+ else:
+ with_license = False
+
+ info = {
+ 'users_count': active_users + inactive_users,
+ 'active_users_count': active_users,
+ 'repos_count': repos_count,
+ 'groups_count': groups_count,
+ 'org_count': org_count,
+ 'multi_tenancy_enabled': multi_tenacy_enabled,
+ 'is_pro': is_pro,
+ 'with_license': with_license,
+ 'license': license_dict
+ }
+
+ return Response(info)
diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html
index 730dcdaa50..48c9754ef3 100644
--- a/seahub/templates/js/sysadmin-templates.html
+++ b/seahub/templates/js/sysadmin-templates.html
@@ -46,3 +46,56 @@
{% endif %}
+
+
diff --git a/seahub/urls.py b/seahub/urls.py
index 9b5371a472..626c0e5530 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -28,6 +28,7 @@ from seahub.api2.endpoints.admin.login import Login
from seahub.api2.endpoints.admin.file_audit import FileAudit
from seahub.api2.endpoints.admin.file_update import FileUpdate
from seahub.api2.endpoints.admin.perm_audit import PermAudit
+from seahub.api2.endpoints.admin.sysinfo import SysInfo
# Uncomment the next two lines to enable the admin:
#from django.contrib import admin
@@ -189,6 +190,7 @@ urlpatterns = patterns(
url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/file/$', FileView.as_view(), name='api-v2.1-file-view'),
url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='api-v2.1-dir-view'),
url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/set-password/$', RepoSetPassword.as_view(), name="api-v2.1-repo-set-password"),
+ url(r'^api/v2.1/admin/sysinfo/$', SysInfo.as_view(), name='api-v2.1-sysinfo'),
(r'^avatar/', include('seahub.avatar.urls')),
(r'^notification/', include('seahub.notifications.urls')),
diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py
index 2ac735a4f0..c24652cba3 100644
--- a/seahub/utils/__init__.py
+++ b/seahub/utils/__init__.py
@@ -1026,11 +1026,11 @@ if HAS_OFFICE_CONVERTER:
def cluster_delegate(delegate_func):
'''usage:
-
+
@cluster_delegate(funcA)
def func(*args):
...non-cluster logic goes here...
-
+
- In non-cluster mode, this decorator effectively does nothing.
- In cluster mode, if this node is not the office convert node,
funcA is called instead of the decorated function itself
@@ -1261,6 +1261,10 @@ def do_urlopen(url, data=None, headers=None):
return ret
def is_pro_version():
+ if seahub.settings.DEBUG:
+ if hasattr(seahub.settings, 'IS_PRO_VERSION') \
+ and seahub.settings.IS_PRO_VERSION:
+ return True
if EVENTS_CONFIG_FILE:
return True
else:
diff --git a/static/scripts/common.js b/static/scripts/common.js
index 0093406058..ec94c12687 100644
--- a/static/scripts/common.js
+++ b/static/scripts/common.js
@@ -154,6 +154,8 @@ define([
case 'search_user': return siteRoot + 'api2/search-user/';
case 'user_profile': return siteRoot + 'profile/' + options.username + '/';
case 'space_and_traffic': return siteRoot + 'ajax/space_and_traffic/';
+ // sysadmin
+ case 'sysinfo': return siteRoot + 'api/v2.1/admin/sysinfo/';
}
},
diff --git a/static/scripts/sysadmin-app/models/sysinfo.js b/static/scripts/sysadmin-app/models/sysinfo.js
new file mode 100644
index 0000000000..272dd89dbe
--- /dev/null
+++ b/static/scripts/sysadmin-app/models/sysinfo.js
@@ -0,0 +1,17 @@
+define([
+ 'underscore',
+ 'backbone',
+ 'common'
+], function(_, Backbone, Common) {
+ 'use strict';
+
+ var SysInfo = Backbone.Model.extend({
+
+ url: function () {
+ return Common.getUrl({name: 'sysinfo'});
+ },
+
+ });
+
+ return SysInfo;
+});
diff --git a/static/scripts/sysadmin-app/router.js b/static/scripts/sysadmin-app/router.js
index 5046cdea71..556f6a15ca 100644
--- a/static/scripts/sysadmin-app/router.js
+++ b/static/scripts/sysadmin-app/router.js
@@ -41,6 +41,7 @@ define([
showDashboard: function() {
this.switchCurrentView(this.dashboardView);
this.sideNavView.setCurTab('dashboard');
+ this.dashboardView.show();
}
});
diff --git a/static/scripts/sysadmin-app/views/dashboard.js b/static/scripts/sysadmin-app/views/dashboard.js
index c66d8864a0..fb9e67ae74 100644
--- a/static/scripts/sysadmin-app/views/dashboard.js
+++ b/static/scripts/sysadmin-app/views/dashboard.js
@@ -2,23 +2,43 @@ define([
'jquery',
'underscore',
'backbone',
- 'common'
-], function($, _, Backbone, Common) {
+ 'common',
+ 'sysadmin-app/models/SysInfo'
+], function($, _, Backbone, Common, SysInfo) {
'use strict';
var DashboardView = Backbone.View.extend({
tagName: 'div',
+ template: _.template($("#sysinfo-tmpl").html()),
initialize: function() {
-
+ this.sysinfo = new SysInfo();
},
events: {
},
render: function() {
-
+ var _this = this;
+ this.sysinfo.fetch({
+ success: function(model, response, options) {
+ _this._showContent();
+ },
+ error: function(model, response, options) {
+ var err_msg;
+ if (response.responseText) {
+ if (response['status'] == 401 || response['status'] == 403) {
+ err_msg = gettext("Permission error");
+ } else {
+ err_msg = $.parseJSON(response.responseText).error_msg;
+ }
+ } else {
+ err_msg = gettext("Failed. Please check the network.");
+ }
+ Common.feedback(err_msg, 'error');
+ }
+ });
},
hide: function() {
@@ -26,7 +46,15 @@ define([
},
show: function() {
+ this.render();
+ },
+
+ _showContent: function() {
+ console.log(this.sysinfo.toJSON());
+ this.$el.html(this.template(this.sysinfo.toJSON()));
+ $("#right-panel").html(this.$el);
+ return this;
}
});