diff --git a/media/css/seahub.css b/media/css/seahub.css
index e1ccae600a..8e90f13ecf 100644
--- a/media/css/seahub.css
+++ b/media/css/seahub.css
@@ -188,6 +188,7 @@
.sf2-icon-lock:before { content: "\e02c"; }
.sf2-icon-unlock:before { content: "\e02d"; }
.sf2-icon-admin-log:before { content: "\e02e"; }
+.sf2-icon-info:before { content: "\e02f"; }
/******* tags **********/
body,
@@ -1068,6 +1069,31 @@ textarea:-moz-placeholder {/* for FF */
bottom:0;
width:100%;
}
+
+/**** details-side-panel ****/
+.details-panel {
+ width:320px;
+}
+.details-panel-img-container {
+ text-align:center;
+ height:160px;
+ padding:6px 0;
+}
+.details-panel-img-container .thumbnail {
+ max-width:calc(100% - 4px); /* `-4px` for full width thumbnail */
+ max-height:100%;
+}
+.details-panel-text-info-container {
+ padding:10px 20px;
+}
+.details-panel-text-info-container table {
+ margin:0;
+}
+.details-panel-text-info-container th,
+.details-panel-text-info-container td {
+ border:none;
+}
+
/**** messages ****/
.messages {
position:fixed;
diff --git a/media/css/sf_font2/seafile-font2.eot b/media/css/sf_font2/seafile-font2.eot
index 711268160e..081d65245c 100644
Binary files a/media/css/sf_font2/seafile-font2.eot and b/media/css/sf_font2/seafile-font2.eot differ
diff --git a/media/css/sf_font2/seafile-font2.svg b/media/css/sf_font2/seafile-font2.svg
index 7f4bcea577..d405f2936a 100644
--- a/media/css/sf_font2/seafile-font2.svg
+++ b/media/css/sf_font2/seafile-font2.svg
@@ -54,4 +54,5 @@
+
diff --git a/media/css/sf_font2/seafile-font2.ttf b/media/css/sf_font2/seafile-font2.ttf
index 29be07f75b..d7286e852c 100644
Binary files a/media/css/sf_font2/seafile-font2.ttf and b/media/css/sf_font2/seafile-font2.ttf differ
diff --git a/media/css/sf_font2/seafile-font2.woff b/media/css/sf_font2/seafile-font2.woff
index b1f1ac9bc9..6741f5ebbd 100644
Binary files a/media/css/sf_font2/seafile-font2.woff and b/media/css/sf_font2/seafile-font2.woff differ
diff --git a/seahub/api2/endpoints/admin/upload_links.py b/seahub/api2/endpoints/admin/upload_links.py
index be75a90fbf..33f34b562d 100644
--- a/seahub/api2/endpoints/admin/upload_links.py
+++ b/seahub/api2/endpoints/admin/upload_links.py
@@ -126,7 +126,7 @@ class AdminUploadLinkUpload(APIView):
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
result = {}
- result['upload_link'] = gen_file_upload_url(token, 'upload-api')
+ result['upload_link'] = gen_file_upload_url(upload_token, 'upload-api')
return Response(result)
diff --git a/seahub/api2/endpoints/repo_trash.py b/seahub/api2/endpoints/repo_trash.py
index 3ef29bc548..6097f8f44a 100644
--- a/seahub/api2/endpoints/repo_trash.py
+++ b/seahub/api2/endpoints/repo_trash.py
@@ -104,7 +104,13 @@ class RepoTrash(APIView):
items = []
if len(deleted_entries) > 1:
- for item in deleted_entries[0:-1]:
+ entries_without_scan_stat = deleted_entries[0:-1]
+
+ # sort entry by delete time
+ entries_without_scan_stat.sort(lambda x, y : cmp(y.delete_time,
+ x.delete_time))
+
+ for item in entries_without_scan_stat:
item_info = self.get_item_info(item)
items.append(item_info)
diff --git a/seahub/api2/endpoints/repos.py b/seahub/api2/endpoints/repos.py
new file mode 100644
index 0000000000..75ea8bef70
--- /dev/null
+++ b/seahub/api2/endpoints/repos.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+import logging
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from rest_framework import status
+
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.utils import api_error
+
+from seahub.base.templatetags.seahub_tags import email2nickname, \
+ email2contact_email
+from seahub.utils import is_org_context
+from seahub.views import check_folder_permission
+
+from seaserv import seafile_api
+
+logger = logging.getLogger(__name__)
+
+class RepoView(APIView):
+
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated, )
+ throttle_classes = (UserRateThrottle, )
+
+ def get(self, request, repo_id):
+ """ Return repo info
+
+ Permission checking:
+ 1. all authenticated user can perform this action.
+ """
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # permission check
+ permission = check_folder_permission(request, repo_id, '/')
+ if permission is None:
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ if is_org_context(request):
+ repo_owner = seafile_api.get_org_repo_owner(repo_id)
+ else:
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+
+ result = {
+ "repo_id": repo.id,
+ "repo_name": repo.name,
+
+ "owner_email": repo_owner,
+ "owner_name": email2nickname(repo_owner),
+ "owner_contact_email": email2contact_email(repo_owner),
+
+ "size": repo.size,
+ "encrypted": repo.encrypted,
+ "file_count": repo.file_count,
+ "permission": permission,
+ }
+
+ return Response(result)
diff --git a/seahub/api2/views.py b/seahub/api2/views.py
index 8ac293a1f4..73e846b9fb 100644
--- a/seahub/api2/views.py
+++ b/seahub/api2/views.py
@@ -851,7 +851,6 @@ class Repo(APIView):
"id":repo.id,
"owner":owner,
"name":repo.name,
- "desc":repo.desc,
"mtime":repo.latest_modify,
"size":repo.size,
"encrypted":repo.encrypted,
@@ -860,6 +859,7 @@ class Repo(APIView):
"modifier_email": repo.last_modifier,
"modifier_contact_email": email2contact_email(repo.last_modifier),
"modifier_name": email2nickname(repo.last_modifier),
+ "file_count": repo.file_count,
}
if repo.encrypted:
repo_json["enc_version"] = repo.enc_version
diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html
index 7a648b21fe..9893d19064 100644
--- a/seahub/templates/js/sysadmin-templates.html
+++ b/seahub/templates/js/sysadmin-templates.html
@@ -5,7 +5,7 @@
{% trans "System Admin" %}
@@ -105,6 +106,7 @@
<% if (app.pageOptions.folder_perm_enabled) { %>
<% } %>
+ {% trans "Details" %}
@@ -115,6 +117,44 @@
<% } %>
+
+
+
{% endblock %}
diff --git a/seahub/urls.py b/seahub/urls.py
index ed39134483..da5ac9fa5b 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -26,6 +26,7 @@ from seahub.api2.endpoints.shared_folders import SharedFolders
from seahub.api2.endpoints.shared_repos import SharedRepos, SharedRepo
from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink
from seahub.api2.endpoints.repos_batch import ReposBatchView
+from seahub.api2.endpoints.repos import RepoView
from seahub.api2.endpoints.file import FileView
from seahub.api2.endpoints.dir import DirView
from seahub.api2.endpoints.repo_trash import RepoTrash
@@ -207,6 +208,7 @@ urlpatterns = patterns(
## user::repos
url(r'^api/v2.1/repos/batch/$', ReposBatchView.as_view(), name='api-v2.1-repos-batch'),
+ url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/$', RepoView.as_view(), name='api-v2.1-repo-view'),
url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/file/$', FileView.as_view(), name='api-v2.1-file-view'),
url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/dir/$', DirView.as_view(), name='api-v2.1-dir-view'),
url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/trash/$', RepoTrash.as_view(), name='api-v2.1-repo-trash'),
@@ -348,6 +350,7 @@ urlpatterns = patterns(
url(r'^sys/uploadlink/remove/$', sys_upload_link_remove, name='sys_upload_link_remove'),
url(r'^sys/notificationadmin/', notification_list, name='notification_list'),
url(r'^sys/invitationadmin/$', sys_invitation_admin, name='sys_invitation_admin'),
+ url(r'^sys/invitationadmin/remove/$', sys_invitation_remove, name='sys_invitation_remove'),
url(r'^sys/sudo/', sys_sudo_mode, name='sys_sudo_mode'),
url(r'^sys/check-license/', sys_check_license, name='sys_check_license'),
url(r'^useradmin/add/$', user_add, name="user_add"),
diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py
index c2ee586cc1..79feffcd18 100644
--- a/seahub/utils/__init__.py
+++ b/seahub/utils/__init__.py
@@ -215,15 +215,27 @@ def calculate_repos_last_modify(repo_list):
def normalize_dir_path(path):
"""Add '/' at the end of directory path if necessary.
+
+ And make sure path starts with '/'
"""
- if path[-1] != '/':
- path = path + '/'
- return path
+
+ path = path.strip('/')
+ if path == '':
+ return '/'
+ else:
+ return '/' + path + '/'
def normalize_file_path(path):
"""Remove '/' at the end of file path if necessary.
+
+ And make sure path starts with '/'
"""
- return path.rstrip('/')
+
+ path = path.strip('/')
+ if path == '':
+ return ''
+ else:
+ return '/' + path
# modified from django1.5:/core/validators, and remove the support for single
# quote in email address
diff --git a/seahub/views/sysadmin.py b/seahub/views/sysadmin.py
index 82574ab7b3..3677e43bcd 100644
--- a/seahub/views/sysadmin.py
+++ b/seahub/views/sysadmin.py
@@ -16,7 +16,7 @@ from django.conf import settings as dj_settings
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseNotAllowed
-from django.shortcuts import render_to_response
+from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils import timezone
from django.utils.translation import ugettext as _
@@ -2316,6 +2316,27 @@ def sys_invitation_admin(request):
},
context_instance=RequestContext(request))
+@login_required
+@sys_staff_required
+def sys_invitation_remove(request):
+ """Delete an invitation.
+ """
+ ct = 'application/json; charset=utf-8'
+ result = {}
+
+ if not ENABLE_GUEST_INVITATION:
+ return HttpResponse(json.dumps({}), status=400, content_type=ct)
+
+ inv_id = request.POST.get('inv_id', '')
+ if not inv_id:
+ result = {'error': "Argument missing"}
+ return HttpResponse(json.dumps(result), status=400, content_type=ct)
+
+ inv = get_object_or_404(Invitation, pk=inv_id)
+ inv.delete()
+
+ return HttpResponse(json.dumps({'success': True}), content_type=ct)
+
@login_required
@sys_staff_required
def sys_terms_admin(request):
diff --git a/static/scripts/app/views/myhome-repos.js b/static/scripts/app/views/myhome-repos.js
index b0d90091d1..e92ad0c803 100644
--- a/static/scripts/app/views/myhome-repos.js
+++ b/static/scripts/app/views/myhome-repos.js
@@ -6,7 +6,9 @@ define([
'app/collections/repos',
'app/views/repo',
'app/views/add-repo',
-], function($, _, Backbone, Common, RepoCollection, RepoView, AddRepoView) {
+ 'app/views/repo-details'
+], function($, _, Backbone, Common, RepoCollection, RepoView, AddRepoView,
+ RepoDetailsView) {
'use strict';
var ReposView = Backbone.View.extend({
@@ -27,11 +29,13 @@ define([
this.listenTo(this.repos, 'add', this.addOne);
this.listenTo(this.repos, 'reset', this.reset);
+ this.repoDetailsView = new RepoDetailsView();
+
this.render();
},
addOne: function(repo, collection, options) {
- var view = new RepoView({model: repo});
+ var view = new RepoView({model: repo, myReposView: this});
if (options.prepend) {
this.$tableBody.prepend(view.render().el);
} else {
@@ -115,6 +119,8 @@ define([
hide: function() {
this.$el.detach();
+
+ this.repoDetailsView.hide();
},
createRepo: function() {
diff --git a/static/scripts/app/views/repo-details.js b/static/scripts/app/views/repo-details.js
new file mode 100644
index 0000000000..f62a93a911
--- /dev/null
+++ b/static/scripts/app/views/repo-details.js
@@ -0,0 +1,73 @@
+define([
+ 'jquery',
+ 'underscore',
+ 'backbone',
+ 'common'
+], function($, _, Backbone, Common) {
+ 'use strict';
+
+ var View = Backbone.View.extend({
+ id: 'repo-details',
+ className: 'details-panel right-side-panel',
+
+ template: _.template($('#repo-details-tmpl').html()),
+
+ initialize: function() {
+ $("#main").append(this.$el);
+
+ var _this = this;
+ $(document).keydown(function(e) {
+ // ESCAPE key pressed
+ if (e.which == 27) {
+ _this.hide();
+ }
+ });
+
+ $(window).resize(function() {
+ _this.setConMaxHeight();
+ });
+ },
+
+ events: {
+ 'click .js-close': 'close'
+ },
+
+ render: function() {
+ this.$el.html(this.template(this.data));
+ },
+
+ update: function(part_data) {
+ if (part_data.error) {
+ this.$('#file-count').html('' + gettext("Error") + '');
+ } else {
+ this.$('#file-count').html(part_data.file_count);
+ }
+ },
+
+ setConMaxHeight: function() {
+ this.$('.right-side-panel-con').css({
+ 'height': $(window).height() - // this.$el `position:fixed; top:0;`
+ this.$('.right-side-panel-hd').outerHeight(true)
+ });
+ },
+
+ hide: function() {
+ this.$el.css({'right': '-320px'});
+ },
+
+ close: function() {
+ this.hide();
+ return false;
+ },
+
+ show: function(options) {
+ this.data = options;
+ this.render();
+ this.$el.css({'right': '0px'});
+ this.setConMaxHeight();
+ }
+
+ });
+
+ return View;
+});
diff --git a/static/scripts/app/views/repo.js b/static/scripts/app/views/repo.js
index a0448cb60e..22e79b5ad3 100644
--- a/static/scripts/app/views/repo.js
+++ b/static/scripts/app/views/repo.js
@@ -24,6 +24,7 @@ define([
transferTemplate: _.template($('#repo-transfer-form-tmpl').html()),
events: {
+ 'click': 'clickItem',
'click td:lt(2)': 'visitRepo',
'click .repo-delete-btn': 'del',
'click .repo-share-btn': 'share',
@@ -32,12 +33,15 @@ define([
'click .js-repo-change-password': 'changePassword',
'click .js-popup-history-setting': 'popupHistorySetting',
'click .js-popup-share-link-admin': 'popupShareLinkAdmin',
- 'click .js-popup-folder-perm-admin': 'popupFolderPermAdmin'
+ 'click .js-popup-folder-perm-admin': 'popupFolderPermAdmin',
+ 'click .js-repo-details': 'viewDetails'
},
- initialize: function() {
+ initialize: function(options) {
HLItemView.prototype.initialize.call(this);
+ this.myReposView = options.myReposView;
+
this.listenTo(this.model, "change", this.render);
},
@@ -65,6 +69,14 @@ define([
return this;
},
+ clickItem: function(e) {
+ var target = e.target || event.srcElement;
+ if (this.$('td').is(target) &&
+ $('#repo-details').css('right') == '0px') { // after `#repo-details` is shown
+ this.viewDetails();
+ }
+ },
+
visitRepo: function() {
if ($(window).width() < 768) {
location.href = this.$('.repo-name-span a').attr('href');
@@ -312,6 +324,38 @@ define([
this.togglePopup(); // close the popup
new RepoChangePasswordDialog(options);
return false;
+ },
+
+ viewDetails: function() {
+ var obj = this.model.toJSON();
+ var icon_size = Common.isHiDPI() ? 96 : 24;
+ var data = $.extend({}, obj, {
+ icon_url: this.model.getIconUrl(icon_size),
+ big_icon_url: this.model.getIconUrl(96)
+ });
+ var detailsView = this.myReposView.repoDetailsView;
+ detailsView.show(data);
+
+ // fetch other data
+ $.ajax({
+ url: Common.getUrl({
+ 'name': 'repo_v2.1',
+ 'repo_id': this.model.get('id')
+ }),
+ cache: false,
+ dataType: 'json',
+ success: function(data) {
+ detailsView.update({
+ 'file_count': data.file_count
+ });
+ },
+ error: function() {
+ detailsView.update({'error': true});
+ }
+ });
+
+ this.togglePopup(); // close the popup
+ return false;
}
});
diff --git a/static/scripts/common.js b/static/scripts/common.js
index 7d02e20bfb..1eb274640c 100644
--- a/static/scripts/common.js
+++ b/static/scripts/common.js
@@ -106,6 +106,7 @@ define([
case 'dir_shared_items': return siteRoot + 'api2/repos/' + options.repo_id + '/dir/shared_items/';
case 'shared_repos': return siteRoot + 'api2/shared-repos/' + options.repo_id + '/';
case 'repo': return siteRoot + 'api2/repos/' + options.repo_id + '/';
+ case 'repo_v2.1': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/';
case 'repo_owner': return siteRoot + 'api2/repos/' + options.repo_id + '/owner/';
case 'repo_history_limit': return siteRoot + 'api2/repos/' + options.repo_id + '/history-limit/';
case 'repo_shared_download_links': return siteRoot + 'api2/repos/' + options.repo_id + '/download-shared-links/';
diff --git a/tests/api/endpoints/test_repos.py b/tests/api/endpoints/test_repos.py
new file mode 100644
index 0000000000..4fadb3382f
--- /dev/null
+++ b/tests/api/endpoints/test_repos.py
@@ -0,0 +1,102 @@
+import json
+from django.core.urlresolvers import reverse
+from seaserv import seafile_api
+from seahub.test_utils import BaseTestCase
+from seahub.base.templatetags.seahub_tags import email2nickname, \
+ email2contact_email
+
+class RepoViewTest(BaseTestCase):
+
+ def setUp(self):
+ self.user_name = self.user.username
+ self.admin_name = self.admin.username
+ self.url = reverse('api-v2.1-repo-view', args=[self.repo.id])
+
+ def tearDown(self):
+ self.remove_repo()
+
+ def test_can_get(self):
+
+ self.login_as(self.user)
+
+ resp = self.client.get(self.url)
+ self.assertEqual(200, resp.status_code)
+ json_resp = json.loads(resp.content)
+
+ assert json_resp['repo_id'] == self.repo.id
+ assert json_resp['repo_name'] == self.repo.name
+
+ assert json_resp['owner_email'] == self.user_name
+ assert json_resp['owner_name'] == email2nickname(self.user_name)
+ assert json_resp['owner_contact_email'] == email2contact_email(self.user_name)
+
+ assert json_resp['permission'] == 'rw'
+
+ self.assertFalse(json_resp['encrypted'])
+ self.assertIsNotNone(json_resp['file_count'])
+ self.assertIsNotNone(json_resp['size'])
+
+ def test_can_get_be_shared_repo_info(self):
+
+ # create admin repo
+ admin_repo_id = seafile_api.create_repo(name='test-repo', desc='',
+ username=self.admin_name, passwd=None)
+ admin_repo = seafile_api.get_repo(admin_repo_id)
+
+ # share admin repo to current user
+ permission = 'r'
+ seafile_api.share_repo(admin_repo_id, self.admin_name,
+ self.user_name, permission)
+
+ self.login_as(self.user)
+
+ url = reverse('api-v2.1-repo-view', args=[admin_repo_id])
+ resp = self.client.get(url)
+ self.assertEqual(200, resp.status_code)
+ json_resp = json.loads(resp.content)
+
+ assert json_resp['repo_id'] == admin_repo.id
+ assert json_resp['repo_name'] == admin_repo.name
+
+ assert json_resp['owner_email'] == self.admin_name
+ assert json_resp['owner_name'] == email2nickname(self.admin_name)
+ assert json_resp['owner_contact_email'] == email2contact_email(self.admin_name)
+
+ assert json_resp['permission'] == permission
+
+ self.assertFalse(json_resp['encrypted'])
+ self.assertIsNotNone(json_resp['file_count'])
+ self.assertIsNotNone(json_resp['size'])
+
+ self.remove_repo(admin_repo_id)
+
+ def test_get_with_invalid_authentication(self):
+
+ self.login_as(self.admin)
+
+ resp = self.client.get(self.url)
+ self.assertEqual(403, resp.status_code)
+
+ def test_get_with_invalid_permission(self):
+
+ admin_repo_id = seafile_api.create_repo(name='test-repo', desc='',
+ username=self.admin_name, passwd=None)
+
+ self.login_as(self.user)
+
+ url = reverse('api-v2.1-repo-view', args=[admin_repo_id])
+ resp = self.client.get(url)
+ self.assertEqual(403, resp.status_code)
+
+ self.remove_repo(admin_repo_id)
+
+ def test_get_with_invalid_repo(self):
+
+ self.login_as(self.user)
+
+ repo_id = self.repo.id
+ invalid_repo_id = repo_id[0:-5] + '12345'
+
+ url = reverse('api-v2.1-repo-view', args=[invalid_repo_id])
+ resp = self.client.get(url)
+ self.assertEqual(404, resp.status_code)
diff --git a/tests/api/test_repo.py b/tests/api/test_repo.py
index 37f16d08f4..5af39d09aa 100644
--- a/tests/api/test_repo.py
+++ b/tests/api/test_repo.py
@@ -21,8 +21,8 @@ class RepoTest(BaseTestCase):
self.assertIsNotNone(json_resp['size'])
self.assertIsNotNone(json_resp['name'])
self.assertIsNotNone(json_resp['root'])
- self.assertIsNotNone(json_resp['desc'])
self.assertIsNotNone(json_resp['type'])
+ self.assertIsNotNone(json_resp['file_count'])
def test_can_delete(self):
self.login_as(self.user)
diff --git a/tests/api/test_repos.py b/tests/api/test_repos.py
index b524082e1e..363b53bb24 100644
--- a/tests/api/test_repos.py
+++ b/tests/api/test_repos.py
@@ -59,7 +59,6 @@ class ReposApiTest(ApiTestBase):
self.assertIsNotNone(rinfo['size'])
self.assertIsNotNone(rinfo['name'])
self.assertIsNotNone(rinfo['root'])
- self.assertIsNotNone(rinfo['desc'])
self.assertIsNotNone(rinfo['type'])
assert len(rinfo['modifier_email']) > 0
assert len(rinfo['modifier_name']) > 0
@@ -84,7 +83,6 @@ class ReposApiTest(ApiTestBase):
#self.assertIsNotNone(commit['parent_id']) #allow null
self.assertIsNotNone(commit['new_merge'])
self.assertIsNotNone(commit['repo_id'])
- self.assertIsNotNone(commit['desc'])
self.assertIsNotNone(commit['id'])
self.assertIsNotNone(commit['conflict'])
#self.assertIsNotNone(commit['second_parent_id']) #allow null
diff --git a/tests/seahub/utils/__init__.py b/tests/seahub/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/seahub/utils/test_normalize_dir_path.py b/tests/seahub/utils/test_normalize_dir_path.py
new file mode 100644
index 0000000000..95430a78d6
--- /dev/null
+++ b/tests/seahub/utils/test_normalize_dir_path.py
@@ -0,0 +1,31 @@
+import posixpath
+from random import randint
+
+from tests.common.utils import randstring
+from seahub.test_utils import BaseTestCase
+from seahub.utils import normalize_dir_path
+
+class NormalizeDirPathTest(BaseTestCase):
+
+ def test_normalize_dir_path(self):
+
+ slash = '/'
+ folder_1 = randstring(3)
+ folder_2 = randstring(3)
+
+ random_slash = ''
+ for i in range(1, randint(1,10)):
+ random_slash += slash
+
+ posix_path = posixpath.join(folder_1, folder_2)
+ correct_path = slash + posix_path + slash
+
+ path_without_slash = posix_path
+ path_starts_with_random_slash = random_slash + posix_path
+ path_ends_with_random_slash = posix_path + random_slash
+ path_with_slash = random_slash + posix_path + random_slash
+
+ assert normalize_dir_path(path_without_slash) == correct_path
+ assert normalize_dir_path(path_starts_with_random_slash) == correct_path
+ assert normalize_dir_path(path_ends_with_random_slash) == correct_path
+ assert normalize_dir_path(path_with_slash) == correct_path
diff --git a/tests/seahub/utils/test_normalize_file_path.py b/tests/seahub/utils/test_normalize_file_path.py
new file mode 100644
index 0000000000..9774c25dc9
--- /dev/null
+++ b/tests/seahub/utils/test_normalize_file_path.py
@@ -0,0 +1,31 @@
+import posixpath
+from random import randint
+
+from tests.common.utils import randstring
+from seahub.test_utils import BaseTestCase
+from seahub.utils import normalize_file_path
+
+class NormalizeDirPathTest(BaseTestCase):
+
+ def test_normalize_file_path(self):
+
+ slash = '/'
+ folder_1 = randstring(3)
+ folder_2 = randstring(3)
+
+ random_slash = ''
+ for i in range(1, randint(1,10)):
+ random_slash += slash
+
+ posix_path = posixpath.join(folder_1, folder_2)
+ correct_path = slash + posix_path
+
+ path_without_slash = posix_path
+ path_starts_with_random_slash = random_slash + posix_path
+ path_ends_with_random_slash = posix_path + random_slash
+ path_with_slash = random_slash + posix_path + random_slash
+
+ assert normalize_file_path(path_without_slash) == correct_path
+ assert normalize_file_path(path_starts_with_random_slash) == correct_path
+ assert normalize_file_path(path_ends_with_random_slash) == correct_path
+ assert normalize_file_path(path_with_slash) == correct_path
diff --git a/thirdpart/shibboleth/middleware.py b/thirdpart/shibboleth/middleware.py
index b2ff68c48a..78deae205d 100755
--- a/thirdpart/shibboleth/middleware.py
+++ b/thirdpart/shibboleth/middleware.py
@@ -1,3 +1,5 @@
+import logging
+
from django.conf import settings
from django.contrib.auth.middleware import RemoteUserMiddleware
from django.core.exceptions import ImproperlyConfigured
@@ -14,6 +16,9 @@ from seahub.profile.models import Profile
from seahub.utils.file_size import get_quota_from_string
from seahub.utils.user_permissions import get_user_role
+# Get an instance of a logger
+logger = logging.getLogger(__name__)
+
class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
"""
@@ -35,7 +40,7 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
#To support logout. If this variable is True, do not
#authenticate user and return now.
- if request.session.get(LOGOUT_SESSION_KEY) == True:
+ if request.session.get(LOGOUT_SESSION_KEY) is True:
return
else:
#Delete the shib reauth session key if present.
@@ -84,7 +89,8 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
# call make profile.
self.make_profile(user, shib_meta)
user_role = self.update_user_role(user, shib_meta)
- self.update_user_quota(user, user_role)
+ if user_role:
+ self.update_user_quota(user, user_role)
#setup session.
self.setup_session(request)
request.shib_login = True
@@ -169,6 +175,7 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
def update_user_quota(self, user, user_role):
if user.permissions.role_quota():
quota = get_quota_from_string(user.permissions.role_quota())
+ logger.info('Set quota[%d] for user: %s, role[%s]' % (quota, user.username, user_role))
seafile_api.set_role_quota(user_role, quota)
else:
return